zone_finder.cc 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  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/memory/rdata_serialization.h>
  18. #include <datasrc/zone_finder.h>
  19. #include <datasrc/data_source.h>
  20. #include <dns/labelsequence.h>
  21. #include <dns/name.h>
  22. #include <dns/rrset.h>
  23. #include <dns/rrtype.h>
  24. #include <dns/nsec3hash.h>
  25. #include <datasrc/logger.h>
  26. #include <boost/scoped_ptr.hpp>
  27. #include <boost/bind.hpp>
  28. #include <algorithm>
  29. #include <vector>
  30. #include <utility>
  31. using namespace isc::dns;
  32. using namespace isc::datasrc::memory;
  33. using namespace isc::datasrc;
  34. namespace isc {
  35. namespace datasrc {
  36. namespace memory {
  37. namespace internal {
  38. // Specialized version of ZoneFinder::ResultContext, which holds objects
  39. // related to find() results using internal representations of the in-memory
  40. // data source implementation.
  41. class ZoneFinderResultContext {
  42. public:
  43. /// \brief Constructor
  44. ///
  45. /// The first three parameters correspond to those of
  46. /// ZoneFinder::ResultContext. If node is non NULL, it specifies the
  47. /// found ZoneNode in the search.
  48. ZoneFinderResultContext(ZoneFinder::Result code_param,
  49. TreeNodeRRsetPtr rrset_param,
  50. ZoneFinder::FindResultFlags flags_param,
  51. const ZoneData& zone_data_param,
  52. const ZoneNode* node, const RdataSet* rdset) :
  53. code(code_param), rrset(rrset_param), flags(flags_param),
  54. zone_data(&zone_data_param), found_node(node), found_rdset(rdset)
  55. {}
  56. const ZoneFinder::Result code;
  57. const TreeNodeRRsetPtr rrset;
  58. const ZoneFinder::FindResultFlags flags;
  59. const ZoneData* const zone_data;
  60. const ZoneNode* const found_node;
  61. const RdataSet* const found_rdset;
  62. };
  63. }
  64. using internal::ZoneFinderResultContext;
  65. namespace {
  66. /// Conceptual RRset in the form of a pair of zone node and RdataSet.
  67. ///
  68. /// In this implementation, the owner name of an RRset is derived from the
  69. /// corresponding zone node, and the rest of the attributes come from
  70. /// an RdataSet. This shortcut type can be used when we want to refer to
  71. /// the conceptual RRset without knowing these details.
  72. ///
  73. /// This is a read-only version of the pair (and at the moment we don't need
  74. /// a mutable version).
  75. typedef std::pair<const ZoneNode*, const RdataSet*> ConstNodeRRset;
  76. /// Creates a TreeNodeRRsetPtr for the given RdataSet at the given Node, for
  77. /// the given RRClass
  78. ///
  79. /// We should probably have some pool so these do not need to be allocated
  80. /// dynamically.
  81. ///
  82. /// \param node The ZoneNode found by the find() calls
  83. /// \param rdataset The RdataSet to create the RRsetPtr for
  84. /// \param rrclass The RRClass as passed by the client
  85. /// \param realname If given, the TreeNodeRRset is created with this name
  86. /// (e.g. for wildcard substitution)
  87. ///
  88. /// Returns an empty TreeNodeRRsetPtr if node is NULL or if rdataset is NULL.
  89. TreeNodeRRsetPtr
  90. createTreeNodeRRset(const ZoneNode* node,
  91. const RdataSet* rdataset,
  92. const RRClass& rrclass,
  93. ZoneFinder::FindOptions options,
  94. const Name* realname = NULL)
  95. {
  96. const bool dnssec = ((options & ZoneFinder::FIND_DNSSEC) != 0);
  97. if (node != NULL && rdataset != NULL) {
  98. if (realname != NULL) {
  99. return (TreeNodeRRsetPtr(new TreeNodeRRset(*realname, rrclass,
  100. node, rdataset,
  101. dnssec)));
  102. } else {
  103. return (TreeNodeRRsetPtr(new TreeNodeRRset(rrclass, node, rdataset,
  104. dnssec)));
  105. }
  106. } else {
  107. return (TreeNodeRRsetPtr());
  108. }
  109. }
  110. /// Maintain intermediate data specific to the search context used in
  111. /// \c find().
  112. ///
  113. /// It will be passed to \c cutCallback() (see below) and record a possible
  114. /// zone cut node and related RRset (normally NS or DNAME).
  115. struct FindState {
  116. FindState(bool glue_ok) :
  117. zonecut_node_(NULL),
  118. dname_node_(NULL),
  119. rdataset_(NULL),
  120. glue_ok_(glue_ok)
  121. {}
  122. // These will be set to a domain node of the highest delegation point,
  123. // if any. In fact, we could use a single variable instead of both.
  124. // But then we would need to distinquish these two cases by something
  125. // else and it seemed little more confusing when this was written.
  126. const ZoneNode* zonecut_node_;
  127. const ZoneNode* dname_node_;
  128. // Delegation RRset (NS or DNAME), if found.
  129. const RdataSet* rdataset_;
  130. // Whether to continue search below a delegation point.
  131. // Set at construction time.
  132. const bool glue_ok_;
  133. };
  134. // A callback called from possible zone cut nodes and nodes with DNAME.
  135. // This will be passed from findNode() to \c ZoneTree::find().
  136. bool cutCallback(const ZoneNode& node, FindState* state) {
  137. // We need to look for DNAME first, there's allowed case where
  138. // DNAME and NS coexist in the apex. DNAME is the one to notice,
  139. // the NS is authoritative, not delegation (corner case explicitly
  140. // allowed by section 3 of 2672)
  141. const RdataSet* found_dname = RdataSet::find(node.getData(),
  142. RRType::DNAME());
  143. if (found_dname != NULL) {
  144. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_DNAME_ENCOUNTERED);
  145. state->dname_node_ = &node;
  146. state->rdataset_ = found_dname;
  147. return (true);
  148. }
  149. // Look for NS
  150. const RdataSet* found_ns = RdataSet::find(node.getData(), RRType::NS());
  151. if (found_ns != NULL) {
  152. // We perform callback check only for the highest zone cut in the
  153. // rare case of nested zone cuts.
  154. if (state->zonecut_node_ != NULL) {
  155. return (false);
  156. }
  157. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
  158. // BIND 9 checks if this node is not the origin. That's probably
  159. // because it can support multiple versions for dynamic updates
  160. // and IXFR, and it's possible that the callback is called at
  161. // the apex and the DNAME doesn't exist for a particular version.
  162. // It cannot happen for us (at least for now), so we don't do
  163. // that check.
  164. state->zonecut_node_ = &node;
  165. state->rdataset_ = found_ns;
  166. // Unless glue is allowed the search stops here, so we return
  167. // false; otherwise return true to continue the search.
  168. return (!state->glue_ok_);
  169. }
  170. // This case should not happen because we enable callback only
  171. // when we add an RR searched for above.
  172. assert(0);
  173. // This is here to avoid warning (therefore compilation error)
  174. // in case assert is turned off. Otherwise we could get "Control
  175. // reached end of non-void function".
  176. return (false);
  177. }
  178. /// Creates a NSEC3 ConstRRsetPtr for the given ZoneNode inside the
  179. /// NSEC3 tree, for the given RRClass.
  180. ///
  181. /// It asserts that the node contains data (RdataSet) and is of type
  182. /// NSEC3.
  183. ///
  184. /// \param node The ZoneNode inside the NSEC3 tree
  185. /// \param rrclass The RRClass as passed by the client
  186. ConstRRsetPtr
  187. createNSEC3RRset(const ZoneNode* node, const RRClass& rrclass) {
  188. const RdataSet* rdataset = node->getData();
  189. // Only NSEC3 ZoneNodes are allowed to be passed to this method. We
  190. // assert that these have data, and also are of type NSEC3.
  191. assert(rdataset != NULL);
  192. assert(rdataset->type == RRType::NSEC3());
  193. // Check for the rare case of RRSIG-only record; in theory it could exist
  194. // but we simply consider it broken for NSEC3.
  195. if (rdataset->getRdataCount() == 0) {
  196. uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
  197. isc_throw(DataSourceError, "Broken zone: RRSIG-only NSEC3 record at "
  198. << node->getAbsoluteLabels(labels_buf) << "/" << rrclass);
  199. }
  200. // Create the RRset. Note the DNSSEC flag: NSEC3 implies DNSSEC.
  201. return (createTreeNodeRRset(node, rdataset, rrclass,
  202. ZoneFinder::FIND_DNSSEC));
  203. }
  204. // convenience function to fill in the final details
  205. //
  206. // Set up ZoneFinderResultContext object as a return value of find(),
  207. // taking into account wildcard matches and DNSSEC information. We set
  208. // the NSEC/NSEC3 flag when applicable regardless of the find option; the
  209. // caller would simply ignore these when they didn't request DNSSEC
  210. // related results.
  211. //
  212. // Also performs the conversion of node + RdataSet into a TreeNodeRRsetPtr
  213. //
  214. // if wild is true, the RESULT_WILDCARD flag will be set.
  215. // If qname is not NULL, this is the query name, to be used in wildcard
  216. // substitution instead of the Node's name).
  217. ZoneFinderResultContext
  218. createFindResult(const RRClass& rrclass,
  219. const ZoneData& zone_data,
  220. ZoneFinder::Result code,
  221. const ZoneNode* node,
  222. const RdataSet* rdataset,
  223. ZoneFinder::FindOptions options,
  224. bool wild = false,
  225. const Name* qname = NULL)
  226. {
  227. ZoneFinder::FindResultFlags flags = ZoneFinder::RESULT_DEFAULT;
  228. const Name* rename = NULL;
  229. if (wild) {
  230. flags = flags | ZoneFinder::RESULT_WILDCARD;
  231. // only use the rename qname if wild is true
  232. rename = qname;
  233. }
  234. if (code == ZoneFinder::NXRRSET || code == ZoneFinder::NXDOMAIN || wild) {
  235. if (zone_data.isNSEC3Signed()) {
  236. flags = flags | ZoneFinder::RESULT_NSEC3_SIGNED;
  237. } else if (zone_data.isSigned()) {
  238. flags = flags | ZoneFinder::RESULT_NSEC_SIGNED;
  239. }
  240. }
  241. return (ZoneFinderResultContext(code, createTreeNodeRRset(node, rdataset,
  242. rrclass, options,
  243. rename),
  244. flags, zone_data, node, rdataset));
  245. }
  246. // A helper function for NSEC-signed zones. It searches the zone for
  247. // the "closest" NSEC corresponding to the search context stored in
  248. // node_path (it should contain sufficient information to identify the
  249. // previous name of the query name in the zone). In some cases the
  250. // immediate closest name may not have NSEC (when it's under a zone cut
  251. // for glue records, or even when the zone is partly broken), so this
  252. // method continues the search until it finds a name that has NSEC,
  253. // and returns the one found first. Due to the prerequisite (see below),
  254. // it should always succeed.
  255. //
  256. // node_path must store valid search context (in practice, it's expected
  257. // to be set by findNode()); otherwise the underlying ZoneTree implementation
  258. // throws.
  259. //
  260. // If the zone is not considered NSEC-signed or DNSSEC records were not
  261. // required in the original search context (specified in options), this
  262. // method doesn't bother to find NSEC, and simply returns NULL. So, by
  263. // definition of "NSEC-signed", when it really tries to find an NSEC it
  264. // should succeed; there should be one at least at the zone origin.
  265. ConstNodeRRset
  266. getClosestNSEC(const ZoneData& zone_data,
  267. ZoneChain& node_path,
  268. ZoneFinder::FindOptions options)
  269. {
  270. if (!zone_data.isSigned() ||
  271. (options & ZoneFinder::FIND_DNSSEC) == 0 ||
  272. zone_data.isNSEC3Signed()) {
  273. return (ConstNodeRRset(NULL, NULL));
  274. }
  275. const ZoneNode* prev_node;
  276. if (node_path.getLastComparisonResult().getRelation() ==
  277. NameComparisonResult::SUBDOMAIN) {
  278. // In case the search ended as a sub-domain, the previous node
  279. // is already at the top of node_path.
  280. prev_node = node_path.getLastComparedNode();
  281. } else {
  282. prev_node = zone_data.getZoneTree().previousNode(node_path);
  283. }
  284. while (prev_node != NULL) {
  285. if (!prev_node->isEmpty()) {
  286. const RdataSet* found =
  287. RdataSet::find(prev_node->getData(), RRType::NSEC());
  288. if (found != NULL) {
  289. return (ConstNodeRRset(prev_node, found));
  290. }
  291. }
  292. prev_node = zone_data.getZoneTree().previousNode(node_path);
  293. }
  294. // This must be impossible and should be an internal bug.
  295. // See the description at the method declaration.
  296. assert(false);
  297. // Even though there is an assert here, strict compilers
  298. // will still need some return value.
  299. return (ConstNodeRRset(NULL, NULL));
  300. }
  301. // A helper function for the NXRRSET case in find(). If the zone is
  302. // NSEC-signed and DNSSEC records are requested, try to find NSEC
  303. // on the given node, and return it if found; return NULL for all other
  304. // cases.
  305. const RdataSet*
  306. getNSECForNXRRSET(const ZoneData& zone_data,
  307. ZoneFinder::FindOptions options,
  308. const ZoneNode* node)
  309. {
  310. if (zone_data.isSigned() &&
  311. !zone_data.isNSEC3Signed() &&
  312. (options & ZoneFinder::FIND_DNSSEC) != 0) {
  313. const RdataSet* found = RdataSet::find(node->getData(),
  314. RRType::NSEC());
  315. if (found != NULL) {
  316. return (found);
  317. }
  318. }
  319. return (NULL);
  320. }
  321. // Structure to hold result data of the findNode() call
  322. class FindNodeResult {
  323. public:
  324. // Bitwise flags to represent supplemental information of the
  325. // search result:
  326. // Search resulted in a wildcard match.
  327. static const unsigned int FIND_WILDCARD = 1;
  328. // Search encountered a zone cut due to NS but continued to look for
  329. // a glue.
  330. static const unsigned int FIND_ZONECUT = 2;
  331. FindNodeResult(ZoneFinder::Result code_param,
  332. const ZoneNode* node_param,
  333. const RdataSet* rdataset_param,
  334. unsigned int flags_param = 0) :
  335. code(code_param),
  336. node(node_param),
  337. rdataset(rdataset_param),
  338. flags(flags_param)
  339. {}
  340. const ZoneFinder::Result code;
  341. const ZoneNode* node;
  342. const RdataSet* rdataset;
  343. const unsigned int flags;
  344. };
  345. // Implementation notes: this method identifies an ZoneNode that best matches
  346. // the give name in terms of DNS query handling. In many cases,
  347. // DomainTree::find() will result in EXACTMATCH or PARTIALMATCH (note that
  348. // the given name is generally expected to be contained in the zone, so
  349. // even if it doesn't exist, it should at least match the zone origin).
  350. // If it finds an exact match, that's obviously the best one. The partial
  351. // match case is more complicated.
  352. //
  353. // We first need to consider the case where search hits a delegation point,
  354. // either due to NS or DNAME. They are indicated as either dname_node_ or
  355. // zonecut_node_ being non NULL. Usually at most one of them will be
  356. // something else than NULL (it might happen both are NULL, in which case we
  357. // consider it NOT FOUND). There's one corner case when both might be
  358. // something else than NULL and it is in case there's a DNAME under a zone
  359. // cut and we search in glue OK mode ‒ in that case we don't stop on the
  360. // domain with NS and ignore it for the answer, but it gets set anyway. Then
  361. // we find the DNAME and we need to act by it, therefore we first check for
  362. // DNAME and then for NS. In all other cases it doesn't matter, as at least
  363. // one of them is NULL.
  364. //
  365. // Next, we need to check if the ZoneTree search stopped at a node for a
  366. // subdomain of the search name (so the comparison result that stopped the
  367. // search is "SUPERDOMAIN"), it means the stopping node is an empty
  368. // non-terminal node. In this case the search name is considered to exist
  369. // but no data should be found there.
  370. //
  371. // If none of above is the case, we then consider whether there's a matching
  372. // wildcard. DomainTree::find() records the node if it encounters a
  373. // "wildcarding" node, i.e., the immediate ancestor of a wildcard name
  374. // (e.g., wild.example.com for *.wild.example.com), and returns it if it
  375. // doesn't find any node that better matches the query name. In this case
  376. // we'll check if there's indeed a wildcard below the wildcarding node.
  377. //
  378. // Note, first, that the wildcard is checked after the empty
  379. // non-terminal domain case above, because if that one triggers, it
  380. // means we should not match according to 4.3.3 of RFC 1034 (the query
  381. // name is known to exist).
  382. //
  383. // Before we try to find a wildcard, we should check whether there's
  384. // an existing node that would cancel the wildcard match. If
  385. // DomainTree::find() stopped at a node which has a common ancestor
  386. // with the query name, it might mean we are comparing with a
  387. // non-wildcard node. In that case, we check which part is common. If
  388. // we have something in common that lives below the node we got (the
  389. // one above *), then we should cancel the match according to section
  390. // 4.3.3 of RFC 1034 (as the name between the wildcard domain and the
  391. // query name is known to exist).
  392. //
  393. // If there's no node below the wildcarding node that shares a common ancestor
  394. // of the query name, we can conclude the wildcard is the best match.
  395. // We'll then identify the wildcard node via an incremental search. Note that
  396. // there's no possibility that the query name is at an empty non terminal
  397. // node below the wildcarding node at this stage; that case should have been
  398. // caught above.
  399. //
  400. // If none of the above succeeds, we conclude the name doesn't exist in
  401. // the zone, and throw an OutOfZone exception by default. If the optional
  402. // out_of_zone_ok is true, it returns an NXDOMAIN result with NULL data so
  403. // the caller can take an action to it (technically it's not "NXDOMAIN",
  404. // but the caller is assumed not to rely on the difference.)
  405. FindNodeResult findNode(const ZoneData& zone_data,
  406. const LabelSequence& name_labels,
  407. ZoneChain& node_path,
  408. ZoneFinder::FindOptions options,
  409. bool out_of_zone_ok = false)
  410. {
  411. const ZoneNode* node = NULL;
  412. FindState state((options & ZoneFinder::FIND_GLUE_OK) != 0);
  413. const ZoneTree& tree(zone_data.getZoneTree());
  414. const ZoneTree::Result result = tree.find(name_labels, &node, node_path,
  415. cutCallback, &state);
  416. const unsigned int zonecut_flag =
  417. (state.zonecut_node_ != NULL) ? FindNodeResult::FIND_ZONECUT : 0;
  418. if (result == ZoneTree::EXACTMATCH) {
  419. return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rdataset_,
  420. zonecut_flag));
  421. } else if (result == ZoneTree::PARTIALMATCH) {
  422. assert(node != NULL);
  423. if (state.dname_node_ != NULL) { // DNAME
  424. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
  425. arg(state.dname_node_->getName());
  426. return (FindNodeResult(ZoneFinder::DNAME, state.dname_node_,
  427. state.rdataset_));
  428. }
  429. if (state.zonecut_node_ != NULL) { // DELEGATION due to NS
  430. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
  431. arg(state.zonecut_node_->getName());
  432. return (FindNodeResult(ZoneFinder::DELEGATION,
  433. state.zonecut_node_,
  434. state.rdataset_));
  435. }
  436. if (node_path.getLastComparisonResult().getRelation() ==
  437. NameComparisonResult::SUPERDOMAIN) { // empty node, so NXRRSET
  438. LOG_DEBUG(logger, DBG_TRACE_DATA,
  439. DATASRC_MEM_SUPER_STOP).arg(name_labels);
  440. ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data, node_path,
  441. options);
  442. return (FindNodeResult(ZoneFinder::NXRRSET, nsec_rrset.first,
  443. nsec_rrset.second));
  444. }
  445. // Nothing really matched.
  446. // May be a wildcard, but check only if not disabled
  447. if (node->getFlag(ZoneData::WILDCARD_NODE) &&
  448. (options & ZoneFinder::NO_WILDCARD) == 0) {
  449. if (node_path.getLastComparisonResult().getRelation() ==
  450. NameComparisonResult::COMMONANCESTOR) {
  451. // This means, e.g., we have *.wild.example and
  452. // bar.foo.wild.example and are looking for
  453. // baz.foo.wild.example. The common ancestor, foo.wild.example,
  454. // should cancel wildcard. Treat it as NXDOMAIN.
  455. LOG_DEBUG(logger, DBG_TRACE_DATA,
  456. DATASRC_MEM_WILDCARD_CANCEL).arg(name_labels);
  457. ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data,
  458. node_path,
  459. options);
  460. return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_rrset.first,
  461. nsec_rrset.second));
  462. }
  463. uint8_t ls_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
  464. // Create the wildcard name (i.e. take "*" and extend it
  465. // with all node labels down to the wildcard node
  466. LabelSequence wildcard_ls(LabelSequence::WILDCARD(), ls_buf);
  467. const ZoneNode* extend_with = node;
  468. while (extend_with != NULL) {
  469. wildcard_ls.extend(extend_with->getLabels(), ls_buf);
  470. extend_with = extend_with->getUpperNode();
  471. }
  472. // Clear the node_path so that we don't keep incorrect (NSEC)
  473. // context
  474. node_path.clear();
  475. ZoneTree::Result result = tree.find(wildcard_ls, &node, node_path,
  476. cutCallback, &state);
  477. // Otherwise, why would the domain_flag::WILD be there if
  478. // there was no wildcard under it?
  479. assert(result == ZoneTree::EXACTMATCH);
  480. return (FindNodeResult(ZoneFinder::SUCCESS, node, state.rdataset_,
  481. FindNodeResult::FIND_WILDCARD | zonecut_flag));
  482. }
  483. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
  484. arg(name_labels);
  485. ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data, node_path,
  486. options);
  487. return (FindNodeResult(ZoneFinder::NXDOMAIN, nsec_rrset.first,
  488. nsec_rrset.second));
  489. } else {
  490. // If the name is neither an exact or partial match, it is
  491. // out of bailiwick, which is considered an error, unless the caller
  492. // is willing to accept it.
  493. if (out_of_zone_ok) {
  494. return (FindNodeResult(ZoneFinder::NXDOMAIN, NULL, NULL));
  495. }
  496. isc_throw(OutOfZone, name_labels << " not in " <<
  497. zone_data.getOriginNode()->getName());
  498. }
  499. }
  500. } // end anonymous namespace
  501. /// \brief Specialization of the ZoneFinder::Context for the in-memory finder.
  502. ///
  503. /// Note that we don't have a specific constructor for the findAll() case.
  504. /// For (successful) type ANY query, found_node points to the
  505. /// corresponding zone node, which is recorded within this specialized
  506. /// context.
  507. class InMemoryZoneFinder::Context : public ZoneFinder::Context {
  508. public:
  509. Context(InMemoryZoneFinder& finder, ZoneFinder::FindOptions options,
  510. const RRClass& rrclass, const ZoneFinderResultContext& result) :
  511. ZoneFinder::Context(options, ResultContext(result.code, result.rrset,
  512. result.flags)),
  513. finder_(finder), // NOTE: when entire #2283 is done we won't need this
  514. rrclass_(rrclass), zone_data_(result.zone_data),
  515. found_node_(result.found_node),
  516. found_rdset_(result.found_rdset)
  517. {}
  518. protected:
  519. // When all tickets in #2283 are done this can simply return NULL.
  520. virtual ZoneFinder* getFinder() { return (&finder_); }
  521. // We don't use the default protected methods that rely on this method,
  522. // so we can simply return NULL.
  523. virtual const std::vector<isc::dns::ConstRRsetPtr>* getAllRRsets() const {
  524. return (NULL);
  525. }
  526. virtual void getAdditionalImpl(const std::vector<RRType>& requested_types,
  527. std::vector<ConstRRsetPtr>& result)
  528. {
  529. if (found_rdset_ != NULL) {
  530. // Normal query with successful result.
  531. getAdditionalForRdataset(found_rdset_, requested_types, result,
  532. options_);
  533. } else if (found_node_ != NULL) {
  534. // Successful type ANY query result. Call
  535. // getAdditionalForRdataset for each RdataSet of the node.
  536. for (const RdataSet* rdset = found_node_->getData();
  537. rdset != NULL;
  538. rdset = rdset->getNext())
  539. {
  540. getAdditionalForRdataset(rdset, requested_types, result,
  541. options_);
  542. }
  543. }
  544. }
  545. private:
  546. // Main subroutine of getAdditionalImpl, iterate over Rdata fields
  547. // find, create, and insert necessary additional RRsets.
  548. void
  549. getAdditionalForRdataset(const RdataSet* rdset,
  550. const std::vector<RRType>& requested_types,
  551. std::vector<ConstRRsetPtr>& result,
  552. ZoneFinder::FindOptions orig_options) const
  553. {
  554. ZoneFinder::FindOptions options = ZoneFinder::FIND_DEFAULT;
  555. if ((orig_options & ZoneFinder::FIND_DNSSEC) != 0) {
  556. options = options | ZoneFinder::FIND_DNSSEC;
  557. }
  558. if (rdset->type == RRType::NS()) {
  559. options = options | ZoneFinder::FIND_GLUE_OK;
  560. }
  561. RdataReader(rrclass_, rdset->type, rdset->getDataBuf(),
  562. rdset->getRdataCount(), rdset->getSigRdataCount(),
  563. boost::bind(&Context::findAdditional, this,
  564. &requested_types, &result, options, _1, _2),
  565. &RdataReader::emptyDataAction).iterate();
  566. }
  567. // RdataReader callback for additional section processing.
  568. void
  569. findAdditional(const std::vector<RRType>* requested_types,
  570. std::vector<ConstRRsetPtr>* result,
  571. ZoneFinder::FindOptions options,
  572. const LabelSequence& name_labels,
  573. RdataNameAttributes attr) const;
  574. // Subroutine for findAdditional() to unify the normal and wildcard match
  575. // cases.
  576. void
  577. findAdditionalHelper(const std::vector<RRType>* requested_types,
  578. std::vector<ConstRRsetPtr>* result,
  579. const ZoneNode* node,
  580. ZoneFinder::FindOptions options,
  581. const Name* real_name) const
  582. {
  583. const std::vector<RRType>::const_iterator type_beg =
  584. requested_types->begin();
  585. const std::vector<RRType>::const_iterator type_end =
  586. requested_types->end();
  587. for (const RdataSet* rdset = node->getData();
  588. rdset != NULL;
  589. rdset = rdset->getNext())
  590. {
  591. // Checking all types for all RdataSets could be suboptimal.
  592. // This can be a bit more optimized, but unless we have many
  593. // requested types the effect is probably marginal. For now we
  594. // keep it simple.
  595. // Check for getRdataCount is necessary not to include RRSIG-only
  596. // records accidentally (should be rare, but possible).
  597. if (std::find(type_beg, type_end, rdset->type) != type_end &&
  598. rdset->getRdataCount() > 0) {
  599. result->push_back(createTreeNodeRRset(node, rdset, rrclass_,
  600. options, real_name));
  601. }
  602. }
  603. }
  604. private:
  605. InMemoryZoneFinder& finder_;
  606. const RRClass rrclass_;
  607. const ZoneData* const zone_data_;
  608. const ZoneNode* const found_node_;
  609. const RdataSet* const found_rdset_;
  610. };
  611. void
  612. InMemoryZoneFinder::Context::findAdditional(
  613. const std::vector<RRType>* requested_types,
  614. std::vector<ConstRRsetPtr>* result,
  615. ZoneFinder::FindOptions options,
  616. const LabelSequence& name_labels,
  617. RdataNameAttributes attr) const
  618. {
  619. // Ignore name data that don't need additional processing.
  620. if ((attr & NAMEATTR_ADDITIONAL) == 0) {
  621. return;
  622. }
  623. // Find the zone node for the additional name. By passing true as the
  624. // last parameter of findNode() we ignore out-of-zone names.
  625. ZoneChain node_path;
  626. const FindNodeResult node_result =
  627. findNode(*zone_data_, name_labels, node_path, options, true);
  628. // we only need non-empty exact match
  629. if (node_result.code != SUCCESS) {
  630. return;
  631. }
  632. // Ignore data at a zone cut (due to subdomain delegation) unless glue is
  633. // allowed. Checking the node callback flag is a cheap way to detect
  634. // zone cuts, but it includes DNAME delegation, in which case we should
  635. // keep finding the additional records regardless of the 'GLUE_OK' flag.
  636. // The last two conditions limit the case to delegation NS, i.e, the node
  637. // has an NS and it's not the zone origin.
  638. const ZoneNode* node = node_result.node;
  639. if ((options & ZoneFinder::FIND_GLUE_OK) == 0 &&
  640. node->getFlag(ZoneNode::FLAG_CALLBACK) &&
  641. node != zone_data_->getOriginNode() &&
  642. RdataSet::find(node->getData(), RRType::NS()) != NULL) {
  643. return;
  644. }
  645. // Examine RdataSets of the node, and create and insert requested types
  646. // of RRsets as we find them.
  647. if ((node_result.flags & FindNodeResult::FIND_WILDCARD) == 0) {
  648. // normal case
  649. findAdditionalHelper(requested_types, result, node, options, NULL);
  650. } else {
  651. // if the additional name is subject to wildcard substitution, we need
  652. // to create a name object for the "real" (after substitution) name.
  653. // This is expensive, but in the additional processing this should be
  654. // very rare cases and acceptable.
  655. size_t data_len;
  656. const uint8_t* data;
  657. data = name_labels.getData(&data_len);
  658. util::InputBuffer buffer(data, data_len);
  659. const Name real_name(buffer);
  660. findAdditionalHelper(requested_types, result, node, options,
  661. &real_name);
  662. }
  663. }
  664. boost::shared_ptr<ZoneFinder::Context>
  665. InMemoryZoneFinder::find(const isc::dns::Name& name,
  666. const isc::dns::RRType& type,
  667. const FindOptions options)
  668. {
  669. return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
  670. findInternal(name, type,
  671. NULL, options))));
  672. }
  673. boost::shared_ptr<ZoneFinder::Context>
  674. InMemoryZoneFinder::findAll(const isc::dns::Name& name,
  675. std::vector<isc::dns::ConstRRsetPtr>& target,
  676. const FindOptions options)
  677. {
  678. return (ZoneFinderContextPtr(new Context(*this, options, rrclass_,
  679. findInternal(name,
  680. RRType::ANY(),
  681. &target,
  682. options))));
  683. }
  684. boost::shared_ptr<ZoneFinder::Context>
  685. InMemoryZoneFinder::findAtOrigin(const isc::dns::RRType& type,
  686. bool /*use_minttl*/, // not yet supported
  687. const FindOptions options)
  688. {
  689. const ZoneNode* const node = zone_data_.getOriginNode();
  690. const RdataSet* const found = RdataSet::find(node->getData(), type);
  691. if (found != NULL) {
  692. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_TYPE_AT_ORIGIN).
  693. arg(type).arg(getOrigin()).arg(rrclass_);
  694. return (ZoneFinderContextPtr(
  695. new Context(*this, options, rrclass_,
  696. createFindResult(rrclass_, zone_data_, SUCCESS,
  697. node, found, options))));
  698. }
  699. return (ZoneFinderContextPtr(
  700. new Context(*this, options, rrclass_,
  701. createFindResult(rrclass_, zone_data_, NXRRSET,
  702. node,
  703. getNSECForNXRRSET(zone_data_,
  704. options,
  705. node),
  706. options))));
  707. }
  708. ZoneFinderResultContext
  709. InMemoryZoneFinder::findInternal(const isc::dns::Name& name,
  710. const isc::dns::RRType& type,
  711. std::vector<ConstRRsetPtr>* target,
  712. const FindOptions options)
  713. {
  714. // Get the node. All other cases than an exact match are handled
  715. // in findNode(). We simply construct a result structure and return.
  716. ZoneChain node_path;
  717. const FindNodeResult node_result =
  718. findNode(zone_data_, LabelSequence(name), node_path, options);
  719. if (node_result.code != SUCCESS) {
  720. return (createFindResult(rrclass_, zone_data_, node_result.code,
  721. node_result.node, node_result.rdataset,
  722. options));
  723. }
  724. const ZoneNode* node = node_result.node;
  725. assert(node != NULL);
  726. // We've found an exact match, may or may not be a result of wildcard.
  727. const bool wild = ((node_result.flags &
  728. FindNodeResult::FIND_WILDCARD) != 0);
  729. // If there is an exact match but the node is empty, it's equivalent
  730. // to NXRRSET.
  731. if (node->isEmpty()) {
  732. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
  733. arg(name);
  734. ConstNodeRRset nsec_rrset = getClosestNSEC(zone_data_, node_path,
  735. options);
  736. return (createFindResult(rrclass_, zone_data_, NXRRSET,
  737. nsec_rrset.first, nsec_rrset.second,
  738. options, wild));
  739. }
  740. const RdataSet* found;
  741. // If the node callback is enabled, this may be a zone cut. If it
  742. // has a NS RR, we should return a delegation, but not in the apex.
  743. // There are two exceptions:
  744. // - the case for DS query, which should always be considered in-zone
  745. // lookup.
  746. // - when we are looking for glue records (FIND_GLUE_OK)
  747. if (node->getFlag(ZoneNode::FLAG_CALLBACK) &&
  748. (options & FIND_GLUE_OK) == 0 &&
  749. node != zone_data_.getOriginNode() && type != RRType::DS()) {
  750. found = RdataSet::find(node->getData(), RRType::NS());
  751. if (found != NULL) {
  752. LOG_DEBUG(logger, DBG_TRACE_DATA,
  753. DATASRC_MEM_EXACT_DELEGATION).arg(name);
  754. return (createFindResult(rrclass_, zone_data_, DELEGATION,
  755. node, found, options, wild, &name));
  756. }
  757. }
  758. // Handle type any query
  759. if (target != NULL && node->getData() != NULL) {
  760. // Empty domain will be handled as NXRRSET by normal processing
  761. const RdataSet* cur_rds = node->getData();
  762. while (cur_rds != NULL) {
  763. target->push_back(createTreeNodeRRset(node, cur_rds, rrclass_,
  764. options, &name));
  765. cur_rds = cur_rds->getNext();
  766. }
  767. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
  768. arg(name);
  769. return (createFindResult(rrclass_, zone_data_, SUCCESS, node, NULL,
  770. options, wild, &name));
  771. }
  772. found = RdataSet::find(node->getData(), type);
  773. if (found != NULL) {
  774. // Good, it is here
  775. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
  776. arg(type);
  777. return (createFindResult(rrclass_, zone_data_, SUCCESS, node, found,
  778. options, wild, &name));
  779. } else {
  780. // Next, try CNAME.
  781. found = RdataSet::find(node->getData(), RRType::CNAME());
  782. if (found != NULL) {
  783. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
  784. return (createFindResult(rrclass_, zone_data_, CNAME, node, found,
  785. options, wild, &name));
  786. }
  787. }
  788. // No exact match or CNAME. Get NSEC if necessary and return NXRRSET.
  789. return (createFindResult(rrclass_, zone_data_, NXRRSET, node,
  790. getNSECForNXRRSET(zone_data_, options, node),
  791. options, wild, &name));
  792. }
  793. isc::datasrc::ZoneFinder::FindNSEC3Result
  794. InMemoryZoneFinder::findNSEC3(const isc::dns::Name& name, bool recursive) {
  795. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3).arg(name).
  796. arg(recursive ? "recursive" : "non-recursive");
  797. uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
  798. const LabelSequence origin_ls(zone_data_.getOriginNode()->
  799. getAbsoluteLabels(labels_buf));
  800. const LabelSequence name_ls(name);
  801. if (!zone_data_.isNSEC3Signed()) {
  802. isc_throw(DataSourceError,
  803. "findNSEC3 attempt for non NSEC3 signed zone: " <<
  804. origin_ls << "/" << getClass());
  805. }
  806. const NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
  807. // This would be a programming mistake, as ZoneData::isNSEC3Signed()
  808. // should check this.
  809. assert(nsec3_data != NULL);
  810. const ZoneTree& tree = nsec3_data->getNSEC3Tree();
  811. if (tree.getNodeCount() == 0) {
  812. isc_throw(DataSourceError,
  813. "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
  814. origin_ls << "/" << getClass());
  815. }
  816. const NameComparisonResult cmp_result = name_ls.compare(origin_ls);
  817. if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
  818. cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
  819. isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: "
  820. << name_ls << ", zone: " << origin_ls << "/"
  821. << getClass());
  822. }
  823. // Convenient shortcuts
  824. const unsigned int olabels = origin_ls.getLabelCount();
  825. const unsigned int qlabels = name.getLabelCount();
  826. // placeholder of the next closer proof
  827. const ZoneNode* covering_node(NULL);
  828. // Now we'll first look up the origin node and initialize orig_chain
  829. // with it.
  830. ZoneChain orig_chain;
  831. const ZoneNode* node(NULL);
  832. ZoneTree::Result result =
  833. tree.find<void*>(origin_ls, &node, orig_chain, NULL, NULL);
  834. if (result != ZoneTree::EXACTMATCH) {
  835. // If the origin node doesn't exist, simply fail.
  836. isc_throw(DataSourceError,
  837. "findNSEC3 attempt but zone has no NSEC3 RRs: " <<
  838. origin_ls << "/" << getClass());
  839. }
  840. const boost::scoped_ptr<NSEC3Hash> hash
  841. (NSEC3Hash::create(nsec3_data->hashalg,
  842. nsec3_data->iterations,
  843. nsec3_data->getSaltData(),
  844. nsec3_data->getSaltLen()));
  845. // Examine all names from the query name to the origin name, stripping
  846. // the deepest label one by one, until we find a name that has a matching
  847. // NSEC3 hash.
  848. for (unsigned int labels = qlabels; labels >= olabels; --labels) {
  849. const Name& hname = (labels == qlabels ?
  850. name : name.split(qlabels - labels, labels));
  851. const std::string hlabel = hash->calculate(hname);
  852. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FINDNSEC3_TRYHASH).
  853. arg(name).arg(labels).arg(hlabel);
  854. node = NULL;
  855. ZoneChain chain(orig_chain);
  856. // Now, make a label sequence relative to the origin.
  857. const Name hlabel_name(hlabel);
  858. LabelSequence hlabel_ls(hlabel_name);
  859. // Remove trailing '.' making it relative
  860. hlabel_ls.stripRight(1);
  861. // Find hlabel relative to the orig_chain.
  862. result = tree.find<void*>(hlabel_ls, &node, chain, NULL, NULL);
  863. if (result == ZoneTree::EXACTMATCH) {
  864. // We found an exact match.
  865. ConstRRsetPtr closest = createNSEC3RRset(node, getClass());
  866. ConstRRsetPtr next;
  867. if (covering_node != NULL) {
  868. next = createNSEC3RRset(covering_node, getClass());
  869. }
  870. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  871. DATASRC_MEM_FINDNSEC3_MATCH).arg(name).arg(labels).
  872. arg(*closest);
  873. return (FindNSEC3Result(true, labels, closest, next));
  874. } else {
  875. while ((covering_node = tree.previousNode(chain)) != NULL &&
  876. covering_node->isEmpty()) {
  877. ;
  878. }
  879. if (covering_node == NULL) {
  880. covering_node = tree.largestNode();
  881. }
  882. if (!recursive) { // in non recursive mode, we are done.
  883. ConstRRsetPtr closest;
  884. if (covering_node != NULL) {
  885. closest = createNSEC3RRset(covering_node, getClass());
  886. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  887. DATASRC_MEM_FINDNSEC3_COVER).
  888. arg(name).arg(*closest);
  889. }
  890. return (FindNSEC3Result(false, labels,
  891. closest, ConstRRsetPtr()));
  892. }
  893. }
  894. }
  895. isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely "
  896. "a broken NSEC3 zone: " << getOrigin() << "/"
  897. << getClass());
  898. }
  899. Name
  900. InMemoryZoneFinder::getOrigin() const {
  901. // In future we may allow adding out-of-zone names in the zone tree.
  902. // For example, to hold out-of-zone NS names so we can establish a
  903. // shortcut link to them as an optimization. If and when that happens
  904. // the origin node may not have an absolute label (consider the zone
  905. // is example.org and we add ns.noexample.org). Even in such cases,
  906. // DomainTreeNode::getAbsoluteLabels() returns the correct absolute
  907. // label sequence.
  908. uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH];
  909. const LabelSequence name_labels =
  910. zone_data_.getOriginNode()->getAbsoluteLabels(labels_buf);
  911. size_t data_len;
  912. const uint8_t* data = name_labels.getData(&data_len);
  913. util::InputBuffer buffer(data, data_len);
  914. return (Name(buffer));
  915. }
  916. } // namespace memory
  917. } // namespace datasrc
  918. } // namespace isc