memory_datasrc.cc 12 KB

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