zone_data_updater.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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 <exceptions/exceptions.h>
  15. #include <datasrc/memory/zone_data_updater.h>
  16. #include <datasrc/memory/logger.h>
  17. #include <datasrc/memory/util_internal.h>
  18. #include <datasrc/zone.h>
  19. #include <dns/rdataclass.h>
  20. #include <cassert>
  21. #include <string>
  22. using namespace isc::dns;
  23. using namespace isc::dns::rdata;
  24. namespace isc {
  25. namespace datasrc {
  26. namespace memory {
  27. using detail::getCoveredType;
  28. void
  29. ZoneDataUpdater::addWildcards(const Name& name) {
  30. Name wname(name);
  31. const unsigned int labels(wname.getLabelCount());
  32. const unsigned int origin_labels(zone_name_.getLabelCount());
  33. for (unsigned int l = labels;
  34. l > origin_labels;
  35. --l, wname = wname.split(1))
  36. {
  37. if (wname.isWildcard()) {
  38. LOG_DEBUG(logger, DBG_TRACE_DATA,
  39. DATASRC_MEMORY_MEM_ADD_WILDCARD).arg(name);
  40. // Ensure a separate level exists for the "wildcarding"
  41. // name, and mark the node as "wild".
  42. ZoneNode* node;
  43. zone_data_->insertName(mem_sgmt_, wname.split(1), &node);
  44. node->setFlag(ZoneData::WILDCARD_NODE);
  45. // Ensure a separate level exists for the wildcard name.
  46. // Note: for 'name' itself we do this later anyway, but the
  47. // overhead should be marginal because wildcard names should
  48. // be rare.
  49. zone_data_->insertName(mem_sgmt_, wname, &node);
  50. }
  51. }
  52. }
  53. void
  54. ZoneDataUpdater::contextCheck(const AbstractRRset& rrset,
  55. const RdataSet* rdataset) const
  56. {
  57. // Ensure CNAME and other type of RR don't coexist for the same
  58. // owner name except with NSEC, which is the only RR that can
  59. // coexist with CNAME (and also RRSIG, which is handled separately)
  60. if (rrset.getType() == RRType::CNAME()) {
  61. for (const RdataSet* sp = rdataset; sp != NULL; sp = sp->getNext()) {
  62. if (sp->type != RRType::NSEC()) {
  63. LOG_ERROR(logger, DATASRC_MEMORY_MEM_CNAME_TO_NONEMPTY).
  64. arg(rrset.getName());
  65. isc_throw(AddError,
  66. "CNAME can't be added with " << sp->type
  67. << " RRType for " << rrset.getName());
  68. }
  69. }
  70. } else if ((rrset.getType() != RRType::NSEC()) &&
  71. (RdataSet::find(rdataset, RRType::CNAME()) != NULL))
  72. {
  73. LOG_ERROR(logger,
  74. DATASRC_MEMORY_MEM_CNAME_COEXIST).arg(rrset.getName());
  75. isc_throw(AddError,
  76. "CNAME and " << rrset.getType() <<
  77. " can't coexist for " << rrset.getName());
  78. }
  79. // Similar with DNAME, but it must not coexist only with NS and only
  80. // in non-apex domains. RFC 2672 section 3 mentions that it is
  81. // implied from it and RFC 2181.
  82. if (rrset.getName() != zone_name_ &&
  83. // Adding DNAME, NS already there
  84. ((rrset.getType() == RRType::DNAME() &&
  85. RdataSet::find(rdataset, RRType::NS()) != NULL) ||
  86. // Adding NS, DNAME already there
  87. (rrset.getType() == RRType::NS() &&
  88. RdataSet::find(rdataset, RRType::DNAME()) != NULL)))
  89. {
  90. LOG_ERROR(logger, DATASRC_MEMORY_MEM_DNAME_NS).arg(rrset.getName());
  91. isc_throw(AddError, "DNAME can't coexist with NS in non-apex domain: "
  92. << rrset.getName());
  93. }
  94. }
  95. void
  96. ZoneDataUpdater::validate(const isc::dns::ConstRRsetPtr rrset) const {
  97. assert(rrset);
  98. if (rrset->getRdataCount() == 0) {
  99. isc_throw(AddError,
  100. "The rrset provided is empty: "
  101. << rrset->getName() << "/" << rrset->getType());
  102. }
  103. // Check for singleton RRs. It should probably handled at a different
  104. // layer in future.
  105. if ((rrset->getType() == RRType::CNAME() ||
  106. rrset->getType() == RRType::DNAME()) &&
  107. rrset->getRdataCount() > 1)
  108. {
  109. // XXX: this is not only for CNAME or DNAME. We should
  110. // generalize this code for all other "singleton RR types" (such
  111. // as SOA) in a separate task.
  112. LOG_ERROR(logger,
  113. DATASRC_MEMORY_MEM_SINGLETON).arg(rrset->getName()).
  114. arg(rrset->getType());
  115. isc_throw(AddError, "multiple RRs of singleton type for "
  116. << rrset->getName());
  117. }
  118. // NSEC3/NSEC3PARAM is not a "singleton" per protocol, but this
  119. // implementation requests it be so at the moment.
  120. if ((rrset->getType() == RRType::NSEC3() ||
  121. rrset->getType() == RRType::NSEC3PARAM()) &&
  122. (rrset->getRdataCount() > 1))
  123. {
  124. isc_throw(AddError, "Multiple NSEC3/NSEC3PARAM RDATA is given for "
  125. << rrset->getName() << " which isn't supported");
  126. }
  127. // For RRSIGs, check consistency of the type covered. We know the
  128. // RRset isn't empty, so the following check is safe.
  129. if (rrset->getType() == RRType::RRSIG()) {
  130. RdataIteratorPtr rit = rrset->getRdataIterator();
  131. const RRType covered = dynamic_cast<const generic::RRSIG&>(
  132. rit->getCurrent()).typeCovered();
  133. for (rit->next(); !rit->isLast(); rit->next()) {
  134. if (dynamic_cast<const generic::RRSIG&>(
  135. rit->getCurrent()).typeCovered() != covered)
  136. {
  137. isc_throw(AddError, "RRSIG contains mixed covered types: "
  138. << rrset->toText());
  139. }
  140. }
  141. }
  142. const NameComparisonResult compare = zone_name_.compare(rrset->getName());
  143. if (compare.getRelation() != NameComparisonResult::SUPERDOMAIN &&
  144. compare.getRelation() != NameComparisonResult::EQUAL)
  145. {
  146. LOG_ERROR(logger,
  147. DATASRC_MEMORY_MEM_OUT_OF_ZONE).arg(rrset->getName()).
  148. arg(zone_name_);
  149. isc_throw(AddError,
  150. "The name " << rrset->getName() <<
  151. " is not contained in zone " << zone_name_);
  152. }
  153. // Some RR types do not really work well with a wildcard. Even
  154. // though the protocol specifically doesn't completely ban such
  155. // usage, we refuse to load a zone containing such RR in order to
  156. // keep the lookup logic simpler and more predictable. See RFC4592
  157. // and (for DNAME) RFC6672 for more technical background. Note also
  158. // that BIND 9 refuses NS at a wildcard, so in that sense we simply
  159. // provide compatible behavior.
  160. if (rrset->getName().isWildcard()) {
  161. if (rrset->getType() == RRType::NS()) {
  162. LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_NS).
  163. arg(rrset->getName());
  164. isc_throw(AddError, "Invalid NS owner name (wildcard): "
  165. << rrset->getName());
  166. }
  167. if (rrset->getType() == RRType::DNAME()) {
  168. LOG_ERROR(logger, DATASRC_MEMORY_MEM_WILDCARD_DNAME).
  169. arg(rrset->getName());
  170. isc_throw(AddError, "Invalid DNAME owner name (wildcard): "
  171. << rrset->getName());
  172. }
  173. }
  174. // Owner names of NSEC3 have special format as defined in RFC5155,
  175. // and cannot be a wildcard name or must be one label longer than
  176. // the zone origin. While the RFC doesn't prohibit other forms of
  177. // names, no sane zone would have such names for NSEC3. BIND 9 also
  178. // refuses NSEC3 at wildcard.
  179. if (rrset->getType() == RRType::NSEC3() &&
  180. (rrset->getName().isWildcard() ||
  181. rrset->getName().getLabelCount() != zone_name_.getLabelCount() + 1))
  182. {
  183. LOG_ERROR(logger, DATASRC_MEMORY_BAD_NSEC3_NAME).arg(rrset->getName());
  184. isc_throw(AddError, "Invalid NSEC3 owner name: " <<
  185. rrset->getName() << "; zone: " << zone_name_);
  186. }
  187. }
  188. const NSEC3Hash*
  189. ZoneDataUpdater::getNSEC3Hash() {
  190. if (hash_ == NULL) {
  191. NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
  192. // This should never be NULL in this codepath.
  193. assert(nsec3_data != NULL);
  194. hash_ = NSEC3Hash::create(nsec3_data->hashalg,
  195. nsec3_data->iterations,
  196. nsec3_data->getSaltData(),
  197. nsec3_data->getSaltLen());
  198. }
  199. return (hash_);
  200. }
  201. template <typename T>
  202. void
  203. ZoneDataUpdater::setupNSEC3(const ConstRRsetPtr rrset) {
  204. // We know rrset has exactly one RDATA
  205. const T& nsec3_rdata =
  206. dynamic_cast<const T&>(
  207. rrset->getRdataIterator()->getCurrent());
  208. NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
  209. if (nsec3_data == NULL) {
  210. nsec3_data = NSEC3Data::create(mem_sgmt_, zone_name_, nsec3_rdata);
  211. zone_data_->setNSEC3Data(nsec3_data);
  212. zone_data_->setSigned(true);
  213. } else {
  214. const NSEC3Hash* hash = getNSEC3Hash();
  215. if (!hash->match(nsec3_rdata)) {
  216. isc_throw(AddError,
  217. rrset->getType() << " with inconsistent parameters: "
  218. << rrset->toText());
  219. }
  220. }
  221. }
  222. void
  223. ZoneDataUpdater::addNSEC3(const Name& name, const ConstRRsetPtr& rrset,
  224. const ConstRRsetPtr& rrsig)
  225. {
  226. if (rrset) {
  227. setupNSEC3<generic::NSEC3>(rrset);
  228. }
  229. NSEC3Data* nsec3_data = zone_data_->getNSEC3Data();
  230. if (nsec3_data == NULL) {
  231. // This is some tricky case: an RRSIG for NSEC3 is given without the
  232. // covered NSEC3, and we don't even know any NSEC3 related data.
  233. // This situation is not necessarily broken, but in our current
  234. // implementation it's very difficult to deal with. So we reject it;
  235. // hopefully this case shouldn't happen in practice, at least unless
  236. // zone is really broken.
  237. assert(!rrset);
  238. isc_throw(NotImplemented,
  239. "RRSIG for NSEC3 cannot be added - no known NSEC3 data");
  240. }
  241. ZoneNode* node;
  242. nsec3_data->insertName(mem_sgmt_, name, &node);
  243. // Create a new RdataSet, merging any existing NSEC3 data for this
  244. // name.
  245. RdataSet* old_rdataset = node->getData();
  246. RdataSet* rdataset = RdataSet::create(mem_sgmt_, encoder_, rrset, rrsig,
  247. old_rdataset);
  248. old_rdataset = node->setData(rdataset);
  249. if (old_rdataset != NULL) {
  250. RdataSet::destroy(mem_sgmt_, old_rdataset, rrclass_);
  251. }
  252. }
  253. void
  254. ZoneDataUpdater::addRdataSet(const Name& name, const RRType& rrtype,
  255. const ConstRRsetPtr& rrset,
  256. const ConstRRsetPtr& rrsig)
  257. {
  258. if (rrtype == RRType::NSEC3()) {
  259. addNSEC3(name, rrset, rrsig);
  260. } else {
  261. ZoneNode* node;
  262. zone_data_->insertName(mem_sgmt_, name, &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. if (rrset) { // this check is only for covered RRset, not RRSIG
  269. contextCheck(*rrset, rdataset_head);
  270. }
  271. // Create a new RdataSet, merging any existing data for this
  272. // type.
  273. RdataSet* old_rdataset = RdataSet::find(rdataset_head, rrtype, true);
  274. RdataSet* rdataset_new = RdataSet::create(mem_sgmt_, encoder_,
  275. rrset, rrsig, old_rdataset);
  276. if (old_rdataset == NULL) {
  277. // There is no existing RdataSet. Prepend the new RdataSet
  278. // to the list.
  279. rdataset_new->next = rdataset_head;
  280. node->setData(rdataset_new);
  281. } else {
  282. // Replace the old RdataSet in the list with the newly
  283. // created one, and destroy the old one.
  284. for (RdataSet* cur = rdataset_head, *prev = NULL;
  285. cur != NULL;
  286. prev = cur, cur = cur->getNext()) {
  287. if (cur == old_rdataset) {
  288. rdataset_new->next = cur->getNext();
  289. if (prev == NULL) {
  290. node->setData(rdataset_new);
  291. } else {
  292. prev->next = rdataset_new;
  293. }
  294. break;
  295. }
  296. }
  297. RdataSet::destroy(mem_sgmt_, old_rdataset, rrclass_);
  298. }
  299. // Ok, we just put it in.
  300. // Convenient (and more efficient) shortcut to check RRsets at origin
  301. const bool is_origin = (node == zone_data_->getOriginNode());
  302. // If this RRset creates a zone cut at this node, mark the node
  303. // indicating the need for callback in find(). Note that we do this
  304. // only when non RRSIG RRset of that type is added.
  305. if (rrset && rrtype == RRType::NS() && !is_origin) {
  306. node->setFlag(ZoneNode::FLAG_CALLBACK);
  307. // If it is DNAME, we have a callback as well here
  308. } else if (rrset && rrtype == RRType::DNAME()) {
  309. node->setFlag(ZoneNode::FLAG_CALLBACK);
  310. }
  311. // If we've added NSEC3PARAM at zone origin, set up NSEC3
  312. // specific data or check consistency with already set up
  313. // parameters.
  314. if (rrset && rrtype == RRType::NSEC3PARAM() && is_origin) {
  315. setupNSEC3<generic::NSEC3PARAM>(rrset);
  316. } else if (rrset && rrtype == RRType::NSEC() && is_origin) {
  317. // If it is NSEC signed zone, we mark the zone as signed
  318. // (conceptually "signed" is a broader notion but our
  319. // current zone finder implementation regards "signed" as
  320. // "NSEC signed")
  321. zone_data_->setSigned(true);
  322. }
  323. // If we are adding a new SOA at the origin, update zone's min TTL.
  324. // Note: if the input is broken and contains multiple SOAs, the load
  325. // or update will be rejected higher level. We just always (though
  326. // this should be only once in normal cases) update the TTL.
  327. if (rrset && rrtype == RRType::SOA() && is_origin) {
  328. // Our own validation ensures the RRset is not empty.
  329. zone_data_->setMinTTL(
  330. dynamic_cast<const generic::SOA&>(
  331. rrset->getRdataIterator()->getCurrent()).getMinimum());
  332. }
  333. }
  334. }
  335. void
  336. ZoneDataUpdater::addInternal(const isc::dns::Name& name,
  337. const isc::dns::RRType& rrtype,
  338. const isc::dns::ConstRRsetPtr& rrset,
  339. const isc::dns::ConstRRsetPtr& rrsig)
  340. {
  341. // Add wildcards possibly contained in the owner name to the domain
  342. // tree. This can only happen for the normal (non-NSEC3) tree.
  343. // Note: this can throw an exception, breaking strong exception
  344. // guarantee. (see also the note for the call to contextCheck()
  345. // above).
  346. if (rrtype != RRType::NSEC3()) {
  347. addWildcards(name);
  348. }
  349. addRdataSet(name, rrtype, rrset, rrsig);
  350. }
  351. void
  352. ZoneDataUpdater::add(const ConstRRsetPtr& rrset,
  353. const ConstRRsetPtr& sig_rrset)
  354. {
  355. // Validate input.
  356. if (!rrset && !sig_rrset) {
  357. isc_throw(NullRRset,
  358. "ZoneDataUpdater::add is given 2 NULL pointers");
  359. }
  360. if (rrset) {
  361. validate(rrset);
  362. }
  363. if (sig_rrset) {
  364. validate(sig_rrset);
  365. }
  366. const Name& name = rrset ? rrset->getName() : sig_rrset->getName();
  367. const RRType& rrtype = rrset ? rrset->getType() :
  368. getCoveredType(sig_rrset);
  369. // OK, can add the RRset.
  370. LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_MEMORY_MEM_ADD_RRSET).arg(name).
  371. arg(rrset ? rrtype.toText() : "RRSIG(" + rrtype.toText() + ")").
  372. arg(zone_name_);
  373. // Store the address, it may change during growth and the address inside
  374. // would get updated.
  375. bool added = false;
  376. do {
  377. try {
  378. addInternal(name, rrtype, rrset, sig_rrset);
  379. added = true;
  380. } catch (const isc::util::MemorySegmentGrown&) {
  381. // The segment has grown. So, we update the base pointer (because
  382. // the data may have been remapped somewhere else in the process).
  383. zone_data_ =
  384. static_cast<ZoneData*>(
  385. mem_sgmt_.getNamedAddress("updater_zone_data"));
  386. }
  387. // Retry if it didn't add due to the growth
  388. } while (!added);
  389. }
  390. } // namespace memory
  391. } // namespace datasrc
  392. } // namespace isc