| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2014-2020, Regents of the University of California. |
| * |
| * This file is part of NDNS (Named Data Networking Domain Name Service). |
| * See AUTHORS.md for complete list of NDNS authors and contributors. |
| * |
| * NDNS is free software: you can redistribute it and/or modify it under the terms |
| * of the GNU General Public License as published by the Free Software Foundation, |
| * either version 3 of the License, or (at your option) any later version. |
| * |
| * NDNS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
| * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| * PURPOSE. See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * NDNS, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "iterative-query-controller.hpp" |
| #include "logger.hpp" |
| |
| #include <sstream> |
| |
| namespace ndn { |
| namespace ndns { |
| |
| NDNS_LOG_INIT(IterQueryCtr); |
| |
| IterativeQueryController::IterativeQueryController(const Name& dstLabel, |
| const name::Component& rrType, |
| const time::milliseconds& interestLifetime, |
| const QuerySucceedCallback& onSucceed, |
| const QueryFailCallback& onFail, |
| Face& face, |
| security::Validator* validator, |
| ndn::InMemoryStorage* cache) |
| : QueryController(dstLabel, rrType, interestLifetime, onSucceed, onFail, face) |
| , m_validator(validator) |
| , m_step(QUERY_STEP_QUERY_NS) |
| , m_nFinishedComps(0) |
| , m_nTryComps(1) |
| , m_nsCache(cache) |
| { |
| } |
| |
| void |
| IterativeQueryController::onTimeout(const Interest& interest) |
| { |
| NDNS_LOG_INFO("[* !! *] timeout happens: " << interest.getName()); |
| NDNS_LOG_TRACE(*this); |
| this->abort(); |
| } |
| |
| void |
| IterativeQueryController::abort() |
| { |
| NDNS_LOG_DEBUG("abort iterative query"); |
| if (m_onFail != nullptr) |
| m_onFail(0, "abort"); |
| else |
| NDNS_LOG_TRACE("m_onFail is 0"); |
| |
| } |
| |
| void |
| IterativeQueryController::onData(const ndn::Interest& interest, const Data& data) |
| { |
| NdnsContentType contentType = NdnsContentType(data.getContentType()); |
| |
| NDNS_LOG_TRACE("[* -> *] get a " << contentType |
| << " Response: " << data.getName()); |
| |
| const Data* toBeValidatedData = nullptr; |
| if (contentType == NDNS_NACK) { |
| m_doe = Data(data.getContent().blockFromValue()); |
| toBeValidatedData = &m_doe; |
| contentType = NDNS_DOE; |
| } |
| else { |
| toBeValidatedData = &data; |
| } |
| |
| if (m_validator == nullptr) { |
| this->onDataValidated(*toBeValidatedData, contentType); |
| } |
| else { |
| m_validator->validate(*toBeValidatedData, |
| bind(&IterativeQueryController::onDataValidated, this, _1, contentType), |
| [this] (const Data& data, const security::ValidationError& err) { |
| NDNS_LOG_WARN("data: " << data.getName() << " fails verification"); |
| this->abort(); |
| } |
| ); |
| } |
| } |
| |
| void |
| IterativeQueryController::onDataValidated(const Data& data, NdnsContentType contentType) |
| { |
| if (m_nsCache != nullptr && contentType == NDNS_LINK) { |
| m_nsCache->insert(data); |
| } |
| |
| switch (m_step) { |
| case QUERY_STEP_QUERY_NS: |
| if (contentType == NDNS_DOE) { |
| // check if requested record is absent by looking up in doe |
| if (isAbsentByDoe(data)) { |
| m_step = QUERY_STEP_QUERY_RR; |
| } |
| else { |
| std::ostringstream oss; |
| oss << "In onDataValidated, absence of record can not be infered from DoE."; |
| oss << " Last query:" << m_lastLabelType << " "; |
| oss << *this; |
| NDN_THROW(std::runtime_error(oss.str())); |
| } |
| } |
| else if (contentType == NDNS_LINK) { |
| Link link(data.wireEncode()); |
| if (link.getDelegationList().empty()) { |
| m_lastLink = Block(); |
| } |
| else { |
| m_lastLink = data.wireEncode(); |
| } |
| |
| // for NS query, if already received, just return, instead of more queries until NACK |
| if (m_nFinishedComps + m_nTryComps == m_dstLabel.size() && m_rrType == label::NS_RR_TYPE) { |
| // NS_RR_TYPE is different, since its record is stored at higher level |
| m_step = QUERY_STEP_ANSWER_STUB; |
| } |
| else { |
| m_nFinishedComps += m_nTryComps; |
| m_nTryComps = 1; |
| } |
| } |
| else if (contentType == NDNS_AUTH) { |
| m_nTryComps += 1; |
| } |
| else { |
| std::ostringstream oss; |
| oss << *this; |
| NDNS_LOG_WARN("get unexpected Response for QUERY_NS: " << oss.str()); |
| } |
| |
| if (m_nFinishedComps + m_nTryComps > m_dstLabel.size()) { |
| if (m_rrType == label::NS_RR_TYPE) { |
| m_step = QUERY_STEP_ANSWER_STUB; |
| } |
| else |
| m_step = QUERY_STEP_QUERY_RR; |
| } |
| break; |
| case QUERY_STEP_QUERY_RR: |
| m_step = QUERY_STEP_ANSWER_STUB; |
| break; |
| default: |
| NDNS_LOG_WARN("get unexpected Response at State " << *this); |
| // throw std::runtime_error("call makeLatestInterest() unexpected: " << *this); |
| // do not throw except since it may be duplicated Data |
| m_step = QUERY_STEP_ABORT; |
| break; |
| } |
| |
| if (!hasEnded()) |
| this->express(this->makeLatestInterest()); // express new Expres |
| else if (m_step == QUERY_STEP_ANSWER_STUB) { |
| NDNS_LOG_TRACE("query ends: " << *this); |
| Response re = this->parseFinalResponse(data); |
| if (m_onSucceed != nullptr) |
| m_onSucceed(data, re); |
| else |
| NDNS_LOG_TRACE("succeed callback is nullptr"); |
| } |
| else if (m_step == QUERY_STEP_ABORT) |
| this->abort(); |
| } |
| |
| bool |
| IterativeQueryController::hasEnded() |
| { |
| return (m_step != QUERY_STEP_QUERY_NS && m_step != QUERY_STEP_QUERY_RR); |
| } |
| |
| void |
| IterativeQueryController::start() |
| { |
| if (m_dstLabel.size() == m_nFinishedComps) |
| m_step = QUERY_STEP_QUERY_RR; |
| |
| Interest interest = this->makeLatestInterest(); |
| express(interest); |
| } |
| |
| |
| void |
| IterativeQueryController::express(const Interest& interest) |
| { |
| if (m_nsCache != nullptr) { |
| shared_ptr<const Data> cachedData = m_nsCache->find(interest); |
| if (cachedData != nullptr) { |
| NDNS_LOG_DEBUG("[* cached *] NS record has been cached before: " |
| << interest.getName()); |
| onData(interest, *cachedData); |
| return ; |
| } |
| } |
| |
| NDNS_LOG_DEBUG("[* <- *] send a Query: " << interest.getName()); |
| m_face.expressInterest(interest, |
| bind(&IterativeQueryController::onData, this, _1, _2), |
| bind(&IterativeQueryController::onTimeout, this, _1), // nack |
| bind(&IterativeQueryController::onTimeout, this, _1) |
| ); |
| } |
| |
| |
| const Response |
| IterativeQueryController::parseFinalResponse(const Data& data) |
| { |
| Response re; |
| Name zone = m_dstLabel.getPrefix(m_nFinishedComps); |
| re.fromData(zone, data); |
| return re; |
| } |
| |
| const Interest |
| IterativeQueryController::makeLatestInterest() |
| { |
| // NDNS_LOG_TRACE("get latest Interest"); |
| Query query; |
| //const Name& dstLabel = m_query.getRrLabel(); |
| |
| query.setZone(m_dstLabel.getPrefix(m_nFinishedComps)); |
| query.setInterestLifetime(m_interestLifetime); |
| |
| // addLink |
| if (m_lastLink.hasWire()) { |
| query.setDelegationListFromLink(Link(m_lastLink)); |
| } |
| |
| switch (m_step) { |
| case QUERY_STEP_QUERY_NS: |
| query.setQueryType(label::NDNS_ITERATIVE_QUERY); |
| query.setRrLabel(m_dstLabel.getSubName(m_nFinishedComps, m_nTryComps)); |
| query.setRrType(label::NS_RR_TYPE); |
| break; |
| case QUERY_STEP_QUERY_RR: |
| query.setQueryType(label::NDNS_ITERATIVE_QUERY); |
| query.setRrLabel(m_dstLabel.getSubName(m_nFinishedComps)); |
| query.setRrType(m_rrType); |
| break; |
| default: |
| std::ostringstream oss; |
| oss << *this; |
| NDNS_LOG_WARN("unexpected state: " << oss.str()); |
| NDN_THROW(std::runtime_error("call makeLatestInterest() unexpected: " + oss.str())); |
| } |
| |
| m_lastLabelType = Name(query.getRrLabel()).append(query.getRrType()); |
| Interest interest = query.toInterest(); |
| return interest; |
| } |
| |
| bool |
| IterativeQueryController::isAbsentByDoe(const Data& data) const |
| { |
| std::pair<Name, Name> range = Response::wireDecodeDoe(data.getContent()); |
| |
| // should not be simple <, use our own definition of compare |
| if (range.first < m_lastLabelType && m_lastLabelType < range.second) { |
| return true; |
| } |
| if (range.second < range.first && |
| (m_lastLabelType < range.first || range.second < m_lastLabelType)) { |
| return true; |
| } |
| return false; |
| } |
| |
| std::ostream& |
| operator<<(std::ostream& os, const IterativeQueryController::QueryStep step) |
| { |
| switch (step) { |
| case IterativeQueryController::QUERY_STEP_QUERY_NS: |
| os << "QueryNS"; |
| break; |
| case IterativeQueryController::QUERY_STEP_QUERY_RR: |
| os << "QueryRR"; |
| break; |
| case IterativeQueryController::QUERY_STEP_ANSWER_STUB: |
| os << "AnswerStub"; |
| break; |
| case IterativeQueryController::QUERY_STEP_ABORT: |
| os << "Abort"; |
| break; |
| default: |
| os << "UNKNOW"; |
| break; |
| } |
| return os; |
| } |
| |
| std::ostream& |
| operator<<(std::ostream& os, const IterativeQueryController& ctr) |
| { |
| os << "InterativeQueryController: dstLabel=" << ctr.getDstLabel() |
| << " rrType=" << ctr.getRrType() |
| << " currentStep=" << ctr.getStep() |
| << " nFinishedComps=" << ctr.getNFinishedComps() |
| << " nTryComp=" << ctr.getNTryComps() |
| ; |
| |
| return os; |
| } |
| |
| } // namespace ndns |
| } // namespace ndn |