query.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dns/message.h>
  15. #include <dns/rcode.h>
  16. #include <dns/rrtype.h>
  17. #include <dns/rrset.h>
  18. #include <dns/rdataclass.h>
  19. #include <datasrc/client.h>
  20. #include <auth/query.h>
  21. #include <boost/foreach.hpp>
  22. #include <boost/bind.hpp>
  23. #include <boost/function.hpp>
  24. #include <algorithm> // for std::max
  25. #include <set>
  26. #include <vector>
  27. using namespace std;
  28. using namespace isc::dns;
  29. using namespace isc::datasrc;
  30. using namespace isc::dns::rdata;
  31. // Commonly used helper callback object for vector<ConstRRsetPtr> to
  32. // insert them to (the specified section of) a message.
  33. //
  34. // One feature is that it maintains an internal set of raw pointers to the
  35. // RRsets as they are added (this is safe - the object is only in scope in
  36. // the createResponse() method and during this time, all RRsets referred to
  37. // remain in existence due to the presence of the ConstRRsetPtr objects
  38. // from which the raw objects were derived). The set is used to detect
  39. // and discard duplicates.
  40. namespace {
  41. class RRsetInserter {
  42. private:
  43. // \brief RRset comparison functor.
  44. struct RRsetLthan {
  45. bool operator()(const AbstractRRset* r1, const AbstractRRset* r2) {
  46. return (r1->lthan(*r2));
  47. }
  48. };
  49. typedef std::set<const AbstractRRset*, RRsetLthan> AddedRRsets;
  50. public:
  51. RRsetInserter(Message& msg, const Message::Section section,
  52. const bool dnssec) :
  53. msg_(msg), section_(section), dnssec_(dnssec)
  54. {}
  55. // \brief Set Target Section
  56. //
  57. // Sets the section into which the information added by addRRset will be
  58. // inserted.
  59. //
  60. // \param section New section number
  61. void setSection(Message::Section section) {
  62. section_ = section;
  63. }
  64. // Insertion operation
  65. //
  66. // \param rrset Pointer to RRset to be added to the message
  67. void addRRset(const ConstRRsetPtr& rrset) {
  68. // Has the RRset already been added to this message?
  69. std::pair<AddedRRsets::iterator, bool> result =
  70. rrsets_added_.insert(rrset.get());
  71. if (result.second) {
  72. // Were able to add the pointer to the RRset to rrsets_added_, so
  73. // the RRset has not already been seen. Add it to the message.
  74. // The const-cast is wrong, but the Message interface seems
  75. // to insist.
  76. msg_.addRRset(section_,
  77. boost::const_pointer_cast<AbstractRRset>(rrset),
  78. dnssec_);
  79. }
  80. }
  81. private:
  82. Message& msg_;
  83. Message::Section section_;
  84. const bool dnssec_;
  85. AddedRRsets rrsets_added_;
  86. };
  87. // This is a "constant" vector storing desired RR types for the additional
  88. // section. The vector is filled first time it's used.
  89. const vector<RRType>&
  90. A_AND_AAAA() {
  91. static vector<RRType> needed_types;
  92. if (needed_types.empty()) {
  93. needed_types.push_back(RRType::A());
  94. needed_types.push_back(RRType::AAAA());
  95. }
  96. return (needed_types);
  97. }
  98. }
  99. namespace isc {
  100. namespace auth {
  101. void
  102. Query::addSOA(ZoneFinder& finder) {
  103. ZoneFinderContextPtr soa_ctx = finder.find(finder.getOrigin(),
  104. RRType::SOA(), dnssec_opt_);
  105. if (soa_ctx->code != ZoneFinder::SUCCESS) {
  106. isc_throw(NoSOA, "There's no SOA record in zone " <<
  107. finder.getOrigin().toText());
  108. } else {
  109. authorities_.push_back(soa_ctx->rrset);
  110. }
  111. }
  112. // Note: unless the data source client implementation or the zone content
  113. // is broken, 'nsec' should be a valid NSEC RR. Likewise, the call to
  114. // find() in this method should result in NXDOMAIN and an NSEC RR that proves
  115. // the non existent of matching wildcard. If these assumptions aren't met
  116. // due to a buggy data source implementation or a broken zone, we'll let
  117. // underlying libdns++ modules throw an exception, which would result in
  118. // either an SERVFAIL response or just ignoring the query. We at least prevent
  119. // a complete crash due to such broken behavior.
  120. void
  121. Query::addNXDOMAINProofByNSEC(ZoneFinder& finder, ConstRRsetPtr nsec) {
  122. if (nsec->getRdataCount() == 0) {
  123. isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
  124. }
  125. // Add the NSEC proving NXDOMAIN to the authority section.
  126. authorities_.push_back(nsec);
  127. // Next, identify the best possible wildcard name that would match
  128. // the query name. It's the longer common suffix with the qname
  129. // between the owner or the next domain of the NSEC that proves NXDOMAIN,
  130. // prefixed by the wildcard label, "*". For example, for query name
  131. // a.b.example.com, if the NXDOMAIN NSEC is
  132. // b.example.com. NSEC c.example.com., the longer suffix is b.example.com.,
  133. // and the best possible wildcard is *.b.example.com. If the NXDOMAIN
  134. // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
  135. // is the next domain of the NSEC, and we get the same wildcard name.
  136. const int qlabels = qname_->getLabelCount();
  137. const int olabels = qname_->compare(nsec->getName()).getCommonLabels();
  138. const int nlabels = qname_->compare(
  139. dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
  140. getCurrent()).
  141. getNextName()).getCommonLabels();
  142. const int common_labels = std::max(olabels, nlabels);
  143. const Name wildname(Name("*").concatenate(qname_->split(qlabels -
  144. common_labels)));
  145. // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
  146. // otherwise we shouldn't have got NXDOMAIN for the original query in
  147. // the first place).
  148. ConstZoneFinderContextPtr fcontext =
  149. finder.find(wildname, RRType::NSEC(), dnssec_opt_);
  150. if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
  151. fcontext->rrset->getRdataCount() == 0) {
  152. isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
  153. }
  154. // Add the (no-) wildcard proof only when it's different from the NSEC
  155. // that proves NXDOMAIN; sometimes they can be the same.
  156. // Note: name comparison is relatively expensive. When we are at the
  157. // stage of performance optimization, we should consider optimizing this
  158. // for some optimized data source implementations.
  159. if (nsec->getName() != fcontext->rrset->getName()) {
  160. authorities_.push_back(fcontext->rrset);
  161. }
  162. }
  163. uint8_t
  164. Query::addClosestEncloserProof(ZoneFinder& finder, const Name& name,
  165. bool exact_ok, bool add_closest)
  166. {
  167. const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, true);
  168. // Validity check (see the method description). Note that a completely
  169. // broken findNSEC3 implementation could even return NULL RRset in
  170. // closest_proof. We don't explicitly check such case; addRRset() will
  171. // throw an exception, and it will be converted to SERVFAIL at the caller.
  172. if (!exact_ok && !result.next_proof) {
  173. isc_throw(BadNSEC3, "Matching NSEC3 found for a non existent name: "
  174. << qname_);
  175. }
  176. if (add_closest) {
  177. authorities_.push_back(result.closest_proof);
  178. }
  179. if (result.next_proof) {
  180. authorities_.push_back(result.next_proof);
  181. }
  182. return (result.closest_labels);
  183. }
  184. void
  185. Query::addNSEC3ForName(ZoneFinder& finder, const Name& name, bool match) {
  186. const ZoneFinder::FindNSEC3Result result = finder.findNSEC3(name, false);
  187. // See the comment for addClosestEncloserProof(). We don't check a
  188. // totally bogus case where closest_proof is NULL here.
  189. if (match != result.matched) {
  190. isc_throw(BadNSEC3, "Unexpected "
  191. << (result.matched ? "matching" : "covering")
  192. << " NSEC3 found for " << name);
  193. }
  194. authorities_.push_back(result.closest_proof);
  195. }
  196. void
  197. Query::addNXDOMAINProofByNSEC3(ZoneFinder& finder) {
  198. // Firstly get the NSEC3 proves for Closest Encloser Proof
  199. // See Section 7.2.1 of RFC 5155.
  200. const uint8_t closest_labels =
  201. addClosestEncloserProof(finder, *qname_, false);
  202. // Next, construct the wildcard name at the closest encloser, i.e.,
  203. // '*' followed by the closest encloser, and add NSEC3 for it.
  204. const Name wildname(Name("*").concatenate(
  205. qname_->split(qname_->getLabelCount() - closest_labels)));
  206. addNSEC3ForName(finder, wildname, false);
  207. }
  208. void
  209. Query::addWildcardProof(ZoneFinder& finder,
  210. const ZoneFinder::Context& db_context)
  211. {
  212. if (db_context.isNSECSigned()) {
  213. // Case for RFC4035 Section 3.1.3.3.
  214. //
  215. // The query name shouldn't exist in the zone if there were no wildcard
  216. // substitution. Confirm that by specifying NO_WILDCARD. It should
  217. // result in NXDOMAIN and an NSEC RR that proves it should be returned.
  218. ConstZoneFinderContextPtr fcontext =
  219. finder.find(*qname_, RRType::NSEC(),
  220. dnssec_opt_ | ZoneFinder::NO_WILDCARD);
  221. if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
  222. fcontext->rrset->getRdataCount() == 0) {
  223. isc_throw(BadNSEC,
  224. "Unexpected NSEC result for wildcard proof");
  225. }
  226. authorities_.push_back(fcontext->rrset);
  227. } else if (db_context.isNSEC3Signed()) {
  228. // Case for RFC 5155 Section 7.2.6.
  229. //
  230. // Note that the closest encloser must be the immediate ancestor
  231. // of the matching wildcard, so NSEC3 for its next closer (and only
  232. // that NSEC3) is what we are expected to provided per the RFC (if
  233. // this assumption isn't met the zone is broken anyway).
  234. addClosestEncloserProof(finder, *qname_, false, false);
  235. }
  236. }
  237. void
  238. Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
  239. // There should be one NSEC RR which was found in the zone to prove
  240. // that there is not matched <QNAME,QTYPE> via wildcard expansion.
  241. if (nsec->getRdataCount() == 0) {
  242. isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
  243. }
  244. ConstZoneFinderContextPtr fcontext =
  245. finder.find(*qname_, RRType::NSEC(),
  246. dnssec_opt_ | ZoneFinder::NO_WILDCARD);
  247. if (fcontext->code != ZoneFinder::NXDOMAIN || !fcontext->rrset ||
  248. fcontext->rrset->getRdataCount() == 0) {
  249. isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
  250. }
  251. if (nsec->getName() != fcontext->rrset->getName()) {
  252. // one NSEC RR proves wildcard_nxrrset that no matched QNAME.
  253. authorities_.push_back(fcontext->rrset);
  254. }
  255. }
  256. void
  257. Query::addDS(ZoneFinder& finder, const Name& dname) {
  258. ConstZoneFinderContextPtr ds_context =
  259. finder.find(dname, RRType::DS(), dnssec_opt_);
  260. if (ds_context->code == ZoneFinder::SUCCESS) {
  261. authorities_.push_back(ds_context->rrset);
  262. } else if (ds_context->code == ZoneFinder::NXRRSET &&
  263. ds_context->isNSECSigned()) {
  264. addNXRRsetProof(finder, *ds_context);
  265. } else if (ds_context->code == ZoneFinder::NXRRSET &&
  266. ds_context->isNSEC3Signed()) {
  267. // Add no DS proof with NSEC3 as specified in RFC 5155 Section 7.2.7.
  268. addClosestEncloserProof(finder, dname, true);
  269. } else {
  270. // Any other case should be an error
  271. isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
  272. }
  273. }
  274. void
  275. Query::addNXRRsetProof(ZoneFinder& finder,
  276. const ZoneFinder::Context& db_context)
  277. {
  278. if (db_context.isNSECSigned() && db_context.rrset) {
  279. authorities_.push_back(db_context.rrset);
  280. if (db_context.isWildcard()) {
  281. addWildcardNXRRSETProof(finder, db_context.rrset);
  282. }
  283. } else if (db_context.isNSEC3Signed() && !db_context.isWildcard()) {
  284. if (*qtype_ == RRType::DS()) {
  285. // RFC 5155, Section 7.2.4. Add either NSEC3 for the qname or
  286. // closest (provable) encloser proof in case of optout.
  287. addClosestEncloserProof(finder, *qname_, true);
  288. } else {
  289. // RFC 5155, Section 7.2.3. Just add NSEC3 for the qname.
  290. addNSEC3ForName(finder, *qname_, true);
  291. }
  292. } else if (db_context.isNSEC3Signed() && db_context.isWildcard()) {
  293. // Case for RFC 5155 Section 7.2.5: add closest encloser proof for the
  294. // qname, construct the matched wildcard name and add NSEC3 for it.
  295. const uint8_t closest_labels =
  296. addClosestEncloserProof(finder, *qname_, false);
  297. const Name wname = Name("*").concatenate(
  298. qname_->split(qname_->getLabelCount() - closest_labels));
  299. addNSEC3ForName(finder, wname, true);
  300. }
  301. }
  302. void
  303. Query::addAuthAdditional(ZoneFinder& finder,
  304. vector<ConstRRsetPtr>& additionals)
  305. {
  306. const Name& origin = finder.getOrigin();
  307. // Fill in authority and addtional sections.
  308. ConstZoneFinderContextPtr ns_context = finder.find(origin, RRType::NS(),
  309. dnssec_opt_);
  310. // zone origin name should have NS records
  311. if (ns_context->code != ZoneFinder::SUCCESS) {
  312. isc_throw(NoApexNS, "There's no apex NS records in zone " <<
  313. finder.getOrigin().toText());
  314. }
  315. authorities_.push_back(ns_context->rrset);
  316. ns_context->getAdditional(A_AND_AAAA(), additionals);
  317. }
  318. namespace {
  319. // A simple wrapper for DataSourceClient::findZone(). Normally we can simply
  320. // check the closest zone to the qname, but for type DS query we need to
  321. // look into the parent zone. Nevertheless, if there is no "parent" (i.e.,
  322. // the qname consists of a single label, which also means it's the root name),
  323. // we should search the deepest zone we have (which should be the root zone;
  324. // otherwise it's a query error).
  325. DataSourceClient::FindResult
  326. findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
  327. if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
  328. return (client.findZone(qname));
  329. }
  330. return (client.findZone(qname.split(1)));
  331. }
  332. }
  333. void
  334. Query::process(datasrc::DataSourceClient& datasrc_client,
  335. const isc::dns::Name& qname, const isc::dns::RRType& qtype,
  336. isc::dns::Message& response, bool dnssec)
  337. {
  338. // Set up the cleaner object so internal pointers and vectors are
  339. // always reset after scope leaves this method
  340. QueryCleaner cleaner(*this);
  341. // Set up query parameters for the rest of the (internal) methods
  342. initialize(datasrc_client, qname, qtype, response, dnssec);
  343. // Found a zone which is the nearest ancestor to QNAME
  344. const DataSourceClient::FindResult result = findZone(*datasrc_client_,
  345. *qname_, *qtype_);
  346. // If we have no matching authoritative zone for the query name, return
  347. // REFUSED. In short, this is to be compatible with BIND 9, but the
  348. // background discussion is not that simple. See the relevant topic
  349. // at the BIND 10 developers's ML:
  350. // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
  351. if (result.code != result::SUCCESS &&
  352. result.code != result::PARTIALMATCH) {
  353. // If we tried to find a "parent zone" for a DS query and failed,
  354. // we may still have authority at the child side. If we do, the query
  355. // has to be handled there.
  356. if (*qtype_ == RRType::DS() && qname_->getLabelCount() > 1 &&
  357. processDSAtChild()) {
  358. return;
  359. }
  360. response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
  361. response_->setRcode(Rcode::REFUSED());
  362. return;
  363. }
  364. ZoneFinder& zfinder = *result.zone_finder;
  365. // We have authority for a zone that contain the query name (possibly
  366. // indirectly via delegation). Look into the zone.
  367. response_->setHeaderFlag(Message::HEADERFLAG_AA);
  368. response_->setRcode(Rcode::NOERROR());
  369. boost::function0<ZoneFinderContextPtr> find;
  370. const bool qtype_is_any = (*qtype_ == RRType::ANY());
  371. if (qtype_is_any) {
  372. find = boost::bind(&ZoneFinder::findAll, &zfinder, *qname_,
  373. boost::ref(answers_), dnssec_opt_);
  374. } else {
  375. find = boost::bind(&ZoneFinder::find, &zfinder, *qname_, *qtype_,
  376. dnssec_opt_);
  377. }
  378. ZoneFinderContextPtr db_context(find());
  379. switch (db_context->code) {
  380. case ZoneFinder::DNAME: {
  381. // First, put the dname into the answer
  382. answers_.push_back(db_context->rrset);
  383. /*
  384. * Empty DNAME should never get in, as it is impossible to
  385. * create one in master file.
  386. *
  387. * FIXME: Other way to prevent this should be done
  388. */
  389. assert(db_context->rrset->getRdataCount() > 0);
  390. // Get the data of DNAME
  391. const rdata::generic::DNAME& dname(
  392. dynamic_cast<const rdata::generic::DNAME&>(
  393. db_context->rrset->getRdataIterator()->getCurrent()));
  394. // The yet unmatched prefix dname
  395. const Name prefix(qname_->split(0, qname_->getLabelCount() -
  396. db_context->rrset->getName().getLabelCount()));
  397. // If we put it together, will it be too long?
  398. // (The prefix contains trailing ., which will be removed
  399. if (prefix.getLength() - Name::ROOT_NAME().getLength() +
  400. dname.getDname().getLength() > Name::MAX_WIRE) {
  401. /*
  402. * In case the synthesized name is too long, section 4.1
  403. * of RFC 2672 mandates we return YXDOMAIN.
  404. */
  405. response_->setRcode(Rcode::YXDOMAIN());
  406. break;
  407. }
  408. // The new CNAME we are creating (it will be unsigned even
  409. // with DNSSEC, the DNAME is signed and it can be validated
  410. // by that)
  411. RRsetPtr cname(new RRset(*qname_, db_context->rrset->getClass(),
  412. RRType::CNAME(), db_context->rrset->getTTL()));
  413. // Construct the new target by replacing the end
  414. cname->addRdata(rdata::generic::CNAME(qname_->split(0,
  415. qname_->getLabelCount() -
  416. db_context->rrset->getName().getLabelCount()).
  417. concatenate(dname.getDname())));
  418. answers_.push_back(cname);
  419. break;
  420. }
  421. case ZoneFinder::CNAME:
  422. /*
  423. * We don't do chaining yet. Therefore handling a CNAME is
  424. * mostly the same as handling SUCCESS, but we didn't get
  425. * what we expected. It means no exceptions in ANY or NS
  426. * on the origin (though CNAME in origin is probably
  427. * forbidden anyway).
  428. *
  429. * So, just put it there.
  430. */
  431. answers_.push_back(db_context->rrset);
  432. // If the answer is a result of wildcard substitution,
  433. // add a proof that there's no closer name.
  434. if (dnssec_ && db_context->isWildcard()) {
  435. addWildcardProof(*result.zone_finder, *db_context);
  436. }
  437. break;
  438. case ZoneFinder::SUCCESS:
  439. // If query type is ANY, the rrs have already been added
  440. if (!qtype_is_any) {
  441. answers_.push_back(db_context->rrset);
  442. }
  443. // Retrieve additional records for the answer
  444. db_context->getAdditional(A_AND_AAAA(), additionals_);
  445. // If apex NS records haven't been provided in the answer
  446. // section, insert apex NS records into the authority section
  447. // and AAAA/A RRS of each of the NS RDATA into the additional
  448. // section.
  449. // Checking the findZone() is a lightweight check to see if
  450. // qname is the zone origin.
  451. if (result.code != result::SUCCESS ||
  452. db_context->code != ZoneFinder::SUCCESS ||
  453. (*qtype_ != RRType::NS() && !qtype_is_any))
  454. {
  455. addAuthAdditional(*result.zone_finder, additionals_);
  456. }
  457. // If the answer is a result of wildcard substitution,
  458. // add a proof that there's no closer name.
  459. if (dnssec_ && db_context->isWildcard()) {
  460. addWildcardProof(*result.zone_finder, *db_context);
  461. }
  462. break;
  463. case ZoneFinder::DELEGATION:
  464. // If a DS query resulted in delegation, we also need to check
  465. // if we are an authority of the child, too. If so, we need to
  466. // complete the process in the child as specified in Section
  467. // 2.2.1.2. of RFC3658.
  468. if (*qtype_ == RRType::DS() && processDSAtChild()) {
  469. return;
  470. }
  471. response_->setHeaderFlag(Message::HEADERFLAG_AA, false);
  472. authorities_.push_back(db_context->rrset);
  473. // Retrieve additional records for the name servers
  474. db_context->getAdditional(A_AND_AAAA(), additionals_);
  475. // If DNSSEC is requested, see whether there is a DS
  476. // record for this delegation.
  477. if (dnssec_) {
  478. addDS(*result.zone_finder, db_context->rrset->getName());
  479. }
  480. break;
  481. case ZoneFinder::NXDOMAIN:
  482. response_->setRcode(Rcode::NXDOMAIN());
  483. addSOA(*result.zone_finder);
  484. if (dnssec_) {
  485. if (db_context->isNSECSigned() && db_context->rrset) {
  486. addNXDOMAINProofByNSEC(zfinder, db_context->rrset);
  487. } else if (db_context->isNSEC3Signed()) {
  488. addNXDOMAINProofByNSEC3(zfinder);
  489. }
  490. }
  491. break;
  492. case ZoneFinder::NXRRSET:
  493. addSOA(*result.zone_finder);
  494. if (dnssec_) {
  495. addNXRRsetProof(zfinder, *db_context);
  496. }
  497. break;
  498. default:
  499. // This is basically a bug of the data source implementation,
  500. // but could also happen in the middle of development where
  501. // we try to add a new result code.
  502. isc_throw(isc::NotImplemented, "Unknown result code");
  503. break;
  504. }
  505. createResponse();
  506. }
  507. void
  508. Query::initialize(datasrc::DataSourceClient& datasrc_client,
  509. const isc::dns::Name& qname, const isc::dns::RRType& qtype,
  510. isc::dns::Message& response, bool dnssec)
  511. {
  512. datasrc_client_ = &datasrc_client;
  513. qname_ = &qname;
  514. qtype_ = &qtype;
  515. response_ = &response;
  516. dnssec_ = dnssec;
  517. dnssec_opt_ = (dnssec ? isc::datasrc::ZoneFinder::FIND_DNSSEC :
  518. isc::datasrc::ZoneFinder::FIND_DEFAULT);
  519. }
  520. void
  521. Query::createResponse() {
  522. // Add the RRsets to the message. The order of sections is important,
  523. // as the RRsetInserter remembers RRsets added and will not add
  524. // duplicates. Adding in the order answer, authory, additional will
  525. // guarantee that if there are duplicates, the single RRset added will
  526. // appear in the most important section.
  527. std::vector<isc::dns::ConstRRsetPtr>::const_iterator i;
  528. RRsetInserter inserter(*response_, Message::SECTION_ANSWER, dnssec_);
  529. for (i = answers_.begin(); i != answers_.end(); ++i) {
  530. inserter.addRRset(*i);
  531. }
  532. inserter.setSection(Message::SECTION_AUTHORITY);
  533. for (i = authorities_.begin(); i != authorities_.end(); ++i) {
  534. inserter.addRRset(*i);
  535. }
  536. inserter.setSection(Message::SECTION_ADDITIONAL);
  537. for (i = additionals_.begin(); i != additionals_.end(); ++i) {
  538. inserter.addRRset(*i);
  539. }
  540. }
  541. void
  542. Query::reset() {
  543. datasrc_client_ = NULL;
  544. qname_ = NULL;
  545. qtype_ = NULL;
  546. response_ = NULL;
  547. answers_.clear();
  548. authorities_.clear();
  549. additionals_.clear();
  550. }
  551. bool
  552. Query::processDSAtChild() {
  553. const DataSourceClient::FindResult zresult =
  554. datasrc_client_->findZone(*qname_);
  555. if (zresult.code != result::SUCCESS) {
  556. return (false);
  557. }
  558. // We are receiving a DS query at the child side of the owner name,
  559. // where the DS isn't supposed to belong. We should return a "no data"
  560. // response as described in Section 3.1.4.1 of RFC4035 and Section
  561. // 2.2.1.1 of RFC 3658. find(DS) should result in NXRRSET, in which
  562. // case (and if DNSSEC is required) we also add the proof for that,
  563. // but even if find() returns an unexpected result, we don't bother.
  564. // The important point in this case is to return SOA so that the resolver
  565. // that happens to contact us can hunt for the appropriate parent zone
  566. // by seeing the SOA.
  567. response_->setHeaderFlag(Message::HEADERFLAG_AA);
  568. response_->setRcode(Rcode::NOERROR());
  569. addSOA(*zresult.zone_finder);
  570. ConstZoneFinderContextPtr ds_context =
  571. zresult.zone_finder->find(*qname_, RRType::DS(), dnssec_opt_);
  572. if (ds_context->code == ZoneFinder::NXRRSET) {
  573. if (dnssec_) {
  574. addNXRRsetProof(*zresult.zone_finder, *ds_context);
  575. }
  576. }
  577. createResponse();
  578. return (true);
  579. }
  580. }
  581. }