memory_datasrc.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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 <dns/name.h>
  19. #include <dns/rrclass.h>
  20. #include <dns/masterload.h>
  21. #include <datasrc/memory_datasrc.h>
  22. #include <datasrc/rbtree.h>
  23. using namespace std;
  24. using namespace isc::dns;
  25. namespace isc {
  26. namespace datasrc {
  27. // Private data and hidden methods of MemoryZone
  28. struct MemoryZone::MemoryZoneImpl {
  29. // Constructor
  30. MemoryZoneImpl(const RRClass& zone_class, const Name& origin) :
  31. zone_class_(zone_class), origin_(origin)
  32. {}
  33. // Information about the zone
  34. RRClass zone_class_;
  35. Name origin_;
  36. string file_name_;
  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. // The actual zone data
  56. DomainTree domains_;
  57. /*
  58. * Implementation of longer methods. We put them here, because the
  59. * access is without the impl_-> and it will get inlined anyway.
  60. */
  61. // Implementation of MemoryZone::add
  62. result::Result add(const ConstRRsetPtr& rrset, DomainTree* domains) {
  63. // Sanitize input
  64. if (!rrset) {
  65. isc_throw(NullRRset, "The rrset provided is NULL");
  66. }
  67. if (rrset->getType() == RRType::CNAME() &&
  68. rrset->getRdataCount() > 1) {
  69. // XXX: this is not only for CNAME. We should generalize this
  70. // code for all other "singleton RR types" (such as SOA) in a
  71. // separate task.
  72. isc_throw(AddError, "multiple RRs of singleton type for "
  73. << rrset->getName());
  74. }
  75. Name name(rrset->getName());
  76. NameComparisonResult compare(origin_.compare(name));
  77. if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
  78. compare.getRelation() != NameComparisonResult::EQUAL)
  79. {
  80. isc_throw(OutOfZone, "The name " << name <<
  81. " is not contained in zone " << origin_);
  82. }
  83. // Get the node
  84. DomainNode* node;
  85. switch (domains->insert(name, &node)) {
  86. // Just check it returns reasonable results
  87. case DomainTree::SUCCESS:
  88. case DomainTree::ALREADYEXISTS:
  89. break;
  90. // Something odd got out
  91. default:
  92. assert(0);
  93. }
  94. assert(node != NULL);
  95. // Now get the domain
  96. DomainPtr domain;
  97. // It didn't exist yet, create it
  98. if (node->isEmpty()) {
  99. domain.reset(new Domain);
  100. node->setData(domain);
  101. } else { // Get existing one
  102. domain = node->getData();
  103. }
  104. // Ensure CNAME and other type of RR don't coexist for the same
  105. // owner name.
  106. // Note: when the check fails and the exception is thrown, it may
  107. // break strong exception guarantee. At the moment we prefer
  108. // code simplicity and don't bother to introduce complicated
  109. // recovery code.
  110. if (rrset->getType() == RRType::CNAME()) {
  111. // XXX: this check will become incorrect when we support DNSSEC
  112. // (depending on how we support DNSSEC). We should revisit it
  113. // at that point.
  114. if (!domain->empty()) {
  115. isc_throw(AddError, "CNAME can't be added with other data for "
  116. << rrset->getName());
  117. }
  118. } else if (domain->find(RRType::CNAME()) != domain->end()) {
  119. isc_throw(AddError, "CNAME and " << rrset->getType() <<
  120. " can't coexist for " << rrset->getName());
  121. }
  122. // Try inserting the rrset there
  123. if (domain->insert(DomainPair(rrset->getType(), rrset)).second) {
  124. // Ok, we just put it in
  125. // If this RRset creates a zone cut at this node, mark the node
  126. // indicating the need for callback in find().
  127. // TBD: handle DNAME, too
  128. if (rrset->getType() == RRType::NS() &&
  129. rrset->getName() != origin_) {
  130. node->enableCallback();
  131. }
  132. return (result::SUCCESS);
  133. } else {
  134. // The RRSet of given type was already there
  135. return (result::EXIST);
  136. }
  137. }
  138. /*
  139. * Same as above, but it checks the return value and if it already exists,
  140. * it throws.
  141. */
  142. void addFromLoad(const ConstRRsetPtr& set, DomainTree* domains) {
  143. switch (add(set, domains)) {
  144. case result::EXIST:
  145. isc_throw(dns::MasterLoadError, "Duplicate rrset: " <<
  146. set->toText());
  147. case result::SUCCESS:
  148. return;
  149. default:
  150. assert(0);
  151. }
  152. }
  153. // Maintain intermediate data specific to the search context used in
  154. /// \c find().
  155. ///
  156. /// It will be passed to \c zonecutCallback() and record a possible
  157. /// zone cut node and related RRset (normally NS or DNAME).
  158. struct FindState {
  159. FindState(FindOptions options) : zonecut_node_(NULL),
  160. options_(options)
  161. {}
  162. const DomainNode* zonecut_node_;
  163. ConstRRsetPtr rrset_;
  164. const FindOptions options_;
  165. };
  166. // A callback called from possible zone cut nodes. This will be passed
  167. // from the \c find() method to \c RBTree::find().
  168. static bool zonecutCallback(const DomainNode& node, FindState* state) {
  169. // We perform callback check only for the highest zone cut in the
  170. // rare case of nested zone cuts.
  171. if (state->zonecut_node_ != NULL) {
  172. return (false);
  173. }
  174. const Domain::const_iterator found(node.getData()->find(RRType::NS()));
  175. if (found != node.getData()->end()) {
  176. // BIND 9 checks if this node is not the origin. But it cannot
  177. // be the origin because we don't enable the callback at the
  178. // origin node (see MemoryZoneImpl::add()). Or should we do a
  179. // double check for it?
  180. state->zonecut_node_ = &node;
  181. state->rrset_ = found->second;
  182. // Unless glue is allowed the search stops here, so we return
  183. // false; otherwise return true to continue the search.
  184. return ((state->options_ & FIND_GLUE_OK) == 0);
  185. }
  186. // This case should not happen because we enable callback only
  187. // when we add an RR searched for above.
  188. assert(0);
  189. // This is here to avoid warning (therefore compilation error)
  190. // in case assert is turned off. Otherwise we could get "Control
  191. // reached end of non-void function".
  192. return (false);
  193. }
  194. // Implementation of MemoryZone::find
  195. FindResult find(const Name& name, RRType type,
  196. const FindOptions options) const
  197. {
  198. // Get the node
  199. DomainNode* node(NULL);
  200. FindState state(options);
  201. switch (domains_.find(name, &node, zonecutCallback, &state)) {
  202. case DomainTree::PARTIALMATCH:
  203. if (state.zonecut_node_ != NULL) {
  204. return (FindResult(DELEGATION, state.rrset_));
  205. }
  206. // TODO: we should also cover empty non-terminal cases, which
  207. // will require non trivial code and is deferred for later
  208. // development. For now, we regard any partial match that
  209. // didn't hit a zone cut as "not found".
  210. case DomainTree::NOTFOUND:
  211. return (FindResult(NXDOMAIN, ConstRRsetPtr()));
  212. case DomainTree::EXACTMATCH: // This one is OK, handle it
  213. break;
  214. default:
  215. assert(0);
  216. }
  217. assert(node);
  218. assert(!node->isEmpty());
  219. Domain::const_iterator found;
  220. // If the node callback is enabled, this may be a zone cut. If it
  221. // has a NS RR, we should return a delegation.
  222. if (node->isCallbackEnabled()) {
  223. found = node->getData()->find(RRType::NS());
  224. if (found != node->getData()->end()) {
  225. return (FindResult(DELEGATION, found->second));
  226. }
  227. }
  228. found = node->getData()->find(type);
  229. if (found != node->getData()->end()) {
  230. // Good, it is here
  231. return (FindResult(SUCCESS, found->second));
  232. } else {
  233. // Next, try CNAME.
  234. found = node->getData()->find(RRType::CNAME());
  235. if (found != node->getData()->end()) {
  236. return (FindResult(CNAME, found->second));
  237. }
  238. }
  239. // No exact match or CNAME. Return NXRRSET.
  240. return (FindResult(NXRRSET, ConstRRsetPtr()));
  241. }
  242. };
  243. MemoryZone::MemoryZone(const RRClass& zone_class, const Name& origin) :
  244. impl_(new MemoryZoneImpl(zone_class, origin))
  245. {
  246. }
  247. MemoryZone::~MemoryZone() {
  248. delete impl_;
  249. }
  250. const Name&
  251. MemoryZone::getOrigin() const {
  252. return (impl_->origin_);
  253. }
  254. const RRClass&
  255. MemoryZone::getClass() const {
  256. return (impl_->zone_class_);
  257. }
  258. Zone::FindResult
  259. MemoryZone::find(const Name& name, const RRType& type,
  260. const FindOptions options) const
  261. {
  262. return (impl_->find(name, type, options));
  263. }
  264. result::Result
  265. MemoryZone::add(const ConstRRsetPtr& rrset) {
  266. return (impl_->add(rrset, &impl_->domains_));
  267. }
  268. void
  269. MemoryZone::load(const string& filename) {
  270. // Load it into a temporary tree
  271. MemoryZoneImpl::DomainTree tmp;
  272. masterLoad(filename.c_str(), getOrigin(), getClass(),
  273. boost::bind(&MemoryZoneImpl::addFromLoad, impl_, _1, &tmp));
  274. // If it went well, put it inside
  275. impl_->file_name_ = filename;
  276. tmp.swap(impl_->domains_);
  277. // And let the old data die with tmp
  278. }
  279. void
  280. MemoryZone::swap(MemoryZone& zone) {
  281. std::swap(impl_, zone.impl_);
  282. }
  283. const string
  284. MemoryZone::getFileName() const {
  285. return (impl_->file_name_);
  286. }
  287. /// Implementation details for \c MemoryDataSrc hidden from the public
  288. /// interface.
  289. ///
  290. /// For now, \c MemoryDataSrc only contains a \c ZoneTable object, which
  291. /// consists of (pointers to) \c MemoryZone objects, we may add more
  292. /// member variables later for new features.
  293. class MemoryDataSrc::MemoryDataSrcImpl {
  294. public:
  295. MemoryDataSrcImpl() : zone_count(0) {}
  296. unsigned int zone_count;
  297. ZoneTable zone_table;
  298. };
  299. MemoryDataSrc::MemoryDataSrc() : impl_(new MemoryDataSrcImpl)
  300. {}
  301. MemoryDataSrc::~MemoryDataSrc() {
  302. delete impl_;
  303. }
  304. unsigned int
  305. MemoryDataSrc::getZoneCount() const {
  306. return (impl_->zone_count);
  307. }
  308. result::Result
  309. MemoryDataSrc::addZone(ZonePtr zone) {
  310. if (!zone) {
  311. isc_throw(InvalidParameter,
  312. "Null pointer is passed to MemoryDataSrc::addZone()");
  313. }
  314. const result::Result result = impl_->zone_table.addZone(zone);
  315. if (result == result::SUCCESS) {
  316. ++impl_->zone_count;
  317. }
  318. return (result);
  319. }
  320. MemoryDataSrc::FindResult
  321. MemoryDataSrc::findZone(const isc::dns::Name& name) const {
  322. return (FindResult(impl_->zone_table.findZone(name).code,
  323. impl_->zone_table.findZone(name).zone));
  324. }
  325. } // end of namespace datasrc
  326. } // end of namespace dns