query.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  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 <algorithm> // for std::max
  15. #include <vector>
  16. #include <boost/foreach.hpp>
  17. #include <boost/bind.hpp>
  18. #include <boost/function.hpp>
  19. #include <dns/message.h>
  20. #include <dns/rcode.h>
  21. #include <dns/rdataclass.h>
  22. #include <datasrc/client.h>
  23. #include <auth/query.h>
  24. using namespace isc::dns;
  25. using namespace isc::datasrc;
  26. using namespace isc::dns::rdata;
  27. namespace isc {
  28. namespace auth {
  29. void
  30. Query::addAdditional(ZoneFinder& zone, const AbstractRRset& rrset) {
  31. RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
  32. for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
  33. const Rdata& rdata(rdata_iterator->getCurrent());
  34. if (rrset.getType() == RRType::NS()) {
  35. // Need to perform the search in the "GLUE OK" mode.
  36. const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
  37. addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
  38. } else if (rrset.getType() == RRType::MX()) {
  39. const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
  40. addAdditionalAddrs(zone, mx.getMXName());
  41. }
  42. }
  43. }
  44. void
  45. Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
  46. const ZoneFinder::FindOptions options)
  47. {
  48. // Out of zone name
  49. NameComparisonResult result = zone.getOrigin().compare(qname);
  50. if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
  51. (result.getRelation() != NameComparisonResult::EQUAL))
  52. return;
  53. // Omit additional data which has already been provided in the answer
  54. // section from the additional.
  55. //
  56. // All the address rrset with the owner name of qname have been inserted
  57. // into ANSWER section.
  58. if (qname_ == qname && qtype_ == RRType::ANY())
  59. return;
  60. // Find A rrset
  61. if (qname_ != qname || qtype_ != RRType::A()) {
  62. ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(),
  63. options | dnssec_opt_);
  64. if (a_result.code == ZoneFinder::SUCCESS) {
  65. response_.addRRset(Message::SECTION_ADDITIONAL,
  66. boost::const_pointer_cast<AbstractRRset>(a_result.rrset), dnssec_);
  67. }
  68. }
  69. // Find AAAA rrset
  70. if (qname_ != qname || qtype_ != RRType::AAAA()) {
  71. ZoneFinder::FindResult aaaa_result = zone.find(qname, RRType::AAAA(),
  72. options | dnssec_opt_);
  73. if (aaaa_result.code == ZoneFinder::SUCCESS) {
  74. response_.addRRset(Message::SECTION_ADDITIONAL,
  75. boost::const_pointer_cast<AbstractRRset>(aaaa_result.rrset),
  76. dnssec_);
  77. }
  78. }
  79. }
  80. void
  81. Query::addSOA(ZoneFinder& finder) {
  82. ZoneFinder::FindResult soa_result = finder.find(finder.getOrigin(),
  83. RRType::SOA(),
  84. dnssec_opt_);
  85. if (soa_result.code != ZoneFinder::SUCCESS) {
  86. isc_throw(NoSOA, "There's no SOA record in zone " <<
  87. finder.getOrigin().toText());
  88. } else {
  89. /*
  90. * FIXME:
  91. * The const-cast is wrong, but the Message interface seems
  92. * to insist.
  93. */
  94. response_.addRRset(Message::SECTION_AUTHORITY,
  95. boost::const_pointer_cast<AbstractRRset>(soa_result.rrset), dnssec_);
  96. }
  97. }
  98. // Note: unless the data source client implementation or the zone content
  99. // is broken, 'nsec' should be a valid NSEC RR. Likewise, the call to
  100. // find() in this method should result in NXDOMAIN and an NSEC RR that proves
  101. // the non existent of matching wildcard. If these assumptions aren't met
  102. // due to a buggy data source implementation or a broken zone, we'll let
  103. // underlying libdns++ modules throw an exception, which would result in
  104. // either an SERVFAIL response or just ignoring the query. We at least prevent
  105. // a complete crash due to such broken behavior.
  106. void
  107. Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
  108. if (nsec->getRdataCount() == 0) {
  109. isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
  110. }
  111. // Add the NSEC proving NXDOMAIN to the authority section.
  112. response_.addRRset(Message::SECTION_AUTHORITY,
  113. boost::const_pointer_cast<AbstractRRset>(nsec), dnssec_);
  114. // Next, identify the best possible wildcard name that would match
  115. // the query name. It's the longer common suffix with the qname
  116. // between the owner or the next domain of the NSEC that proves NXDOMAIN,
  117. // prefixed by the wildcard label, "*". For example, for query name
  118. // a.b.example.com, if the NXDOMAIN NSEC is
  119. // b.example.com. NSEC c.example.com., the longer suffix is b.example.com.,
  120. // and the best possible wildcard is *.b.example.com. If the NXDOMAIN
  121. // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
  122. // is the next domain of the NSEC, and we get the same wildcard name.
  123. const int qlabels = qname_.getLabelCount();
  124. const int olabels = qname_.compare(nsec->getName()).getCommonLabels();
  125. const int nlabels = qname_.compare(
  126. dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
  127. getCurrent()).
  128. getNextName()).getCommonLabels();
  129. const int common_labels = std::max(olabels, nlabels);
  130. const Name wildname(Name("*").concatenate(qname_.split(qlabels -
  131. common_labels)));
  132. // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
  133. // otherwise we shouldn't have got NXDOMAIN for the original query in
  134. // the first place).
  135. const ZoneFinder::FindResult fresult =
  136. finder.find(wildname, RRType::NSEC(), dnssec_opt_);
  137. if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
  138. fresult.rrset->getRdataCount() == 0) {
  139. isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
  140. }
  141. // Add the (no-) wildcard proof only when it's different from the NSEC
  142. // that proves NXDOMAIN; sometimes they can be the same.
  143. // Note: name comparison is relatively expensive. When we are at the
  144. // stage of performance optimization, we should consider optimizing this
  145. // for some optimized data source implementations.
  146. if (nsec->getName() != fresult.rrset->getName()) {
  147. response_.addRRset(Message::SECTION_AUTHORITY,
  148. boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
  149. dnssec_);
  150. }
  151. }
  152. void
  153. Query::addWildcardProof(ZoneFinder& finder,
  154. const ZoneFinder::FindResult& db_result)
  155. {
  156. // The query name shouldn't exist in the zone if there were no wildcard
  157. // substitution. Confirm that by specifying NO_WILDCARD. It should result
  158. // in NXDOMAIN and an NSEC RR that proves it should be returned.
  159. if(db_result.isNSECSigned() && db_result.isWildcard()){
  160. const ZoneFinder::FindResult fresult =
  161. finder.find(qname_, RRType::NSEC(),
  162. dnssec_opt_ | ZoneFinder::NO_WILDCARD);
  163. if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
  164. fresult.rrset->getRdataCount() == 0) {
  165. isc_throw(BadNSEC, "Unexpected NSEC result for wildcard proof");
  166. }
  167. response_.addRRset(Message::SECTION_AUTHORITY,
  168. boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
  169. dnssec_);
  170. }else if(db_result.isNSEC3Signed() && db_result.isWildcard()){
  171. // case for RFC5155 Section 7.2.6
  172. const ZoneFinder::FindNSEC3Result NSEC3Result(finder.findNSEC3(qname_,
  173. true));
  174. /*
  175. if (NSEC3Result.code != ZoneFinder::NXDOMAIN || !NSEC3Result.rrset ||
  176. NSEC3Result.rrset->getRdataCount() == 0) {
  177. isc_throw(BadNSEC3, "Unexpected NSEC3 result for wildcard proof");
  178. }
  179. */
  180. response_.addRRset(Message::SECTION_AUTHORITY,
  181. boost::const_pointer_cast<AbstractRRset>(
  182. NSEC3Result.next_proof), dnssec_);
  183. const Name wname = Name("*").concatenate(
  184. qname_.split(qname_.getLabelCount() - NSEC3Result.closest_labels));
  185. const ZoneFinder::FindNSEC3Result wresult(finder.findNSEC3(wname,
  186. false));
  187. if (wresult.matched) {
  188. response_.addRRset(Message::SECTION_AUTHORITY,
  189. boost::const_pointer_cast<AbstractRRset>(
  190. wresult.closest_proof), dnssec_);
  191. } else {
  192. isc_throw(BadNSEC3, "No matching NSEC3 found for existing domain "
  193. << wname);
  194. }
  195. }
  196. }
  197. void
  198. Query::addWildcardNXRRSETProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
  199. // There should be one NSEC RR which was found in the zone to prove
  200. // that there is not matched <QNAME,QTYPE> via wildcard expansion.
  201. if (nsec->getRdataCount() == 0) {
  202. isc_throw(BadNSEC, "NSEC for WILDCARD_NXRRSET is empty");
  203. }
  204. const ZoneFinder::FindResult fresult =
  205. finder.find(qname_, RRType::NSEC(),
  206. dnssec_opt_ | ZoneFinder::NO_WILDCARD);
  207. if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
  208. fresult.rrset->getRdataCount() == 0) {
  209. isc_throw(BadNSEC, "Unexpected result for no match QNAME proof");
  210. }
  211. if (nsec->getName() != fresult.rrset->getName()) {
  212. // one NSEC RR proves wildcard_nxrrset that no matched QNAME.
  213. response_.addRRset(Message::SECTION_AUTHORITY,
  214. boost::const_pointer_cast<AbstractRRset>(fresult.rrset),
  215. dnssec_);
  216. }
  217. }
  218. void
  219. Query::addDS(ZoneFinder& finder, const Name& dname) {
  220. ZoneFinder::FindResult ds_result =
  221. finder.find(dname, RRType::DS(), dnssec_opt_);
  222. if (ds_result.code == ZoneFinder::SUCCESS) {
  223. response_.addRRset(Message::SECTION_AUTHORITY,
  224. boost::const_pointer_cast<AbstractRRset>(ds_result.rrset),
  225. dnssec_);
  226. } else if (ds_result.code == ZoneFinder::NXRRSET) {
  227. addNXRRsetProof(finder, ds_result);
  228. } else {
  229. // Any other case should be an error
  230. isc_throw(BadDS, "Unexpected result for DS lookup for delegation");
  231. }
  232. }
  233. void
  234. Query::addNXRRsetProof(ZoneFinder& finder,
  235. const ZoneFinder::FindResult& db_result)
  236. {
  237. if (db_result.isNSECSigned() && db_result.rrset) {
  238. response_.addRRset(Message::SECTION_AUTHORITY,
  239. boost::const_pointer_cast<AbstractRRset>(
  240. db_result.rrset),
  241. dnssec_);
  242. if (db_result.isWildcard()) {
  243. addWildcardNXRRSETProof(finder, db_result.rrset);
  244. }
  245. } else if (db_result.isNSEC3Signed()) {
  246. ZoneFinder::FindNSEC3Result result(finder.findNSEC3(qname_, false));
  247. if (result.matched) {
  248. response_.addRRset(Message::SECTION_AUTHORITY,
  249. boost::const_pointer_cast<AbstractRRset>(
  250. result.closest_proof), dnssec_);
  251. } else {
  252. isc_throw(BadNSEC3, "No NSEC3 found for existing domain " <<
  253. qname_.toText());
  254. }
  255. }
  256. }
  257. void
  258. Query::addAuthAdditional(ZoneFinder& finder) {
  259. // Fill in authority and addtional sections.
  260. ZoneFinder::FindResult ns_result =
  261. finder.find(finder.getOrigin(), RRType::NS(), dnssec_opt_);
  262. // zone origin name should have NS records
  263. if (ns_result.code != ZoneFinder::SUCCESS) {
  264. isc_throw(NoApexNS, "There's no apex NS records in zone " <<
  265. finder.getOrigin().toText());
  266. } else {
  267. response_.addRRset(Message::SECTION_AUTHORITY,
  268. boost::const_pointer_cast<AbstractRRset>(ns_result.rrset), dnssec_);
  269. // Handle additional for authority section
  270. addAdditional(finder, *ns_result.rrset);
  271. }
  272. }
  273. namespace {
  274. // A simple wrapper for DataSourceClient::findZone(). Normally we can simply
  275. // check the closest zone to the qname, but for type DS query we need to
  276. // look into the parent zone. Nevertheless, if there is no "parent" (i.e.,
  277. // the qname consists of a single label, which also means it's the root name),
  278. // we should search the deepest zone we have (which should be the root zone;
  279. // otherwise it's a query error).
  280. DataSourceClient::FindResult
  281. findZone(const DataSourceClient& client, const Name& qname, RRType qtype) {
  282. if (qtype != RRType::DS() || qname.getLabelCount() == 1) {
  283. return (client.findZone(qname));
  284. }
  285. return (client.findZone(qname.split(1)));
  286. }
  287. }
  288. void
  289. Query::process() {
  290. // Found a zone which is the nearest ancestor to QNAME
  291. const DataSourceClient::FindResult result = findZone(datasrc_client_,
  292. qname_, qtype_);
  293. // If we have no matching authoritative zone for the query name, return
  294. // REFUSED. In short, this is to be compatible with BIND 9, but the
  295. // background discussion is not that simple. See the relevant topic
  296. // at the BIND 10 developers's ML:
  297. // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
  298. if (result.code != result::SUCCESS &&
  299. result.code != result::PARTIALMATCH) {
  300. // If we tried to find a "parent zone" for a DS query and failed,
  301. // we may still have authority at the child side. If we do, the query
  302. // has to be handled there.
  303. if (qtype_ == RRType::DS() && qname_.getLabelCount() > 1 &&
  304. processDSAtChild()) {
  305. return;
  306. }
  307. response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
  308. response_.setRcode(Rcode::REFUSED());
  309. return;
  310. }
  311. ZoneFinder& zfinder = *result.zone_finder;
  312. // We have authority for a zone that contain the query name (possibly
  313. // indirectly via delegation). Look into the zone.
  314. response_.setHeaderFlag(Message::HEADERFLAG_AA);
  315. response_.setRcode(Rcode::NOERROR());
  316. std::vector<ConstRRsetPtr> target;
  317. boost::function0<ZoneFinder::FindResult> find;
  318. const bool qtype_is_any = (qtype_ == RRType::ANY());
  319. if (qtype_is_any) {
  320. find = boost::bind(&ZoneFinder::findAll, &zfinder, qname_,
  321. boost::ref(target), dnssec_opt_);
  322. } else {
  323. find = boost::bind(&ZoneFinder::find, &zfinder, qname_, qtype_,
  324. dnssec_opt_);
  325. }
  326. ZoneFinder::FindResult db_result(find());
  327. switch (db_result.code) {
  328. case ZoneFinder::DNAME: {
  329. // First, put the dname into the answer
  330. response_.addRRset(Message::SECTION_ANSWER,
  331. boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
  332. dnssec_);
  333. /*
  334. * Empty DNAME should never get in, as it is impossible to
  335. * create one in master file.
  336. *
  337. * FIXME: Other way to prevent this should be done
  338. */
  339. assert(db_result.rrset->getRdataCount() > 0);
  340. // Get the data of DNAME
  341. const rdata::generic::DNAME& dname(
  342. dynamic_cast<const rdata::generic::DNAME&>(
  343. db_result.rrset->getRdataIterator()->getCurrent()));
  344. // The yet unmatched prefix dname
  345. const Name prefix(qname_.split(0, qname_.getLabelCount() -
  346. db_result.rrset->getName().getLabelCount()));
  347. // If we put it together, will it be too long?
  348. // (The prefix contains trailing ., which will be removed
  349. if (prefix.getLength() - Name::ROOT_NAME().getLength() +
  350. dname.getDname().getLength() > Name::MAX_WIRE) {
  351. /*
  352. * In case the synthesized name is too long, section 4.1
  353. * of RFC 2672 mandates we return YXDOMAIN.
  354. */
  355. response_.setRcode(Rcode::YXDOMAIN());
  356. return;
  357. }
  358. // The new CNAME we are creating (it will be unsigned even
  359. // with DNSSEC, the DNAME is signed and it can be validated
  360. // by that)
  361. RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
  362. RRType::CNAME(), db_result.rrset->getTTL()));
  363. // Construct the new target by replacing the end
  364. cname->addRdata(rdata::generic::CNAME(qname_.split(0,
  365. qname_.getLabelCount() -
  366. db_result.rrset->getName().getLabelCount()).
  367. concatenate(dname.getDname())));
  368. response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
  369. break;
  370. }
  371. case ZoneFinder::CNAME:
  372. /*
  373. * We don't do chaining yet. Therefore handling a CNAME is
  374. * mostly the same as handling SUCCESS, but we didn't get
  375. * what we expected. It means no exceptions in ANY or NS
  376. * on the origin (though CNAME in origin is probably
  377. * forbidden anyway).
  378. *
  379. * So, just put it there.
  380. */
  381. response_.addRRset(Message::SECTION_ANSWER,
  382. boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
  383. dnssec_);
  384. // If the answer is a result of wildcard substitution,
  385. // add a proof that there's no closer name.
  386. if (dnssec_ && db_result.isWildcard()) {
  387. addWildcardProof(*result.zone_finder,db_result);
  388. }
  389. break;
  390. case ZoneFinder::SUCCESS:
  391. if (qtype_is_any) {
  392. // If quety type is ANY, insert all RRs under the domain
  393. // into answer section.
  394. BOOST_FOREACH(ConstRRsetPtr rrset, target) {
  395. response_.addRRset(Message::SECTION_ANSWER,
  396. boost::const_pointer_cast<AbstractRRset>(rrset), dnssec_);
  397. // Handle additional for answer section
  398. addAdditional(*result.zone_finder, *rrset.get());
  399. }
  400. } else {
  401. response_.addRRset(Message::SECTION_ANSWER,
  402. boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
  403. dnssec_);
  404. // Handle additional for answer section
  405. addAdditional(*result.zone_finder, *db_result.rrset);
  406. }
  407. // If apex NS records haven't been provided in the answer
  408. // section, insert apex NS records into the authority section
  409. // and AAAA/A RRS of each of the NS RDATA into the additional
  410. // section.
  411. if (qname_ != result.zone_finder->getOrigin() ||
  412. db_result.code != ZoneFinder::SUCCESS ||
  413. (qtype_ != RRType::NS() && !qtype_is_any))
  414. {
  415. addAuthAdditional(*result.zone_finder);
  416. }
  417. // If the answer is a result of wildcard substitution,
  418. // add a proof that there's no closer name.
  419. if (dnssec_ && db_result.isWildcard()) {
  420. addWildcardProof(*result.zone_finder,db_result);
  421. }
  422. break;
  423. case ZoneFinder::DELEGATION:
  424. // If a DS query resulted in delegation, we also need to check
  425. // if we are an authority of the child, too. If so, we need to
  426. // complete the process in the child as specified in Section
  427. // 2.2.1.2. of RFC3658.
  428. if (qtype_ == RRType::DS() && processDSAtChild()) {
  429. return;
  430. }
  431. response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
  432. response_.addRRset(Message::SECTION_AUTHORITY,
  433. boost::const_pointer_cast<AbstractRRset>(db_result.rrset),
  434. dnssec_);
  435. // If DNSSEC is requested, see whether there is a DS
  436. // record for this delegation.
  437. if (dnssec_) {
  438. addDS(*result.zone_finder, db_result.rrset->getName());
  439. }
  440. addAdditional(*result.zone_finder, *db_result.rrset);
  441. break;
  442. case ZoneFinder::NXDOMAIN:
  443. response_.setRcode(Rcode::NXDOMAIN());
  444. addSOA(*result.zone_finder);
  445. if (dnssec_ && db_result.rrset) {
  446. addNXDOMAINProof(zfinder, db_result.rrset);
  447. }
  448. break;
  449. case ZoneFinder::NXRRSET:
  450. addSOA(*result.zone_finder);
  451. if (dnssec_) {
  452. addNXRRsetProof(zfinder, db_result);
  453. }
  454. break;
  455. default:
  456. // This is basically a bug of the data source implementation,
  457. // but could also happen in the middle of development where
  458. // we try to add a new result code.
  459. isc_throw(isc::NotImplemented, "Unknown result code");
  460. break;
  461. }
  462. }
  463. bool
  464. Query::processDSAtChild() {
  465. const DataSourceClient::FindResult zresult =
  466. datasrc_client_.findZone(qname_);
  467. if (zresult.code != result::SUCCESS) {
  468. return (false);
  469. }
  470. // We are receiving a DS query at the child side of the owner name,
  471. // where the DS isn't supposed to belong. We should return a "no data"
  472. // response as described in Section 3.1.4.1 of RFC4035 and Section
  473. // 2.2.1.1 of RFC 3658. find(DS) should result in NXRRSET, in which
  474. // case (and if DNSSEC is required) we also add the proof for that,
  475. // but even if find() returns an unexpected result, we don't bother.
  476. // The important point in this case is to return SOA so that the resolver
  477. // that happens to contact us can hunt for the appropriate parent zone
  478. // by seeing the SOA.
  479. response_.setHeaderFlag(Message::HEADERFLAG_AA);
  480. response_.setRcode(Rcode::NOERROR());
  481. addSOA(*zresult.zone_finder);
  482. const ZoneFinder::FindResult ds_result =
  483. zresult.zone_finder->find(qname_, RRType::DS(), dnssec_opt_);
  484. if (ds_result.code == ZoneFinder::NXRRSET) {
  485. if (dnssec_) {
  486. addNXRRsetProof(*zresult.zone_finder, ds_result);
  487. }
  488. }
  489. return (true);
  490. }
  491. }
  492. }