zone_finder.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. // Copyright (C) 2012 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 <datasrc/memory/zone_finder.h>
  15. #include <datasrc/memory/domaintree.h>
  16. #include <datasrc/memory/treenode_rrset.h>
  17. #include <datasrc/zone.h>
  18. #include <datasrc/data_source.h>
  19. #include <dns/labelsequence.h>
  20. #include <dns/name.h>
  21. #include <dns/rrset.h>
  22. #include <dns/rrtype.h>
  23. #include <datasrc/logger.h>
  24. using namespace isc::dns;
  25. using namespace isc::datasrc::memory;
  26. using namespace isc::datasrc;
  27. namespace isc {
  28. namespace datasrc {
  29. namespace memory {
  30. namespace {
  31. /// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
  32. /// the given RRClass
  33. ///
  34. /// We should probably have some pool so these do not need to be allocated
  35. /// dynamically.
  36. ///
  37. /// \param node The ZoneNode found by the find() calls
  38. /// \param rdataset The RdataSet to create the RRsetPtr for
  39. /// \param rrclass The RRClass as passed by the client
  40. /// \param realname If given, the TreeNodeRRset is created with this name
  41. /// (e.g. for wildcard substitution)
  42. ///
  43. /// Returns an empty TreeNodeRRsetPtr if node is NULL or if rdataset is NULL.
  44. TreeNodeRRsetPtr
  45. createTreeNodeRRset(const ZoneNode* node,
  46. const RdataSet* rdataset,
  47. const RRClass& rrclass,
  48. const Name* realname = NULL)
  49. {
  50. if (node != NULL && rdataset != NULL) {
  51. if (realname != NULL) {
  52. return TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass, node,
  53. rdataset, true));
  54. } else {
  55. return TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node,
  56. rdataset, true));
  57. }
  58. } else {
  59. return TreeNodeRRsetPtr();
  60. }
  61. }
  62. /// Maintain intermediate data specific to the search context used in
  63. /// \c find().
  64. ///
  65. /// It will be passed to \c cutCallback() (see below) and record a possible
  66. /// zone cut node and related RRset (normally NS or DNAME).
  67. struct FindState {
  68. FindState(bool glue_ok) :
  69. zonecut_node_(NULL),
  70. dname_node_(NULL),
  71. rrset_(NULL),
  72. glue_ok_(glue_ok)
  73. {}
  74. // These will be set to a domain node of the highest delegation point,
  75. // if any. In fact, we could use a single variable instead of both.
  76. // But then we would need to distinquish these two cases by something
  77. // else and it seemed little more confusing when this was written.
  78. const ZoneNode* zonecut_node_;
  79. const ZoneNode* dname_node_;
  80. // Delegation RRset (NS or DNAME), if found.
  81. const RdataSet* rrset_;
  82. // Whether to continue search below a delegation point.
  83. // Set at construction time.
  84. const bool glue_ok_;
  85. };
  86. // A callback called from possible zone cut nodes and nodes with DNAME.
  87. // This will be passed from findNode() to \c RBTree::find().
  88. bool cutCallback(const ZoneNode& node, FindState* state) {
  89. // We need to look for DNAME first, there's allowed case where
  90. // DNAME and NS coexist in the apex. DNAME is the one to notice,
  91. // the NS is authoritative, not delegation (corner case explicitly
  92. // allowed by section 3 of 2672)
  93. const RdataSet* found_dname = RdataSet::find(node.getData(),
  94. RRType::DNAME());
  95. if (found_dname != NULL) {
  96. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
  97. state->dname_node_ = &node;
  98. state->rrset_ = found_dname;
  99. return (true);
  100. }
  101. // Look for NS
  102. const RdataSet* found_ns = RdataSet::find(node.getData(), RRType::NS());
  103. if (found_ns != NULL) {
  104. // We perform callback check only for the highest zone cut in the
  105. // rare case of nested zone cuts.
  106. if (state->zonecut_node_ != NULL) {
  107. return (false);
  108. }
  109. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
  110. // BIND 9 checks if this node is not the origin. That's probably
  111. // because it can support multiple versions for dynamic updates
  112. // and IXFR, and it's possible that the callback is called at
  113. // the apex and the DNAME doesn't exist for a particular version.
  114. // It cannot happen for us (at least for now), so we don't do
  115. // that check.
  116. state->zonecut_node_ = &node;
  117. state->rrset_ = found_ns;
  118. // Unless glue is allowed the search stops here, so we return
  119. // false; otherwise return true to continue the search.
  120. return (!state->glue_ok_);
  121. }
  122. // This case should not happen because we enable callback only
  123. // when we add an RR searched for above.
  124. assert(0);
  125. // This is here to avoid warning (therefore compilation error)
  126. // in case assert is turned off. Otherwise we could get "Control
  127. // reached end of non-void function".
  128. return (false);
  129. }
  130. /// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
  131. /// NSEC3 tree, for the given RRClass.
  132. ///
  133. /// It asserts that the node contains data (RdataSet) and is of type
  134. /// NSEC3.
  135. ///
  136. /// \param node The ZoneNode inside the NSEC3 tree
  137. /// \param rrclass The RRClass as passed by the client
  138. ConstRRsetPtr
  139. createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
  140. const RdataSet* rdataset = node->getData();
  141. // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
  142. // assert that these have data, and also are of type NSEC3.
  143. assert(rdataset != NULL);
  144. assert(rdataset->type == RRType::NSEC3());
  145. return (createTreeNodeRRset(node, rdataset, rrclass));
  146. }
  147. // convenience function to fill in the final details
  148. //
  149. // Set up ZoneFinderResultContext object as a return value of find(),
  150. // taking into account wildcard matches and DNSSEC information. We set
  151. // the NSEC/NSEC3 flag when applicable regardless of the find option; the
  152. // caller would simply ignore these when they didn't request DNSSEC
  153. // related results.
  154. //
  155. // Also performs the conversion of node + RdataSet into a TreeNodeRRsetPtr
  156. //
  157. // if wild is true, the RESULT_WILDCARD flag will be set.
  158. // If qname is not NULL, this is the query name, to be used in wildcard
  159. // substitution instead of the Node's name).
  160. isc::datasrc::memory::ZoneFinderResultContext
  161. createFindResult(const RRClass& rrclass,
  162. const ZoneData& zone_data,
  163. ZoneFinder::Result code,
  164. const RdataSet* rrset,
  165. const ZoneNode* node,
  166. bool wild = false,
  167. const Name* qname = NULL) {
  168. ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
  169. const Name* rename = NULL;
  170. if (wild) {
  171. flags = flags | ZoneFinder::RESULT_WILDCARD;
  172. // only use the rename qname if wild is true
  173. rename = qname;
  174. }
  175. if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) {
  176. if (zone_data.isNSEC3Signed()) {
  177. flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
  178. } else if (zone_data.isSigned()) {
  179. flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
  180. }
  181. }
  182. return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rrset,
  183. rrclass, rename),
  184. flags, node));
  185. }
  186. // A helper function for NSEC-signed zones. It searches the zone for
  187. // the "closest" NSEC corresponding to the search context stored in
  188. // node_path (it should contain sufficient information to identify the
  189. // previous name of the query name in the zone). In some cases the
  190. // immediate closest name may not have NSEC (when it's under a zone cut
  191. // for glue records, or even when the zone is partly broken), so this
  192. // method continues the search until it finds a name that has NSEC,
  193. // and returns the one found first. Due to the prerequisite (see below),
  194. // it should always succeed.
  195. //
  196. // node_path must store valid search context (in practice, it's expected
  197. // to be set by findNode()); otherwise the underlying RBTree implementation
  198. // throws.
  199. //
  200. // If the zone is not considered NSEC-signed or DNSSEC records were not
  201. // required in the original search context (specified in options), this
  202. // method doesn't bother to find NSEC, and simply returns NULL. So, by
  203. // definition of "NSEC-signed", when it really tries to find an NSEC it
  204. // should succeed; there should be one at least at the zone origin.
  205. const RdataSet*
  206. getClosestNSEC(const ZoneData& zone_data,
  207. ZoneChain& node_path,
  208. const ZoneNode** nsec_node,
  209. ZoneFinder::FindOptions options)
  210. {
  211. if (!zone_data.isSigned() ||
  212. (options & ZoneFinder::FIND_DNSSEC) == 0 ||
  213. zone_data.isNSEC3Signed()) {
  214. return (NULL);
  215. }
  216. const ZoneNode* prev_node;
  217. while ((prev_node = zone_data.getZoneTree().previousNode(node_path))
  218. != NULL) {
  219. if (!prev_node->isEmpty()) {
  220. const RdataSet* found =
  221. RdataSet::find(prev_node->getData(), RRType::NSEC());
  222. if (found != NULL) {
  223. *nsec_node = prev_node;
  224. return (found);
  225. }
  226. }
  227. }
  228. // This must be impossible and should be an internal bug.
  229. // See the description at the method declaration.
  230. assert(false);
  231. // Even though there is an assert here, strict compilers
  232. // will still need some return value.
  233. return (NULL);
  234. }
  235. // A helper function for the NXRRSET case in find(). If the zone is
  236. // NSEC-signed and DNSSEC records are requested, try to find NSEC
  237. // on the given node, and return it if found; return NULL for all other
  238. // cases.
  239. const RdataSet*
  240. getNSECForNXRRSET(const ZoneData& zone_data,
  241. ZoneFinder::FindOptions options,
  242. const ZoneNode* node)
  243. {
  244. if (zone_data.isSigned() &&
  245. !zone_data.isNSEC3Signed() &&
  246. (options & ZoneFinder::FIND_DNSSEC) != 0) {
  247. const RdataSet* found = RdataSet::find(node->getData(),
  248. RRType::NSEC());
  249. if (found != NULL) {
  250. return (found);
  251. }
  252. }
  253. return (NULL);
  254. }
  255. // Structure to hold result data of the findNode() call
  256. class FindNodeResult {
  257. public:
  258. // Bitwise flags to represent supplemental information of the
  259. // search result:
  260. // Search resulted in a wildcard match.
  261. static const unsigned int FIND_WILDCARD = 1;
  262. // Search encountered a zone cut due to NS but continued to look for
  263. // a glue.
  264. static const unsigned int FIND_ZONECUT = 2;
  265. FindNodeResult(ZoneFinder::Result code_param,
  266. const ZoneNode* node_param,
  267. const RdataSet* rrset_param,
  268. unsigned int flags_param = 0) :
  269. code(code_param),
  270. node(node_param),
  271. rrset(rrset_param),
  272. flags(flags_param)
  273. {}
  274. const ZoneFinder::Result code;
  275. const ZoneNode* node;
  276. const RdataSet* rrset;
  277. const unsigned int flags;
  278. };
  279. // Implementation notes: this method identifies an ZoneNode that best matches
  280. // the give name in terms of DNS query handling. In many cases,
  281. // DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
  282. // the given name is generally expected to be contained in the zone, so
  283. // even if it doesn't exist, it should at least match the zone origin).
  284. // If it finds an exact match, that's obviously the best one. The partial
  285. // match case is more complicated.
  286. //
  287. // We first need to consider the case where search hits a delegation point,
  288. // either due to NS or DNAME. They are indicated as either dname_node_ or
  289. // zonecut_node_ being non NULL. Usually at most one of them will be
  290. // something else than NULL (it might happen both are NULL, in which case we
  291. // consider it NOT FOUND). There's one corner case when both might be
  292. // something else than NULL and it is in case there's a DNAME under a zone
  293. // cut and we search in glue OK mode ‒ in that case we don't stop on the
  294. // domain with NS and ignore it for the answer, but it gets set anyway. Then
  295. // we find the DNAME and we need to act by it, therefore we first check for
  296. // DNAME and then for NS. In all other cases it doesn't matter, as at least
  297. // one of them is NULL.
  298. //
  299. // Next, we need to check if the ZoneTree search stopped at a node for a
  300. // subdomain of the search name (so the comparison result that stopped the
  301. // search is "SUPERDOMAIN"), it means the stopping node is an empty
  302. // non-terminal node. In this case the search name is considered to exist
  303. // but no data should be found there.
  304. //
  305. // If none of above is the case, we then consider whether there's a matching
  306. // wildcard. DomainTree::find() records the node if it encounters a
  307. // "wildcarding" node, i.e., the immediate ancestor of a wildcard name
  308. // (e.g., wild.example.com for *.wild.example.com), and returns it if it
  309. // doesn't find any node that better matches the query name. In this case
  310. // we'll check if there's indeed a wildcard below the wildcarding node.
  311. //
  312. // Note, first, that the wildcard is checked after the empty
  313. // non-terminal domain case above, because if that one triggers, it
  314. // means we should not match according to 4.3.3 of RFC 1034 (the query
  315. // name is known to exist).
  316. //
  317. // Before we try to find a wildcard, we should check whether there's
  318. // an existing node that would cancel the wildcard match. If
  319. // DomainTree::find() stopped at a node which has a common ancestor
  320. // with the query name, it might mean we are comparing with a
  321. // non-wildcard node. In that case, we check which part is common. If
  322. // we have something in common that lives below the node we got (the
  323. // one above *), then we should cancel the match according to section
  324. // 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
  325. // query name is known to exist).
  326. //
  327. // If there's no node below the wildcarding node that shares a common ancestor
  328. // of the query name, we can conclude the wildcard is the best match.
  329. // We'll then identify the wildcard node via an incremental search. Note that
  330. // there's no possibility that the query name is at an empty non terminal
  331. // node below the wildcarding node at this stage; that case should have been
  332. // caught above.
  333. //
  334. // If none of the above succeeds, we conclude the name doesn't exist in
  335. // the zone, and throw an OutOfZone exception.
  336. FindNodeResult findNode(const ZoneData& zone_data,
  337. const Name& name,
  338. ZoneChain& node_path,
  339. ZoneFinder::FindOptions options)
  340. {
  341. ZoneNode* node = NULL;
  342. FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
  343. const ZoneTree& tree(zone_data.getZoneTree());
  344. ZoneTree::Result result = tree.find(isc::dns::LabelSequence(name),
  345. &node, node_path, cutCallback,
  346. &state);
  347. const unsigned int zonecut_flag =
  348. (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
  349. if (result == ZoneTree::EXACTMATCH) {
  350. return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
  351. zonecut_flag));
  352. } else if (result == ZoneTree::PARTIALMATCH) {
  353. assert(node != NULL);
  354. if (state.dname_node_ != NULL) { // DNAME
  355. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
  356. arg(state.dname_node_->getName());
  357. return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_,
  358. state.rrset_));
  359. }
  360. if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
  361. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
  362. arg(state.zonecut_node_->getName());
  363. return (FindNodeResult(ZoneFinder::DELEGATION,
  364. state.zonecut_node_,
  365. state.rrset_));
  366. }
  367. if (node_path.getLastComparisonResult().getRelation() ==
  368. NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
  369. LOG_DEBUG(logger, DBG_TRACE_DATA,
  370. DATASRC_MEM_SUPER_STOP).arg(name);
  371. const ZoneNode* nsec_node;
  372. const RdataSet* nsec_rds = getClosestNSEC(zone_data,
  373. node_path,
  374. &nsec_node,
  375. options);
  376. return (FindNodeResult(ZoneFinder::NXRRSET, nsec_node,
  377. nsec_rds));
  378. }
  379. // Nothing really matched.
  380. // May be a wildcard, but check only if not disabled
  381. if (node->getFlag(ZoneData::WILDCARD_NODE) &&
  382. (options & ZoneFinder::NO_WILDCARD) == 0) {
  383. if (node_path.getLastComparisonResult().getRelation() ==
  384. NameComparisonResult::COMMONANCESTOR) {
  385. // This means, e.g., we have *.wild.example and
  386. // bar.foo.wild.example and are looking for
  387. // baz.foo.wild.example. The common ancestor, foo.wild.example,
  388. // should cancel wildcard. Treat it as NXDOMAIN.
  389. LOG_DEBUG(logger, DBG_TRACE_DATA,
  390. DATASRC_MEM_WILDCARD_CANCEL).arg(name);
  391. const ZoneNode* nsec_node;
  392. const RdataSet* nsec_rds = getClosestNSEC(zone_data,
  393. node_path,
  394. &nsec_node,
  395. options);
  396. return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node,
  397. nsec_rds));
  398. }
  399. uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
  400. // Create the wildcard name (i.e. take "*" and extend it
  401. // with all node labels down to the wildcard node
  402. LabelSequence wildcard_ls(LabelSequence::WILDCARD(), ls_buf);
  403. const ZoneNode* extend_with = node;
  404. while (extend_with != NULL) {
  405. wildcard_ls.extend(extend_with->getLabels(), ls_buf);
  406. extend_with = extend_with->getUpperNode();
  407. }
  408. // Clear the node_path so that we don't keep incorrect (NSEC)
  409. // context
  410. node_path.clear();
  411. ZoneTree::Result result = tree.find(LabelSequence(wildcard_ls),
  412. &node, node_path, cutCallback,
  413. &state);
  414. // Otherwise, why would the domain_flag::WILD be there if
  415. // there was no wildcard under it?
  416. assert(result == ZoneTree::EXACTMATCH);
  417. return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rrset_,
  418. FindNodeResult::FIND_WILDCARD | zonecut_flag));
  419. }
  420. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).arg(name);
  421. const ZoneNode* nsec_node;
  422. const RdataSet* nsec_rds = getClosestNSEC(zone_data, node_path,
  423. &nsec_node, options);
  424. return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_node, nsec_rds));
  425. } else {
  426. // If the name is neither an exact or partial match, it is
  427. // out of bailiwick, which is considered an error.
  428. isc_throw(OutOfZone, name.toText() << " not in " <<
  429. zone_data.getOriginNode()->getName());
  430. }
  431. }
  432. } // end anonymous namespace
  433. // Specialization of the ZoneFinder::Context for the in-memory finder.
  434. class InMemoryZoneFinder::Context : public ZoneFinder::Context {
  435. public:
  436. /// \brief Constructor.
  437. ///
  438. /// Note that we don't have a specific constructor for the findAll() case.
  439. /// For (successful) type ANY query, found_node points to the
  440. /// corresponding RB node, which is recorded within this specialized
  441. /// context.
  442. Context(ZoneFinder& finder, ZoneFinder::FindOptions options,
  443. ZoneFinderResultContext result) :
  444. ZoneFinder::Context(finder, options,
  445. ResultContext(result.code, result.rrset,
  446. result.flags)),
  447. rrset_(result.rrset), found_node_(result.found_node)
  448. {}
  449. private:
  450. const TreeNodeRRsetPtr rrset_;
  451. const ZoneNode* const found_node_;
  452. };
  453. boost::shared_ptr<ZoneFinder::Context>
  454. InMemoryZoneFinder::find(const isc::dns::Name& name,
  455. const isc::dns::RRType& type,
  456. const FindOptions options)
  457. {
  458. return ZoneFinderContextPtr(new Context(*this, options,
  459. find_internal(name,
  460. type,
  461. NULL,
  462. options)));
  463. }
  464. boost::shared_ptr<ZoneFinder::Context>
  465. InMemoryZoneFinder::findAll(const isc::dns::Name& name,
  466. std::vector<isc::dns::ConstRRsetPtr>& target,
  467. const FindOptions options)
  468. {
  469. return ZoneFinderContextPtr(new Context(*this, options,
  470. find_internal(name,
  471. RRType::ANY(),
  472. &target,
  473. options)));
  474. }
  475. ZoneFinderResultContext
  476. InMemoryZoneFinder::find_internal(const isc::dns::Name& name,
  477. const isc::dns::RRType& type,
  478. std::vector<ConstRRsetPtr>* target,
  479. const FindOptions options)
  480. {
  481. // Get the node. All other cases than an exact match are handled
  482. // in findNode(). We simply construct a result structure and return.
  483. ZoneChain node_path;
  484. const FindNodeResult node_result =
  485. findNode(zone_data_, name, node_path, options);
  486. if (node_result.code != SUCCESS) {
  487. return (createFindResult(rrclass_, zone_data_, node_result.code,
  488. node_result.rrset, node_result.node));
  489. }
  490. const ZoneNode* node = node_result.node;
  491. assert(node != NULL);
  492. // We've found an exact match, may or may not be a result of wildcard.
  493. const bool wild = ((node_result.flags &
  494. FindNodeResult::FIND_WILDCARD) != 0);
  495. // If there is an exact match but the node is empty, it's equivalent
  496. // to NXRRSET.
  497. if (node->isEmpty()) {
  498. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
  499. arg(name);
  500. const ZoneNode* nsec_node;
  501. const RdataSet* nsec_rds = getClosestNSEC(zone_data_, node_path,
  502. &nsec_node, options);
  503. return (createFindResult(rrclass_, zone_data_, NXRRSET,
  504. nsec_rds,
  505. nsec_node,
  506. wild));
  507. }
  508. const RdataSet* found;
  509. // If the node callback is enabled, this may be a zone cut. If it
  510. // has a NS RR, we should return a delegation, but not in the apex.
  511. // There is one exception: the case for DS query, which should always
  512. // be considered in-zone lookup.
  513. if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
  514. node != zone_data_.getOriginNode() && type != RRType::DS()) {
  515. found = RdataSet::find(node->getData(), RRType::NS());
  516. if (found != NULL) {
  517. LOG_DEBUG(logger, DBG_TRACE_DATA,
  518. DATASRC_MEM_EXACT_DELEGATION).arg(name);
  519. return (createFindResult(rrclass_, zone_data_, DELEGATION,
  520. found, node, wild, &name));
  521. }
  522. }
  523. // Handle type any query
  524. if (target != NULL && node->getData() != NULL) {
  525. // Empty domain will be handled as NXRRSET by normal processing
  526. const RdataSet* cur_rds = node->getData();
  527. while (cur_rds != NULL) {
  528. target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
  529. &name));
  530. cur_rds = cur_rds->getNext();
  531. }
  532. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
  533. arg(name);
  534. return (createFindResult(rrclass_, zone_data_, SUCCESS, NULL, node,
  535. wild, &name));
  536. }
  537. const RdataSet* currds = node->getData();
  538. while (currds != NULL) {
  539. currds = currds->getNext();
  540. }
  541. found = RdataSet::find(node->getData(), type);
  542. if (found != NULL) {
  543. // Good, it is here
  544. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
  545. arg(type);
  546. return (createFindResult(rrclass_, zone_data_, SUCCESS, found, node,
  547. wild, &name));
  548. } else {
  549. // Next, try CNAME.
  550. found = RdataSet::find(node->getData(), RRType::CNAME());
  551. if (found != NULL) {
  552. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
  553. return (createFindResult(rrclass_, zone_data_, CNAME, found, node,
  554. wild, &name));
  555. }
  556. }
  557. // No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
  558. return (createFindResult(rrclass_, zone_data_, NXRRSET,
  559. getNSECForNXRRSET(zone_data_, options, node),
  560. node, wild, &name));
  561. }
  562. isc::datasrc::ZoneFinder::FindNSEC3Result
  563. InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
  564. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
  565. arg(recursive ? "recursive" : "non-recursive");
  566. if (!zone_data_.isNSEC3Signed()) {
  567. isc_throw(DataSourceError,
  568. "findNSEC3 attempt for non NSEC3 signed zone: " <<
  569. getOrigin() << "/" << getClass());
  570. }
  571. const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
  572. // This would be a programming mistake, as ZoneData::isNSEC3Signed()
  573. // should check this.
  574. assert(nsec3_data != NULL);
  575. const ZoneTree& tree = nsec3_data->getNSEC3Tree();
  576. if (tree.getNodeCount() == 0) {
  577. isc_throw(DataSourceError,
  578. "findNSEC3 attempt but zone has no NSEC3 RR: " <<
  579. getOrigin() << "/" << getClass());
  580. }
  581. const NameComparisonResult cmp_result = name.compare(getOrigin());
  582. if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
  583. cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
  584. isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
  585. << name << ", zone: " << getOrigin() << "/"
  586. << getClass());
  587. }
  588. // Convenient shortcuts
  589. const unsigned int olabels = getOrigin().getLabelCount();
  590. const unsigned int qlabels = name.getLabelCount();
  591. // placeholder of the next closer proof
  592. const ZoneNode* covering_node(NULL);
  593. ZoneChain chain;
  594. // Examine all names from the query name to the origin name, stripping
  595. // the deepest label one by one, until we find a name that has a matching
  596. // NSEC3 hash.
  597. for (unsigned int labels = qlabels; labels >= olabels; --labels) {
  598. const Name& hname = (labels == qlabels ?
  599. name : name.split(qlabels - labels, labels));
  600. const std::string hlabel =
  601. (nsec3_calculate_) (hname,
  602. nsec3_data->iterations,
  603. nsec3_data->getSaltData(),
  604. nsec3_data->getSaltLen());
  605. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
  606. arg(name).arg(labels).arg(hlabel);
  607. ZoneNode* node(NULL);
  608. chain.clear();
  609. const ZoneTree::Result result =
  610. tree.find(Name(hlabel).concatenate(getOrigin()), &node, chain);
  611. if (result == ZoneTree::EXACTMATCH) {
  612. // We found an exact match.
  613. ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
  614. ConstRRsetPtr next;
  615. if (covering_node != NULL) {
  616. next = createNSEC3RRset(covering_node, getClass());
  617. }
  618. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  619. DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
  620. arg(*closest);
  621. return (FindNSEC3Result(true, labels, closest, next));
  622. } else {
  623. while ((covering_node = tree.previousNode(chain)) != NULL &&
  624. covering_node->isEmpty()) {
  625. ;
  626. }
  627. if (covering_node == NULL) {
  628. covering_node = tree.largestNode();
  629. }
  630. if (!recursive) { // in non recursive mode, we are done.
  631. ConstRRsetPtr closest;
  632. if (covering_node != NULL) {
  633. closest = createNSEC3RRset(covering_node, getClass());
  634. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  635. DATASRC_MEM_FINDNSEC3_COVER).
  636. arg(name).arg(*closest);
  637. }
  638. return (FindNSEC3Result(false, labels,
  639. closest, ConstRRsetPtr()));
  640. }
  641. }
  642. }
  643. isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
  644. "a broken NSEC3 zone: " << getOrigin() << "/"
  645. << getClass());
  646. }
  647. } // namespace memory
  648. } // namespace datasrc
  649. } // namespace isc