memory_datasrc.cc 11 KB

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