memory_datasrc.cc 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  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 <map>
  15. #include <cassert>
  16. #include <boost/shared_ptr.hpp>
  17. #include <boost/bind.hpp>
  18. #include <boost/foreach.hpp>
  19. #include <exceptions/exceptions.h>
  20. #include <dns/name.h>
  21. #include <dns/rrclass.h>
  22. #include <dns/rrsetlist.h>
  23. #include <dns/masterload.h>
  24. #include <datasrc/memory_datasrc.h>
  25. #include <datasrc/rbtree.h>
  26. #include <datasrc/logger.h>
  27. #include <datasrc/iterator.h>
  28. #include <datasrc/data_source.h>
  29. #include <datasrc/factory.h>
  30. #include <cc/data.h>
  31. using namespace std;
  32. using namespace isc::dns;
  33. using namespace isc::data;
  34. namespace isc {
  35. namespace datasrc {
  36. namespace {
  37. // Some type aliases
  38. /*
  39. * Each domain consists of some RRsets. They will be looked up by the
  40. * RRType.
  41. *
  42. * The use of map is questionable with regard to performance - there'll
  43. * be usually only few RRsets in the domain, so the log n benefit isn't
  44. * much and a vector/array might be faster due to its simplicity and
  45. * continuous memory location. But this is unlikely to be a performance
  46. * critical place and map has better interface for the lookups, so we use
  47. * that.
  48. */
  49. typedef map<RRType, ConstRRsetPtr> Domain;
  50. typedef Domain::value_type DomainPair;
  51. typedef boost::shared_ptr<Domain> DomainPtr;
  52. // The tree stores domains
  53. typedef RBTree<Domain> DomainTree;
  54. typedef RBNode<Domain> DomainNode;
  55. }
  56. // Private data and hidden methods of InMemoryZoneFinder
  57. struct InMemoryZoneFinder::InMemoryZoneFinderImpl {
  58. // Constructor
  59. InMemoryZoneFinderImpl(const RRClass& zone_class, const Name& origin) :
  60. zone_class_(zone_class), origin_(origin), origin_data_(NULL),
  61. domains_(true)
  62. {
  63. // We create the node for origin (it needs to exist anyway in future)
  64. domains_.insert(origin, &origin_data_);
  65. DomainPtr origin_domain(new Domain);
  66. origin_data_->setData(origin_domain);
  67. }
  68. static const DomainNode::Flags DOMAINFLAG_WILD = DomainNode::FLAG_USER1;
  69. // Information about the zone
  70. RRClass zone_class_;
  71. Name origin_;
  72. DomainNode* origin_data_;
  73. string file_name_;
  74. // The actual zone data
  75. DomainTree domains_;
  76. // Add the necessary magic for any wildcard contained in 'name'
  77. // (including itself) to be found in the zone.
  78. //
  79. // In order for wildcard matching to work correctly in find(),
  80. // we must ensure that a node for the wildcarding level exists in the
  81. // backend RBTree.
  82. // E.g. if the wildcard name is "*.sub.example." then we must ensure
  83. // that "sub.example." exists and is marked as a wildcard level.
  84. // Note: the "wildcarding level" is for the parent name of the wildcard
  85. // name (such as "sub.example.").
  86. //
  87. // We also perform the same trick for empty wild card names possibly
  88. // contained in 'name' (e.g., '*.foo.example' in 'bar.*.foo.example').
  89. void addWildcards(DomainTree& domains, const Name& name) {
  90. Name wname(name);
  91. const unsigned int labels(wname.getLabelCount());
  92. const unsigned int origin_labels(origin_.getLabelCount());
  93. for (unsigned int l = labels;
  94. l > origin_labels;
  95. --l, wname = wname.split(1)) {
  96. if (wname.isWildcard()) {
  97. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_WILDCARD).
  98. arg(name);
  99. // Ensure a separate level exists for the "wildcarding" name,
  100. // and mark the node as "wild".
  101. DomainNode* node;
  102. DomainTree::Result result(domains.insert(wname.split(1),
  103. &node));
  104. assert(result == DomainTree::SUCCESS ||
  105. result == DomainTree::ALREADYEXISTS);
  106. node->setFlag(DOMAINFLAG_WILD);
  107. // Ensure a separate level exists for the wildcard name.
  108. // Note: for 'name' itself we do this later anyway, but the
  109. // overhead should be marginal because wildcard names should
  110. // be rare.
  111. result = domains.insert(wname, &node);
  112. assert(result == DomainTree::SUCCESS ||
  113. result == DomainTree::ALREADYEXISTS);
  114. }
  115. }
  116. }
  117. /*
  118. * Does some checks in context of the data that are already in the zone.
  119. * Currently checks for forbidden combinations of RRsets in the same
  120. * domain (CNAME+anything, DNAME+NS).
  121. *
  122. * If such condition is found, it throws AddError.
  123. */
  124. void contextCheck(const ConstRRsetPtr& rrset,
  125. const DomainPtr& domain) const {
  126. // Ensure CNAME and other type of RR don't coexist for the same
  127. // owner name.
  128. if (rrset->getType() == RRType::CNAME()) {
  129. // TODO: this check will become incorrect when we support DNSSEC
  130. // (depending on how we support DNSSEC). We should revisit it
  131. // at that point.
  132. if (!domain->empty()) {
  133. LOG_ERROR(logger, DATASRC_MEM_CNAME_TO_NONEMPTY).
  134. arg(rrset->getName());
  135. isc_throw(AddError, "CNAME can't be added with other data for "
  136. << rrset->getName());
  137. }
  138. } else if (domain->find(RRType::CNAME()) != domain->end()) {
  139. LOG_ERROR(logger, DATASRC_MEM_CNAME_COEXIST).arg(rrset->getName());
  140. isc_throw(AddError, "CNAME and " << rrset->getType() <<
  141. " can't coexist for " << rrset->getName());
  142. }
  143. /*
  144. * Similar with DNAME, but it must not coexist only with NS and only in
  145. * non-apex domains.
  146. * RFC 2672 section 3 mentions that it is implied from it and RFC 2181
  147. */
  148. if (rrset->getName() != origin_ &&
  149. // Adding DNAME, NS already there
  150. ((rrset->getType() == RRType::DNAME() &&
  151. domain->find(RRType::NS()) != domain->end()) ||
  152. // Adding NS, DNAME already there
  153. (rrset->getType() == RRType::NS() &&
  154. domain->find(RRType::DNAME()) != domain->end())))
  155. {
  156. LOG_ERROR(logger, DATASRC_MEM_DNAME_NS).arg(rrset->getName());
  157. isc_throw(AddError, "DNAME can't coexist with NS in non-apex "
  158. "domain " << rrset->getName());
  159. }
  160. }
  161. // Validate rrset before adding it to the zone. If something is wrong
  162. // it throws an exception. It doesn't modify the zone, and provides
  163. // the strong exception guarantee.
  164. void addValidation(const ConstRRsetPtr rrset) {
  165. if (!rrset) {
  166. isc_throw(NullRRset, "The rrset provided is NULL");
  167. }
  168. // Check for singleton RRs. It should probably handled at a different
  169. // in future.
  170. if ((rrset->getType() == RRType::CNAME() ||
  171. rrset->getType() == RRType::DNAME()) &&
  172. rrset->getRdataCount() > 1)
  173. {
  174. // XXX: this is not only for CNAME or DNAME. We should generalize
  175. // this code for all other "singleton RR types" (such as SOA) in a
  176. // separate task.
  177. LOG_ERROR(logger, DATASRC_MEM_SINGLETON).arg(rrset->getName()).
  178. arg(rrset->getType());
  179. isc_throw(AddError, "multiple RRs of singleton type for "
  180. << rrset->getName());
  181. }
  182. NameComparisonResult compare(origin_.compare(rrset->getName()));
  183. if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
  184. compare.getRelation() != NameComparisonResult::EQUAL)
  185. {
  186. LOG_ERROR(logger, DATASRC_MEM_OUT_OF_ZONE).arg(rrset->getName()).
  187. arg(origin_);
  188. isc_throw(OutOfZone, "The name " << rrset->getName() <<
  189. " is not contained in zone " << origin_);
  190. }
  191. // Some RR types do not really work well with a wildcard.
  192. // Even though the protocol specifically doesn't completely ban such
  193. // usage, we refuse to load a zone containing such RR in order to
  194. // keep the lookup logic simpler and more predictable.
  195. // See RFC4592 and (for DNAME) draft-ietf-dnsext-rfc2672bis-dname
  196. // for more technical background. Note also that BIND 9 refuses
  197. // NS at a wildcard, so in that sense we simply provide compatible
  198. // behavior.
  199. if (rrset->getName().isWildcard()) {
  200. if (rrset->getType() == RRType::NS()) {
  201. LOG_ERROR(logger, DATASRC_MEM_WILDCARD_NS).
  202. arg(rrset->getName());
  203. isc_throw(AddError, "Invalid NS owner name (wildcard): " <<
  204. rrset->getName());
  205. }
  206. if (rrset->getType() == RRType::DNAME()) {
  207. LOG_ERROR(logger, DATASRC_MEM_WILDCARD_DNAME).
  208. arg(rrset->getName());
  209. isc_throw(AddError, "Invalid DNAME owner name (wildcard): " <<
  210. rrset->getName());
  211. }
  212. }
  213. }
  214. /*
  215. * Implementation of longer methods. We put them here, because the
  216. * access is without the impl_-> and it will get inlined anyway.
  217. */
  218. // Implementation of InMemoryZoneFinder::add
  219. result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
  220. // Sanitize input. This will cause an exception to be thrown
  221. // if the input RRset is empty.
  222. addValidation(rrset);
  223. // OK, can add the RRset.
  224. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ADD_RRSET).
  225. arg(rrset->getName()).arg(rrset->getType()).arg(origin_);
  226. // Add wildcards possibly contained in the owner name to the domain
  227. // tree.
  228. // Note: this can throw an exception, breaking strong exception
  229. // guarantee. (see also the note for contextCheck() below).
  230. addWildcards(*domains, rrset->getName());
  231. // Get the node
  232. DomainNode* node;
  233. DomainTree::Result result = domains->insert(rrset->getName(), &node);
  234. // Just check it returns reasonable results
  235. assert((result == DomainTree::SUCCESS ||
  236. result == DomainTree::ALREADYEXISTS) && node!= NULL);
  237. // Now get the domain
  238. DomainPtr domain;
  239. // It didn't exist yet, create it
  240. if (node->isEmpty()) {
  241. domain.reset(new Domain);
  242. node->setData(domain);
  243. } else { // Get existing one
  244. domain = node->getData();
  245. }
  246. // Checks related to the surrounding data.
  247. // Note: when the check fails and the exception is thrown, it may
  248. // break strong exception guarantee. At the moment we prefer
  249. // code simplicity and don't bother to introduce complicated
  250. // recovery code.
  251. contextCheck(rrset, domain);
  252. // Try inserting the rrset there
  253. if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
  254. // Ok, we just put it in
  255. // If this RRset creates a zone cut at this node, mark the node
  256. // indicating the need for callback in find().
  257. if (rrset->getType() == RRType::NS() &&
  258. rrset->getName() != origin_) {
  259. node->setFlag(DomainNode::FLAG_CALLBACK);
  260. // If it is DNAME, we have a callback as well here
  261. } else if (rrset->getType() == RRType::DNAME()) {
  262. node->setFlag(DomainNode::FLAG_CALLBACK);
  263. }
  264. return (result::SUCCESS);
  265. } else {
  266. // The RRSet of given type was already there
  267. return (result::EXIST);
  268. }
  269. }
  270. /*
  271. * Same as above, but it checks the return value and if it already exists,
  272. * it throws.
  273. */
  274. void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
  275. switch (add(set, domains)) {
  276. case result::EXIST:
  277. LOG_ERROR(logger, DATASRC_MEM_DUP_RRSET).
  278. arg(set->getName()).arg(set->getType());
  279. isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
  280. set->toText());
  281. case result::SUCCESS:
  282. return;
  283. default:
  284. assert(0);
  285. }
  286. }
  287. // Maintain intermediate data specific to the search context used in
  288. /// \c find().
  289. ///
  290. /// It will be passed to \c zonecutCallback() and record a possible
  291. /// zone cut node and related RRset (normally NS or DNAME).
  292. struct FindState {
  293. FindState(FindOptions options) :
  294. zonecut_node_(NULL),
  295. dname_node_(NULL),
  296. options_(options)
  297. {}
  298. const DomainNode* zonecut_node_;
  299. const DomainNode* dname_node_;
  300. ConstRRsetPtr rrset_;
  301. const FindOptions options_;
  302. };
  303. // A callback called from possible zone cut nodes and nodes with DNAME.
  304. // This will be passed from the \c find() method to \c RBTree::find().
  305. static bool cutCallback(const DomainNode& node, FindState* state) {
  306. // We need to look for DNAME first, there's allowed case where
  307. // DNAME and NS coexist in the apex. DNAME is the one to notice,
  308. // the NS is authoritative, not delegation (corner case explicitly
  309. // allowed by section 3 of 2672)
  310. const Domain::const_iterator foundDNAME(node.getData()->find(
  311. RRType::DNAME()));
  312. if (foundDNAME != node.getData()->end()) {
  313. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  314. DATASRC_MEM_DNAME_ENCOUNTERED);
  315. state->dname_node_ = &node;
  316. state->rrset_ = foundDNAME->second;
  317. // No more processing below the DNAME (RFC 2672, section 3
  318. // forbids anything to exist below it, so there's no need
  319. // to actually search for it). This is strictly speaking
  320. // a different way than described in 4.1 of that RFC,
  321. // but because of the assumption in section 3, it has the
  322. // same behaviour.
  323. return (true);
  324. }
  325. // Look for NS
  326. const Domain::const_iterator foundNS(node.getData()->find(
  327. RRType::NS()));
  328. if (foundNS != node.getData()->end()) {
  329. // We perform callback check only for the highest zone cut in the
  330. // rare case of nested zone cuts.
  331. if (state->zonecut_node_ != NULL) {
  332. return (false);
  333. }
  334. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_NS_ENCOUNTERED);
  335. // BIND 9 checks if this node is not the origin. That's probably
  336. // because it can support multiple versions for dynamic updates
  337. // and IXFR, and it's possible that the callback is called at
  338. // the apex and the DNAME doesn't exist for a particular version.
  339. // It cannot happen for us (at least for now), so we don't do
  340. // that check.
  341. state->zonecut_node_ = &node;
  342. state->rrset_ = foundNS->second;
  343. // Unless glue is allowed the search stops here, so we return
  344. // false; otherwise return true to continue the search.
  345. return ((state->options_ & FIND_GLUE_OK) == 0);
  346. }
  347. // This case should not happen because we enable callback only
  348. // when we add an RR searched for above.
  349. assert(0);
  350. // This is here to avoid warning (therefore compilation error)
  351. // in case assert is turned off. Otherwise we could get "Control
  352. // reached end of non-void function".
  353. return (false);
  354. }
  355. /*
  356. * Prepares a rrset to be return as a result.
  357. *
  358. * If rename is false, it returns the one provided. If it is true, it
  359. * creates a new rrset with the same data but with provided name.
  360. * It is designed for wildcard case, where we create the rrsets
  361. * dynamically.
  362. */
  363. static ConstRRsetPtr prepareRRset(const Name& name, const ConstRRsetPtr&
  364. rrset, bool rename)
  365. {
  366. if (rename) {
  367. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_MEM_RENAME).
  368. arg(rrset->getName()).arg(name);
  369. /*
  370. * We lose a signature here. But it would be wrong anyway, because
  371. * the name changed. This might turn out to be unimportant in
  372. * future, because wildcards will probably be handled somehow
  373. * by DNSSEC.
  374. */
  375. RRsetPtr result(new RRset(name, rrset->getClass(),
  376. rrset->getType(), rrset->getTTL()));
  377. for (RdataIteratorPtr i(rrset->getRdataIterator()); !i->isLast();
  378. i->next()) {
  379. result->addRdata(i->getCurrent());
  380. }
  381. return (result);
  382. } else {
  383. return (rrset);
  384. }
  385. }
  386. // Implementation of InMemoryZoneFinder::find
  387. FindResult find(const Name& name, RRType type, const FindOptions options)
  388. const
  389. {
  390. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_FIND).arg(name).
  391. arg(type);
  392. // Get the node
  393. DomainNode* node(NULL);
  394. FindState state(options);
  395. RBTreeNodeChain<Domain> node_path;
  396. bool rename(false);
  397. switch (domains_.find(name, &node, node_path, cutCallback, &state)) {
  398. case DomainTree::PARTIALMATCH:
  399. /*
  400. * In fact, we could use a single variable instead of
  401. * dname_node_ and zonecut_node_. But then we would need
  402. * to distinquish these two cases by something else and
  403. * it seemed little more confusing to me when I wrote it.
  404. *
  405. * Usually at most one of them will be something else than
  406. * NULL (it might happen both are NULL, in which case we
  407. * consider it NOT FOUND). There's one corner case when
  408. * both might be something else than NULL and it is in case
  409. * there's a DNAME under a zone cut and we search in
  410. * glue OK mode ‒ in that case we don't stop on the domain
  411. * with NS and ignore it for the answer, but it gets set
  412. * anyway. Then we find the DNAME and we need to act by it,
  413. * therefore we first check for DNAME and then for NS. In
  414. * all other cases it doesn't matter, as at least one of them
  415. * is NULL.
  416. */
  417. if (state.dname_node_ != NULL) {
  418. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DNAME_FOUND).
  419. arg(state.rrset_->getName());
  420. // We were traversing a DNAME node (and wanted to go
  421. // lower below it), so return the DNAME
  422. return (FindResult(DNAME, prepareRRset(name, state.rrset_,
  423. rename)));
  424. }
  425. if (state.zonecut_node_ != NULL) {
  426. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DELEG_FOUND).
  427. arg(state.rrset_->getName());
  428. return (FindResult(DELEGATION, prepareRRset(name,
  429. state.rrset_, rename)));
  430. }
  431. // If the RBTree search stopped at a node for a super domain
  432. // of the search name, it means the search name exists in
  433. // the zone but is empty. Treat it as NXRRSET.
  434. if (node_path.getLastComparisonResult().getRelation() ==
  435. NameComparisonResult::SUPERDOMAIN) {
  436. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUPER_STOP).
  437. arg(node_path.getAbsoluteName()).arg(name);
  438. return (FindResult(NXRRSET, ConstRRsetPtr()));
  439. }
  440. /*
  441. * No redirection anywhere. Let's try if it is a wildcard.
  442. *
  443. * The wildcard is checked after the empty non-terminal domain
  444. * case above, because if that one triggers, it means we should
  445. * not match according to 4.3.3 of RFC 1034 (the query name
  446. * is known to exist).
  447. */
  448. if (node->getFlag(DOMAINFLAG_WILD)) {
  449. /* Should we cancel this match?
  450. *
  451. * If we compare with some node and get a common ancestor,
  452. * it might mean we are comparing with a non-wildcard node.
  453. * In that case, we check which part is common. If we have
  454. * something in common that lives below the node we got
  455. * (the one above *), then we should cancel the match
  456. * according to section 4.3.3 of RFC 1034 (as the name
  457. * between the wildcard domain and the query name is known
  458. * to exist).
  459. *
  460. * Because the way the tree stores relative names, we will
  461. * have exactly one common label (the ".") in case we have
  462. * nothing common under the node we got and we will get
  463. * more common labels otherwise (yes, this relies on the
  464. * internal RBTree structure, which leaks out through this
  465. * little bit).
  466. *
  467. * If the empty non-terminal node actually exists in the
  468. * tree, then this cancellation is not needed, because we
  469. * will not get here at all.
  470. */
  471. if (node_path.getLastComparisonResult().getRelation() ==
  472. NameComparisonResult::COMMONANCESTOR && node_path.
  473. getLastComparisonResult().getCommonLabels() > 1) {
  474. LOG_DEBUG(logger, DBG_TRACE_DATA,
  475. DATASRC_MEM_WILDCARD_CANCEL).arg(name);
  476. return (FindResult(NXDOMAIN, ConstRRsetPtr()));
  477. }
  478. Name wildcard(Name("*").concatenate(
  479. node_path.getAbsoluteName()));
  480. DomainTree::Result result(domains_.find(wildcard, &node));
  481. /*
  482. * Otherwise, why would the DOMAINFLAG_WILD be there if
  483. * there was no wildcard under it?
  484. */
  485. assert(result == DomainTree::EXACTMATCH);
  486. /*
  487. * We have the wildcard node now. Jump below the switch,
  488. * where handling of the common (exact-match) case is.
  489. *
  490. * However, rename it to the searched name.
  491. */
  492. rename = true;
  493. break;
  494. }
  495. // fall through
  496. case DomainTree::NOTFOUND:
  497. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NOT_FOUND).
  498. arg(name);
  499. return (FindResult(NXDOMAIN, ConstRRsetPtr()));
  500. case DomainTree::EXACTMATCH: // This one is OK, handle it
  501. break;
  502. default:
  503. assert(0);
  504. }
  505. assert(node != NULL);
  506. // If there is an exact match but the node is empty, it's equivalent
  507. // to NXRRSET.
  508. if (node->isEmpty()) {
  509. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_DOMAIN_EMPTY).
  510. arg(name);
  511. return (FindResult(NXRRSET, ConstRRsetPtr()));
  512. }
  513. Domain::const_iterator found;
  514. // If the node callback is enabled, this may be a zone cut. If it
  515. // has a NS RR, we should return a delegation, but not in the apex.
  516. if (node->getFlag(DomainNode::FLAG_CALLBACK) && node != origin_data_) {
  517. found = node->getData()->find(RRType::NS());
  518. if (found != node->getData()->end()) {
  519. LOG_DEBUG(logger, DBG_TRACE_DATA,
  520. DATASRC_MEM_EXACT_DELEGATION).arg(name);
  521. return (FindResult(DELEGATION, prepareRRset(name,
  522. found->second, rename)));
  523. }
  524. }
  525. #if 0
  526. TODO: Move this to some other place, new method
  527. // handle type any query
  528. if (target != NULL && !node->getData()->empty()) {
  529. // Empty domain will be handled as NXRRSET by normal processing
  530. for (found = node->getData()->begin();
  531. found != node->getData()->end(); ++found)
  532. {
  533. target->addRRset(
  534. boost::const_pointer_cast<RRset>(prepareRRset(name,
  535. found->second, rename)));
  536. }
  537. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_ANY_SUCCESS).
  538. arg(name);
  539. return (FindResult(SUCCESS, ConstRRsetPtr()));
  540. }
  541. #endif
  542. found = node->getData()->find(type);
  543. if (found != node->getData()->end()) {
  544. // Good, it is here
  545. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_SUCCESS).arg(name).
  546. arg(type);
  547. return (FindResult(SUCCESS, prepareRRset(name, found->second,
  548. rename)));
  549. } else {
  550. // Next, try CNAME.
  551. found = node->getData()->find(RRType::CNAME());
  552. if (found != node->getData()->end()) {
  553. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_CNAME).arg(name);
  554. return (FindResult(CNAME, prepareRRset(name, found->second,
  555. rename)));
  556. }
  557. }
  558. // No exact match or CNAME. Return NXRRSET.
  559. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_NXRRSET).arg(type).
  560. arg(name);
  561. return (FindResult(NXRRSET, ConstRRsetPtr()));
  562. }
  563. };
  564. InMemoryZoneFinder::InMemoryZoneFinder(const RRClass& zone_class, const Name& origin) :
  565. impl_(new InMemoryZoneFinderImpl(zone_class, origin))
  566. {
  567. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_CREATE).arg(origin).
  568. arg(zone_class);
  569. }
  570. InMemoryZoneFinder::~InMemoryZoneFinder() {
  571. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_DESTROY).arg(getOrigin()).
  572. arg(getClass());
  573. delete impl_;
  574. }
  575. Name
  576. InMemoryZoneFinder::getOrigin() const {
  577. return (impl_->origin_);
  578. }
  579. RRClass
  580. InMemoryZoneFinder::getClass() const {
  581. return (impl_->zone_class_);
  582. }
  583. ZoneFinder::FindResult
  584. InMemoryZoneFinder::find(const Name& name, const RRType& type,
  585. const FindOptions options)
  586. {
  587. return (impl_->find(name, type, options));
  588. }
  589. result::Result
  590. InMemoryZoneFinder::add(const ConstRRsetPtr& rrset) {
  591. return (impl_->add(rrset, &impl_->domains_));
  592. }
  593. void
  594. InMemoryZoneFinder::load(const string& filename) {
  595. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_LOAD).arg(getOrigin()).
  596. arg(filename);
  597. // Load it into a temporary tree
  598. DomainTree tmp;
  599. masterLoad(filename.c_str(), getOrigin(), getClass(),
  600. boost::bind(&InMemoryZoneFinderImpl::addFromLoad, impl_, _1, &tmp));
  601. // If it went well, put it inside
  602. impl_->file_name_ = filename;
  603. tmp.swap(impl_->domains_);
  604. // And let the old data die with tmp
  605. }
  606. void
  607. InMemoryZoneFinder::swap(InMemoryZoneFinder& zone_finder) {
  608. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_SWAP).arg(getOrigin()).
  609. arg(zone_finder.getOrigin());
  610. std::swap(impl_, zone_finder.impl_);
  611. }
  612. const string
  613. InMemoryZoneFinder::getFileName() const {
  614. return (impl_->file_name_);
  615. }
  616. isc::dns::Name
  617. InMemoryZoneFinder::findPreviousName(const isc::dns::Name&) const {
  618. isc_throw(NotImplemented, "InMemory data source doesn't support DNSSEC "
  619. "yet, can't find previous name");
  620. }
  621. /// Implementation details for \c InMemoryClient hidden from the public
  622. /// interface.
  623. ///
  624. /// For now, \c InMemoryClient only contains a \c ZoneTable object, which
  625. /// consists of (pointers to) \c InMemoryZoneFinder objects, we may add more
  626. /// member variables later for new features.
  627. class InMemoryClient::InMemoryClientImpl {
  628. public:
  629. InMemoryClientImpl() : zone_count(0) {}
  630. unsigned int zone_count;
  631. ZoneTable zone_table;
  632. };
  633. InMemoryClient::InMemoryClient() : impl_(new InMemoryClientImpl)
  634. {}
  635. InMemoryClient::~InMemoryClient() {
  636. delete impl_;
  637. }
  638. unsigned int
  639. InMemoryClient::getZoneCount() const {
  640. return (impl_->zone_count);
  641. }
  642. result::Result
  643. InMemoryClient::addZone(ZoneFinderPtr zone_finder) {
  644. if (!zone_finder) {
  645. isc_throw(InvalidParameter,
  646. "Null pointer is passed to InMemoryClient::addZone()");
  647. }
  648. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_MEM_ADD_ZONE).
  649. arg(zone_finder->getOrigin()).arg(zone_finder->getClass().toText());
  650. const result::Result result = impl_->zone_table.addZone(zone_finder);
  651. if (result == result::SUCCESS) {
  652. ++impl_->zone_count;
  653. }
  654. return (result);
  655. }
  656. InMemoryClient::FindResult
  657. InMemoryClient::findZone(const isc::dns::Name& name) const {
  658. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEM_FIND_ZONE).arg(name);
  659. ZoneTable::FindResult result(impl_->zone_table.findZone(name));
  660. return (FindResult(result.code, result.zone));
  661. }
  662. namespace {
  663. class MemoryIterator : public ZoneIterator {
  664. private:
  665. RBTreeNodeChain<Domain> chain_;
  666. Domain::const_iterator dom_iterator_;
  667. const DomainTree& tree_;
  668. const DomainNode* node_;
  669. // Only used when separate_rrs_ is true
  670. RdataIteratorPtr rdata_iterator_;
  671. bool separate_rrs_;
  672. bool ready_;
  673. public:
  674. MemoryIterator(const DomainTree& tree, const Name& origin, bool separate_rrs) :
  675. tree_(tree),
  676. separate_rrs_(separate_rrs),
  677. ready_(true)
  678. {
  679. // Find the first node (origin) and preserve the node chain for future
  680. // searches
  681. DomainTree::Result result(tree_.find<void*>(origin, &node_, chain_,
  682. NULL, NULL));
  683. // It can't happen that the origin is not in there
  684. if (result != DomainTree::EXACTMATCH) {
  685. isc_throw(Unexpected,
  686. "In-memory zone corrupted, missing origin node");
  687. }
  688. // Initialize the iterator if there's somewhere to point to
  689. if (node_ != NULL && node_->getData() != DomainPtr()) {
  690. dom_iterator_ = node_->getData()->begin();
  691. if (separate_rrs_ && dom_iterator_ != node_->getData()->end()) {
  692. rdata_iterator_ = dom_iterator_->second->getRdataIterator();
  693. }
  694. }
  695. }
  696. virtual ConstRRsetPtr getNextRRset() {
  697. if (!ready_) {
  698. isc_throw(Unexpected, "Iterating past the zone end");
  699. }
  700. /*
  701. * This cycle finds the first nonempty node with yet unused RRset.
  702. * If it is NULL, we run out of nodes. If it is empty, it doesn't
  703. * contain any RRsets. If we are at the end, just get to next one.
  704. */
  705. while (node_ != NULL && (node_->getData() == DomainPtr() ||
  706. dom_iterator_ == node_->getData()->end())) {
  707. node_ = tree_.nextNode(chain_);
  708. // If there's a node, initialize the iterator and check next time
  709. // if the map is empty or not
  710. if (node_ != NULL && node_->getData() != NULL) {
  711. dom_iterator_ = node_->getData()->begin();
  712. // New RRset, so get a new rdata iterator
  713. if (separate_rrs_) {
  714. rdata_iterator_ = dom_iterator_->second->getRdataIterator();
  715. }
  716. }
  717. }
  718. if (node_ == NULL) {
  719. // That's all, folks
  720. ready_ = false;
  721. return (ConstRRsetPtr());
  722. }
  723. if (separate_rrs_) {
  724. // For separate rrs, reconstruct a new RRset with just the
  725. // 'current' rdata
  726. RRsetPtr result(new RRset(dom_iterator_->second->getName(),
  727. dom_iterator_->second->getClass(),
  728. dom_iterator_->second->getType(),
  729. dom_iterator_->second->getTTL()));
  730. result->addRdata(rdata_iterator_->getCurrent());
  731. rdata_iterator_->next();
  732. if (rdata_iterator_->isLast()) {
  733. // all used up, next.
  734. ++dom_iterator_;
  735. // New RRset, so get a new rdata iterator, but only if this
  736. // was not the final RRset in the chain
  737. if (dom_iterator_ != node_->getData()->end()) {
  738. rdata_iterator_ = dom_iterator_->second->getRdataIterator();
  739. }
  740. }
  741. return (result);
  742. } else {
  743. // The iterator points to the next yet unused RRset now
  744. ConstRRsetPtr result(dom_iterator_->second);
  745. // This one is used, move it to the next time for next call
  746. ++dom_iterator_;
  747. return (result);
  748. }
  749. }
  750. virtual ConstRRsetPtr getSOA() const {
  751. isc_throw(NotImplemented, "Not imelemented");
  752. }
  753. };
  754. } // End of anonymous namespace
  755. ZoneIteratorPtr
  756. InMemoryClient::getIterator(const Name& name, bool separate_rrs) const {
  757. ZoneTable::FindResult result(impl_->zone_table.findZone(name));
  758. if (result.code != result::SUCCESS) {
  759. isc_throw(DataSourceError, "No such zone: " + name.toText());
  760. }
  761. const InMemoryZoneFinder*
  762. zone(dynamic_cast<const InMemoryZoneFinder*>(result.zone.get()));
  763. if (zone == NULL) {
  764. /*
  765. * TODO: This can happen only during some of the tests and only as
  766. * a temporary solution. This should be fixed by #1159 and then
  767. * this cast and check shouldn't be necessary. We don't have
  768. * test for handling a "can not happen" condition.
  769. */
  770. isc_throw(Unexpected, "The zone at " + name.toText() +
  771. " is not InMemoryZoneFinder");
  772. }
  773. return (ZoneIteratorPtr(new MemoryIterator(zone->impl_->domains_, name,
  774. separate_rrs)));
  775. }
  776. ZoneUpdaterPtr
  777. InMemoryClient::getUpdater(const isc::dns::Name&, bool, bool) const {
  778. isc_throw(isc::NotImplemented, "Update attempt on in memory data source");
  779. }
  780. pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
  781. InMemoryClient::getJournalReader(const isc::dns::Name&, uint32_t,
  782. uint32_t) const
  783. {
  784. isc_throw(isc::NotImplemented, "Journaling isn't supported for "
  785. "in memory data source");
  786. }
  787. namespace {
  788. // convencience function to add an error message to a list of those
  789. // (TODO: move functions like these to some util lib?)
  790. void
  791. addError(ElementPtr errors, const std::string& error) {
  792. if (errors != ElementPtr() && errors->getType() == Element::list) {
  793. errors->add(Element::create(error));
  794. }
  795. }
  796. /// Check if the given element exists in the map, and if it is a string
  797. bool
  798. checkConfigElementString(ConstElementPtr config, const std::string& name,
  799. ElementPtr errors)
  800. {
  801. if (!config->contains(name)) {
  802. addError(errors,
  803. "Config for memory backend does not contain a '"
  804. +name+
  805. "' value");
  806. return false;
  807. } else if (!config->get(name) ||
  808. config->get(name)->getType() != Element::string) {
  809. addError(errors, "value of " + name +
  810. " in memory backend config is not a string");
  811. return false;
  812. } else {
  813. return true;
  814. }
  815. }
  816. bool
  817. checkZoneConfig(ConstElementPtr config, ElementPtr errors) {
  818. bool result = true;
  819. if (!config || config->getType() != Element::map) {
  820. addError(errors, "Elements in memory backend's zone list must be maps");
  821. result = false;
  822. } else {
  823. if (!checkConfigElementString(config, "origin", errors)) {
  824. result = false;
  825. }
  826. if (!checkConfigElementString(config, "file", errors)) {
  827. result = false;
  828. }
  829. // we could add some existence/readabilty/parsability checks here
  830. // if we want
  831. }
  832. return result;
  833. }
  834. bool
  835. checkConfig(ConstElementPtr config, ElementPtr errors) {
  836. /* Specific configuration is under discussion, right now this accepts
  837. * the 'old' configuration, see [TODO]
  838. * So for memory datasource, we get a structure like this:
  839. * { "type": string ("memory"),
  840. * "class": string ("IN"/"CH"/etc),
  841. * "zones": list
  842. * }
  843. * Zones list is a list of maps:
  844. * { "origin": string,
  845. * "file": string
  846. * }
  847. *
  848. * At this moment we cannot be completely sure of the contents of the
  849. * structure, so we have to do some more extensive tests than should
  850. * strictly be necessary (e.g. existence and type of elements)
  851. */
  852. bool result = true;
  853. if (!config || config->getType() != Element::map) {
  854. addError(errors, "Base config for memory backend must be a map");
  855. result = false;
  856. } else {
  857. if (!checkConfigElementString(config, "type", errors)) {
  858. result = false;
  859. } else {
  860. if (config->get("type")->stringValue() != "memory") {
  861. addError(errors,
  862. "Config for memory backend is not of type \"memory\"");
  863. result = false;
  864. }
  865. }
  866. if (!checkConfigElementString(config, "class", errors)) {
  867. result = false;
  868. } else {
  869. try {
  870. RRClass rrc(config->get("class")->stringValue());
  871. } catch (const isc::Exception& rrce) {
  872. addError(errors,
  873. "Error parsing class config for memory backend: " +
  874. std::string(rrce.what()));
  875. result = false;
  876. }
  877. }
  878. if (!config->contains("zones")) {
  879. addError(errors, "No 'zones' element in memory backend config");
  880. result = false;
  881. } else if (!config->get("zones") ||
  882. config->get("zones")->getType() != Element::list) {
  883. addError(errors, "'zones' element in memory backend config is not a list");
  884. result = false;
  885. } else {
  886. BOOST_FOREACH(ConstElementPtr zone_config,
  887. config->get("zones")->listValue()) {
  888. if (!checkZoneConfig(zone_config, errors)) {
  889. result = false;
  890. }
  891. }
  892. }
  893. }
  894. return (result);
  895. return true;
  896. }
  897. } // end anonymous namespace
  898. DataSourceClient *
  899. createInstance(isc::data::ConstElementPtr config, std::string& error) {
  900. ElementPtr errors(Element::createList());
  901. if (!checkConfig(config, errors)) {
  902. error = "Configuration error: " + errors->str();
  903. return (NULL);
  904. }
  905. try {
  906. return (new InMemoryClient());
  907. } catch (const std::exception& exc) {
  908. error = std::string("Error creating memory datasource: ") + exc.what();
  909. return (NULL);
  910. } catch (...) {
  911. error = std::string("Error creating memory datasource, "
  912. "unknown exception");
  913. return (NULL);
  914. }
  915. }
  916. void destroyInstance(DataSourceClient* instance) {
  917. delete instance;
  918. }
  919. } // end of namespace datasrc
  920. } // end of namespace isc