zone_data_updater.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. // Copyright (C) 2012 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 <datasrc/memory/zone_data_updater.h>
  15. #include <datasrc/zone.h>
  16. #include <dns/rdataclass.h>
  17. #include <boost/scoped_ptr.hpp>
  18. #include <boost/foreach.hpp>
  19. using namespace isc::dns;
  20. using namespace isc::dns::rdata;
  21. namespace isc {
  22. namespace datasrc {
  23. namespace memory {
  24. namespace { // unnamed namespace
  25. // Returns a value-less than, or greater-than zero if 'a' is less-than
  26. // or greater-than 'b'. If they are equal, it returns 0. The comparison
  27. // function is such that often-used types such as A, AAAA, NS, SOA, MX,
  28. // etc. are less than other types. See the code for the ordering.
  29. int
  30. compareTypes(const RdataSet* a, const RdataSet* b) {
  31. // First RRType::A()
  32. if (a->type == RRType::A()) {
  33. return (-1);
  34. } else if (b->type == RRType::A()) {
  35. return (1);
  36. }
  37. // Then, RRType::AAAA(), etc.
  38. if (a->type == RRType::AAAA()) {
  39. return (-1);
  40. } else if (b->type == RRType::AAAA()) {
  41. return (1);
  42. }
  43. if (a->type == RRType::NS()) {
  44. return (-1);
  45. } else if (b->type == RRType::NS()) {
  46. return (1);
  47. }
  48. if (a->type == RRType::SOA()) {
  49. return (-1);
  50. } else if (b->type == RRType::SOA()) {
  51. return (1);
  52. }
  53. if (a->type == RRType::MX()) {
  54. return (-1);
  55. } else if (b->type == RRType::MX()) {
  56. return (1);
  57. }
  58. // Everything else comes in front of the rest of the list, so that
  59. // we can insert quickly.
  60. return (-1);
  61. }
  62. } // end of unnamed namespace
  63. void
  64. ZoneDataUpdater::addWildcards(const Name& name) {
  65. Name wname(name);
  66. const unsigned int labels(wname.getLabelCount());
  67. const unsigned int origin_labels(zone_name_.getLabelCount());
  68. for (unsigned int l = labels;
  69. l > origin_labels;
  70. --l, wname = wname.split(1))
  71. {
  72. if (wname.isWildcard()) {
  73. // Ensure a separate level exists for the "wildcarding"
  74. // name, and mark the node as "wild".
  75. ZoneNode* node;
  76. zone_data_.insertName(mem_sgmt_, wname.split(1), &node);
  77. node->setFlag(ZoneData::WILDCARD_NODE);
  78. // Ensure a separate level exists for the wildcard name.
  79. // Note: for 'name' itself we do this later anyway, but the
  80. // overhead should be marginal because wildcard names should
  81. // be rare.
  82. zone_data_.insertName(mem_sgmt_, wname, &node);
  83. }
  84. }
  85. }
  86. void
  87. ZoneDataUpdater::contextCheck(const AbstractRRset& rrset,
  88. const RdataSet* rdataset) const
  89. {
  90. // Ensure CNAME and other type of RR don't coexist for the same
  91. // owner name except with NSEC, which is the only RR that can
  92. // coexist with CNAME (and also RRSIG, which is handled separately)
  93. if (rrset.getType() == RRType::CNAME()) {
  94. for (const RdataSet* sp = rdataset; sp != NULL; sp = sp->getNext()) {
  95. if (sp->type != RRType::NSEC()) {
  96. isc_throw(AddError,
  97. "CNAME can't be added with " << sp->type
  98. << " RRType for " << rrset.getName());
  99. }
  100. }
  101. } else if ((rrset.getType() != RRType::NSEC()) &&
  102. (RdataSet::find(rdataset, RRType::CNAME()) != NULL))
  103. {
  104. isc_throw(AddError,
  105. "CNAME and " << rrset.getType() <<
  106. " can't coexist for " << rrset.getName());
  107. }
  108. // Similar with DNAME, but it must not coexist only with NS and only
  109. // in non-apex domains. RFC 2672 section 3 mentions that it is
  110. // implied from it and RFC 2181.
  111. if (rrset.getName() != zone_name_ &&
  112. // Adding DNAME, NS already there
  113. ((rrset.getType() == RRType::DNAME() &&
  114. RdataSet::find(rdataset, RRType::NS()) != NULL) ||
  115. // Adding NS, DNAME already there
  116. (rrset.getType() == RRType::NS() &&
  117. RdataSet::find(rdataset, RRType::DNAME()) != NULL)))
  118. {
  119. isc_throw(AddError, "DNAME can't coexist with NS in non-apex domain: "
  120. << rrset.getName());
  121. }
  122. }
  123. void
  124. ZoneDataUpdater::validate(const isc::dns::ConstRRsetPtr rrset) const {
  125. if (!rrset) {
  126. isc_throw(NullRRset, "The rrset provided is NULL");
  127. }
  128. if (rrset->getRdataCount() == 0) {
  129. isc_throw(AddError,
  130. "The rrset provided is empty: "
  131. << rrset->getName() << "/" << rrset->getType());
  132. }
  133. // Check for singleton RRs. It should probably handled at a different
  134. // layer in future.
  135. if ((rrset->getType() == RRType::CNAME() ||
  136. rrset->getType() == RRType::DNAME()) &&
  137. rrset->getRdataCount() > 1)
  138. {
  139. // XXX: this is not only for CNAME or DNAME. We should
  140. // generalize this code for all other "singleton RR types" (such
  141. // as SOA) in a separate task.
  142. isc_throw(AddError, "multiple RRs of singleton type for "
  143. << rrset->getName());
  144. }
  145. // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
  146. // implementation requests it be so at the moment.
  147. if ((rrset->getType() == RRType::NSEC3() ||
  148. rrset->getType() == RRType::NSEC3PARAM()) &&
  149. (rrset->getRdataCount() > 1))
  150. {
  151. isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
  152. << rrset->getName() << " which isn't supported");
  153. }
  154. // For RRSIGs, check consistency of the type covered. We know the
  155. // RRset isn't empty, so the following check is safe.
  156. if (rrset->getType() == RRType::RRSIG()) {
  157. RdataIteratorPtr rit = rrset->getRdataIterator();
  158. const RRType covered = dynamic_cast<const generic::RRSIG&>(
  159. rit->getCurrent()).typeCovered();
  160. for (rit->next(); !rit->isLast(); rit->next()) {
  161. if (dynamic_cast<const generic::RRSIG&>(
  162. rit->getCurrent()).typeCovered() != covered)
  163. {
  164. isc_throw(AddError, "RRSIG contains mixed covered types: "
  165. << rrset->toText());
  166. }
  167. }
  168. }
  169. const NameComparisonResult compare = zone_name_.compare(rrset->getName());
  170. if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
  171. compare.getRelation() != NameComparisonResult::EQUAL)
  172. {
  173. isc_throw(OutOfZone,
  174. "The name " << rrset->getName() <<
  175. " is not contained in zone " << zone_name_);
  176. }
  177. // Some RR types do not really work well with a wildcard. Even
  178. // though the protocol specifically doesn't completely ban such
  179. // usage, we refuse to load a zone containing such RR in order to
  180. // keep the lookup logic simpler and more predictable. See RFC4592
  181. // and (for DNAME) RFC6672 for more technical background. Note also
  182. // that BIND 9 refuses NS at a wildcard, so in that sense we simply
  183. // provide compatible behavior.
  184. if (rrset->getName().isWildcard()) {
  185. if (rrset->getType() == RRType::NS()) {
  186. isc_throw(AddError, "Invalid NS owner name (wildcard): "
  187. << rrset->getName());
  188. }
  189. if (rrset->getType() == RRType::DNAME()) {
  190. isc_throw(AddError, "Invalid DNAME owner name (wildcard): "
  191. << rrset->getName());
  192. }
  193. }
  194. // Owner names of NSEC3 have special format as defined in RFC5155,
  195. // and cannot be a wildcard name or must be one label longer than
  196. // the zone origin. While the RFC doesn't prohibit other forms of
  197. // names, no sane zone would have such names for NSEC3. BIND 9 also
  198. // refuses NSEC3 at wildcard.
  199. if (rrset->getType() == RRType::NSEC3() &&
  200. (rrset->getName().isWildcard() ||
  201. rrset->getName().getLabelCount() != zone_name_.getLabelCount() + 1))
  202. {
  203. isc_throw(AddError, "Invalid NSEC3 owner name: " <<
  204. rrset->getName() << "; zone: " << zone_name_);
  205. }
  206. }
  207. const NSEC3Hash*
  208. ZoneDataUpdater::getNSEC3Hash() {
  209. if (hash_ == NULL) {
  210. NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
  211. // This should never be NULL in this codepath.
  212. assert(nsec3_data != NULL);
  213. hash_ = NSEC3Hash::create(nsec3_data->hashalg,
  214. nsec3_data->iterations,
  215. nsec3_data->getSaltData(),
  216. nsec3_data->getSaltLen());
  217. }
  218. return (hash_);
  219. }
  220. template <typename T>
  221. void
  222. ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
  223. // We know rrset has exactly one RDATA
  224. const T& nsec3_rdata =
  225. dynamic_cast<const T&>(
  226. rrset->getRdataIterator()->getCurrent());
  227. NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
  228. if (nsec3_data == NULL) {
  229. nsec3_data = NSEC3Data::create(mem_sgmt_, nsec3_rdata);
  230. zone_data_.setNSEC3Data(nsec3_data);
  231. zone_data_.setSigned(true);
  232. } else {
  233. const NSEC3Hash* hash = getNSEC3Hash();
  234. if (!hash->match(nsec3_rdata)) {
  235. isc_throw(AddError,
  236. rrset->getType() << " with inconsistent parameters: "
  237. << rrset->toText());
  238. }
  239. }
  240. }
  241. void
  242. ZoneDataUpdater::addNSEC3(const ConstRRsetPtr rrset, const ConstRRsetPtr rrsig)
  243. {
  244. setupNSEC3<generic::NSEC3>(rrset);
  245. NSEC3Data* nsec3_data = zone_data_.getNSEC3Data();
  246. ZoneNode* node;
  247. nsec3_data->insertName(mem_sgmt_, rrset->getName(), &node);
  248. RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, rrset, rrsig);
  249. RdataSet* old_rdataset = node->setData(rdataset);
  250. if (old_rdataset != NULL) {
  251. RdataSet::destroy(mem_sgmt_, rrclass_, old_rdataset);
  252. }
  253. }
  254. void
  255. ZoneDataUpdater::addRdataSet(const ConstRRsetPtr rrset,
  256. const ConstRRsetPtr rrsig)
  257. {
  258. if (rrset->getType() == RRType::NSEC3()) {
  259. addNSEC3(rrset, rrsig);
  260. } else {
  261. ZoneNode* node;
  262. zone_data_.insertName(mem_sgmt_, rrset->getName(), &node);
  263. RdataSet* rdataset_head = node->getData();
  264. // Checks related to the surrounding data. Note: when the check
  265. // fails and the exception is thrown, it may break strong
  266. // exception guarantee. At the moment we prefer code simplicity
  267. // and don't bother to introduce complicated recovery code.
  268. contextCheck(*rrset, rdataset_head);
  269. if (RdataSet::find(rdataset_head, rrset->getType()) != NULL) {
  270. isc_throw(AddError,
  271. "RRset of the type already exists: "
  272. << rrset->getName() << " (type: "
  273. << rrset->getType() << ")");
  274. }
  275. RdataSet* rdataset_new = RdataSet::create(mem_sgmt_, encoder_,
  276. rrset, rrsig);
  277. // Insertion sort the new RdataSet into place in the list.
  278. RdataSet* prev = NULL;
  279. RdataSet* current = rdataset_head;
  280. for (; current != NULL; current = current->getNext()) {
  281. if (compareTypes(rdataset_new, current) < 0) {
  282. break;
  283. }
  284. prev = current;
  285. }
  286. rdataset_new->next = current;
  287. if (prev != NULL) {
  288. prev->next = rdataset_new;
  289. } else {
  290. node->setData(rdataset_new);
  291. }
  292. // Ok, we just put it in.
  293. // If this RRset creates a zone cut at this node, mark the node
  294. // indicating the need for callback in find().
  295. if (rrset->getType() == RRType::NS() &&
  296. rrset->getName() != zone_name_) {
  297. node->setFlag(ZoneNode::FLAG_CALLBACK);
  298. // If it is DNAME, we have a callback as well here
  299. } else if (rrset->getType() == RRType::DNAME()) {
  300. node->setFlag(ZoneNode::FLAG_CALLBACK);
  301. }
  302. // If we've added NSEC3PARAM at zone origin, set up NSEC3
  303. // specific data or check consistency with already set up
  304. // parameters.
  305. if (rrset->getType() == RRType::NSEC3PARAM() &&
  306. rrset->getName() == zone_name_) {
  307. setupNSEC3<generic::NSEC3PARAM>(rrset);
  308. } else if (rrset->getType() == RRType::NSEC()) {
  309. // If it is NSEC signed zone, we mark the zone as signed
  310. // (conceptually "signed" is a broader notion but our
  311. // current zone finder implementation regards "signed" as
  312. // "NSEC signed")
  313. zone_data_.setSigned(true);
  314. }
  315. }
  316. }
  317. void
  318. ZoneDataUpdater::add(const ConstRRsetPtr& rrset,
  319. const ConstRRsetPtr& sig_rrset)
  320. {
  321. // Validate input. This will cause an exception to be thrown if the
  322. // input RRset is empty.
  323. validate(rrset);
  324. if (sig_rrset) {
  325. validate(sig_rrset);
  326. }
  327. // OK, can add the RRset.
  328. // Add wildcards possibly contained in the owner name to the domain
  329. // tree. This can only happen for the normal (non-NSEC3) tree.
  330. // Note: this can throw an exception, breaking strong exception
  331. // guarantee. (see also the note for the call to contextCheck()
  332. // above).
  333. if (rrset->getType() != RRType::NSEC3()) {
  334. addWildcards(rrset->getName());
  335. }
  336. addRdataSet(rrset, sig_rrset);
  337. }
  338. void
  339. ZoneDataUpdater::Loader::addFromLoad(const ConstRRsetPtr& rrset) {
  340. // If we see a new name, flush the temporary holders, adding the
  341. // pairs of RRsets and RRSIGs of the previous name to the zone.
  342. if ((!node_rrsets_.empty() || !node_rrsigsets_.empty()) &&
  343. (getCurrentName() != rrset->getName())) {
  344. flushNodeRRsets();
  345. }
  346. // Store this RRset until it can be added to the zone. The current
  347. // implementation requires RRs of the same RRset should be added at
  348. // once, so we check the "duplicate" here.
  349. const bool is_rrsig = rrset->getType() == RRType::RRSIG();
  350. NodeRRsets& node_rrsets = is_rrsig ? node_rrsigsets_ : node_rrsets_;
  351. const RRType& rrtype = is_rrsig ? getCoveredType(rrset) : rrset->getType();
  352. if (!node_rrsets.insert(NodeRRsetsVal(rrtype, rrset)).second) {
  353. isc_throw(ZoneDataUpdater::AddError,
  354. "Duplicate add of the same type of"
  355. << (is_rrsig ? " RRSIG" : "") << " RRset: "
  356. << rrset->getName() << "/" << rrtype);
  357. }
  358. }
  359. void
  360. ZoneDataUpdater::Loader::flushNodeRRsets() {
  361. BOOST_FOREACH(NodeRRsetsVal val, node_rrsets_) {
  362. // Identify the corresponding RRSIG for the RRset, if any. If
  363. // found add both the RRset and its RRSIG at once.
  364. ConstRRsetPtr sig_rrset;
  365. NodeRRsets::iterator sig_it = node_rrsigsets_.find(val.first);
  366. if (sig_it != node_rrsigsets_.end()) {
  367. sig_rrset = sig_it->second;
  368. node_rrsigsets_.erase(sig_it);
  369. }
  370. updater_.add(val.second, sig_rrset);
  371. }
  372. // Right now, we don't accept RRSIG without covered RRsets (this
  373. // should eventually allowed, but to do so we'll need to update the
  374. // finder).
  375. if (!node_rrsigsets_.empty()) {
  376. isc_throw(ZoneDataUpdater::AddError,
  377. "RRSIG is added without covered RRset for "
  378. << getCurrentName());
  379. }
  380. node_rrsets_.clear();
  381. node_rrsigsets_.clear();
  382. }
  383. RRType
  384. ZoneDataUpdater::Loader::getCoveredType(const ConstRRsetPtr& sig_rrset) {
  385. RdataIteratorPtr it = sig_rrset->getRdataIterator();
  386. // Empty RRSIG shouldn't be passed either via a master file or
  387. // another data source iterator, but it could still happen if the
  388. // iterator has a bug. We catch and reject such cases.
  389. if (it->isLast()) {
  390. isc_throw(isc::Unexpected,
  391. "Empty RRset is passed in-memory loader, name: "
  392. << sig_rrset->getName());
  393. }
  394. return (dynamic_cast<const generic::RRSIG&>(it->getCurrent()).
  395. typeCovered());
  396. }
  397. const Name&
  398. ZoneDataUpdater::Loader::getCurrentName() const {
  399. if (!node_rrsets_.empty()) {
  400. return (node_rrsets_.begin()->second->getName());
  401. }
  402. assert(!node_rrsigsets_.empty());
  403. return (node_rrsigsets_.begin()->second->getName());
  404. }
  405. } // namespace memory
  406. } // namespace datasrc
  407. } // namespace isc