memory_datasrc.cc 11 KB

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