blob: b40ba9b37a22c42e9cb8d9b46feb7a4edc8f4ab9 [file] [log] [blame]
/* -*- 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 <iostream>
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;
BOOST_THROW_EXCEPTION(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());
BOOST_THROW_EXCEPTION(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