database.cc 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940
  1. // Copyright (C) 2011 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 <string>
  15. #include <vector>
  16. #include <datasrc/database.h>
  17. #include <datasrc/data_source.h>
  18. #include <datasrc/iterator.h>
  19. #include <exceptions/exceptions.h>
  20. #include <dns/name.h>
  21. #include <dns/rrclass.h>
  22. #include <dns/rrttl.h>
  23. #include <dns/rrset.h>
  24. #include <dns/rdata.h>
  25. #include <dns/rdataclass.h>
  26. #include <datasrc/data_source.h>
  27. #include <datasrc/logger.h>
  28. #include <boost/foreach.hpp>
  29. using namespace isc::dns;
  30. using namespace std;
  31. using boost::shared_ptr;
  32. using namespace isc::dns::rdata;
  33. namespace isc {
  34. namespace datasrc {
  35. DatabaseClient::DatabaseClient(RRClass rrclass,
  36. boost::shared_ptr<DatabaseAccessor>
  37. accessor) :
  38. rrclass_(rrclass), accessor_(accessor)
  39. {
  40. if (!accessor_) {
  41. isc_throw(isc::InvalidParameter,
  42. "No database provided to DatabaseClient");
  43. }
  44. }
  45. DataSourceClient::FindResult
  46. DatabaseClient::findZone(const Name& name) const {
  47. std::pair<bool, int> zone(accessor_->getZone(name.toText()));
  48. // Try exact first
  49. if (zone.first) {
  50. return (FindResult(result::SUCCESS,
  51. ZoneFinderPtr(new Finder(accessor_,
  52. zone.second, name))));
  53. }
  54. // Then super domains
  55. // Start from 1, as 0 is covered above
  56. for (size_t i(1); i < name.getLabelCount(); ++i) {
  57. isc::dns::Name superdomain(name.split(i));
  58. zone = accessor_->getZone(superdomain.toText());
  59. if (zone.first) {
  60. return (FindResult(result::PARTIALMATCH,
  61. ZoneFinderPtr(new Finder(accessor_,
  62. zone.second,
  63. superdomain))));
  64. }
  65. }
  66. // No, really nothing
  67. return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
  68. }
  69. DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
  70. int zone_id, const isc::dns::Name& origin) :
  71. accessor_(accessor),
  72. zone_id_(zone_id),
  73. origin_(origin)
  74. { }
  75. namespace {
  76. // Adds the given Rdata to the given RRset
  77. // If the rrset is an empty pointer, a new one is
  78. // created with the given name, class, type and ttl
  79. // The type is checked if the rrset exists, but the
  80. // name is not.
  81. //
  82. // Then adds the given rdata to the set
  83. //
  84. // Raises a DataSourceError if the type does not
  85. // match, or if the given rdata string does not
  86. // parse correctly for the given type and class
  87. //
  88. // The DatabaseAccessor is passed to print the
  89. // database name in the log message if the TTL is
  90. // modified
  91. void addOrCreate(isc::dns::RRsetPtr& rrset,
  92. const isc::dns::Name& name,
  93. const isc::dns::RRClass& cls,
  94. const isc::dns::RRType& type,
  95. const isc::dns::RRTTL& ttl,
  96. const std::string& rdata_str,
  97. const DatabaseAccessor& db
  98. )
  99. {
  100. if (!rrset) {
  101. rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
  102. } else {
  103. // This is a check to make sure find() is not messing things up
  104. assert(type == rrset->getType());
  105. if (ttl != rrset->getTTL()) {
  106. if (ttl < rrset->getTTL()) {
  107. rrset->setTTL(ttl);
  108. }
  109. logger.warn(DATASRC_DATABASE_FIND_TTL_MISMATCH)
  110. .arg(db.getDBName()).arg(name).arg(cls)
  111. .arg(type).arg(rrset->getTTL());
  112. }
  113. }
  114. try {
  115. rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
  116. } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
  117. // at this point, rrset may have been initialised for no reason,
  118. // and won't be used. But the caller would drop the shared_ptr
  119. // on such an error anyway, so we don't care.
  120. isc_throw(DataSourceError,
  121. "bad rdata in database for " << name << " "
  122. << type << ": " << ivrt.what());
  123. }
  124. }
  125. // This class keeps a short-lived store of RRSIG records encountered
  126. // during a call to find(). If the backend happens to return signatures
  127. // before the actual data, we might not know which signatures we will need
  128. // So if they may be relevant, we store the in this class.
  129. //
  130. // (If this class seems useful in other places, we might want to move
  131. // it to util. That would also provide an opportunity to add unit tests)
  132. class RRsigStore {
  133. public:
  134. // Adds the given signature Rdata to the store
  135. // The signature rdata MUST be of the RRSIG rdata type
  136. // (the caller must make sure of this).
  137. // NOTE: if we move this class to a public namespace,
  138. // we should add a type_covered argument, so as not
  139. // to have to do this cast here.
  140. void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
  141. const isc::dns::RRType& type_covered =
  142. static_cast<isc::dns::rdata::generic::RRSIG*>(
  143. sig_rdata.get())->typeCovered();
  144. sigs[type_covered].push_back(sig_rdata);
  145. }
  146. // If the store contains signatures for the type of the given
  147. // rrset, they are appended to it.
  148. void appendSignatures(isc::dns::RRsetPtr& rrset) const {
  149. std::map<isc::dns::RRType,
  150. std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
  151. found = sigs.find(rrset->getType());
  152. if (found != sigs.end()) {
  153. BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
  154. rrset->addRRsig(sig);
  155. }
  156. }
  157. }
  158. private:
  159. std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
  160. };
  161. }
  162. DatabaseClient::Finder::FoundRRsets
  163. DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
  164. bool check_ns, const string* construct_name)
  165. {
  166. RRsigStore sig_store;
  167. bool records_found = false;
  168. std::map<RRType, RRsetPtr> result;
  169. // Request the context
  170. DatabaseAccessor::IteratorContextPtr
  171. context(accessor_->getRecords(name, zone_id_));
  172. // It must not return NULL, that's a bug of the implementation
  173. if (!context) {
  174. isc_throw(isc::Unexpected, "Iterator context null at " + name);
  175. }
  176. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  177. if (construct_name == NULL) {
  178. construct_name = &name;
  179. }
  180. const Name construct_name_object(*construct_name);
  181. bool seen_cname(false);
  182. bool seen_ds(false);
  183. bool seen_other(false);
  184. bool seen_ns(false);
  185. while (context->getNext(columns)) {
  186. // The domain is not empty
  187. records_found = true;
  188. try {
  189. const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
  190. if (cur_type == RRType::RRSIG()) {
  191. // If we get signatures before we get the actual data, we
  192. // can't know which ones to keep and which to drop...
  193. // So we keep a separate store of any signature that may be
  194. // relevant and add them to the final RRset when we are
  195. // done.
  196. // A possible optimization here is to not store them for
  197. // types we are certain we don't need
  198. sig_store.addSig(rdata::createRdata(cur_type, getClass(),
  199. columns[DatabaseAccessor::RDATA_COLUMN]));
  200. }
  201. if (types.find(cur_type) != types.end()) {
  202. // This type is requested, so put it into result
  203. const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
  204. // Ths sigtype column was an optimization for finding the
  205. // relevant RRSIG RRs for a lookup. Currently this column is
  206. // not used in this revised datasource implementation. We
  207. // should either start using it again, or remove it from use
  208. // completely (i.e. also remove it from the schema and the
  209. // backend implementation).
  210. // Note that because we don't use it now, we also won't notice
  211. // it if the value is wrong (i.e. if the sigtype column
  212. // contains an rrtype that is different from the actual value
  213. // of the 'type covered' field in the RRSIG Rdata).
  214. //cur_sigtype(columns[SIGTYPE_COLUMN]);
  215. addOrCreate(result[cur_type], construct_name_object,
  216. getClass(), cur_type, cur_ttl,
  217. columns[DatabaseAccessor::RDATA_COLUMN],
  218. *accessor_);
  219. }
  220. if (cur_type == RRType::CNAME()) {
  221. seen_cname = true;
  222. } else if (cur_type == RRType::NS()) {
  223. seen_ns = true;
  224. } else if (cur_type == RRType::DS()) {
  225. seen_ds = true;
  226. } else if (cur_type != RRType::RRSIG() &&
  227. cur_type != RRType::NSEC3() &&
  228. cur_type != RRType::NSEC()) {
  229. // NSEC and RRSIG can coexist with anything, otherwise
  230. // we've seen something that can't live together with potential
  231. // CNAME or NS
  232. //
  233. // NSEC3 lives in separate namespace from everything, therefore
  234. // we just ignore it here for these checks as well.
  235. seen_other = true;
  236. }
  237. } catch (const InvalidRRType&) {
  238. isc_throw(DataSourceError, "Invalid RRType in database for " <<
  239. name << ": " << columns[DatabaseAccessor::
  240. TYPE_COLUMN]);
  241. } catch (const InvalidRRTTL&) {
  242. isc_throw(DataSourceError, "Invalid TTL in database for " <<
  243. name << ": " << columns[DatabaseAccessor::
  244. TTL_COLUMN]);
  245. } catch (const rdata::InvalidRdataText&) {
  246. isc_throw(DataSourceError, "Invalid rdata in database for " <<
  247. name << ": " << columns[DatabaseAccessor::
  248. RDATA_COLUMN]);
  249. }
  250. }
  251. if (seen_cname && (seen_other || seen_ns || seen_ds)) {
  252. isc_throw(DataSourceError, "CNAME shares domain " << name <<
  253. " with something else");
  254. }
  255. if (check_ns && seen_ns && seen_other) {
  256. isc_throw(DataSourceError, "NS shares domain " << name <<
  257. " with something else");
  258. }
  259. // Add signatures to all found RRsets
  260. for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
  261. i != result.end(); ++ i) {
  262. sig_store.appendSignatures(i->second);
  263. }
  264. return (FoundRRsets(records_found, result));
  265. }
  266. bool
  267. DatabaseClient::Finder::hasSubdomains(const std::string& name) {
  268. // Request the context
  269. DatabaseAccessor::IteratorContextPtr
  270. context(accessor_->getRecords(name, zone_id_, true));
  271. // It must not return NULL, that's a bug of the implementation
  272. if (!context) {
  273. isc_throw(isc::Unexpected, "Iterator context null at " + name);
  274. }
  275. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  276. return (context->getNext(columns));
  277. }
  278. // Some manipulation with RRType sets
  279. namespace {
  280. // To conveniently put the RRTypes into the sets. This is not really clean
  281. // design, but it is hidden inside this file and makes the calls much more
  282. // convenient.
  283. //
  284. // While this is not straightforward use of the + operator, some mathematical
  285. // conventions do allow summing sets with elements (usually in commutative
  286. // way, we define only one order of the operator parameters, as the other
  287. // isn't used).
  288. //
  289. // This arguably produces more readable code than having bunch of proxy
  290. // functions and set.insert calls scattered through the code.
  291. std::set<RRType> operator +(std::set<RRType> set, const RRType& type) {
  292. set.insert(type);
  293. return (set);
  294. }
  295. }
  296. ZoneFinder::FindResult
  297. DatabaseClient::Finder::find(const isc::dns::Name& name,
  298. const isc::dns::RRType& type,
  299. isc::dns::RRsetList*,
  300. const FindOptions options)
  301. {
  302. // This variable is used to determine the difference between
  303. // NXDOMAIN and NXRRSET
  304. bool records_found = false;
  305. bool glue_ok((options & FIND_GLUE_OK) != 0);
  306. const bool dnssec_data((options & FIND_DNSSEC) != 0);
  307. bool get_cover(false);
  308. isc::dns::RRsetPtr result_rrset;
  309. ZoneFinder::Result result_status = SUCCESS;
  310. FoundRRsets found;
  311. logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
  312. .arg(accessor_->getDBName()).arg(name).arg(type);
  313. // In case we are in GLUE_OK mode and start matching wildcards,
  314. // we can't do it under NS, so we store it here to check
  315. isc::dns::RRsetPtr first_ns;
  316. // This is used at multiple places
  317. static const WantedTypes nsec_types(WantedTypes() + RRType::NSEC());
  318. // First, do we have any kind of delegation (NS/DNAME) here?
  319. const Name origin(getOrigin());
  320. const size_t origin_label_count(origin.getLabelCount());
  321. // Number of labels in the last known non-empty domain
  322. size_t last_known(origin_label_count);
  323. const size_t current_label_count(name.getLabelCount());
  324. // This is how many labels we remove to get origin
  325. size_t remove_labels(current_label_count - origin_label_count);
  326. // Type shortcut, used a lot here
  327. typedef std::map<RRType, RRsetPtr>::const_iterator FoundIterator;
  328. // Now go trough all superdomains from origin down
  329. for (int i(remove_labels); i > 0; --i) {
  330. Name superdomain(name.split(i));
  331. // Look if there's NS or DNAME (but ignore the NS in origin)
  332. static const WantedTypes delegation_types(WantedTypes() +
  333. RRType::DNAME() +
  334. RRType::NS());
  335. found = getRRsets(superdomain.toText(), delegation_types,
  336. i != remove_labels);
  337. if (found.first) {
  338. // It contains some RRs, so it exists.
  339. last_known = superdomain.getLabelCount();
  340. const FoundIterator nsi(found.second.find(RRType::NS()));
  341. const FoundIterator dni(found.second.find(RRType::DNAME()));
  342. // In case we are in GLUE_OK mode, we want to store the
  343. // highest encountered NS (but not apex)
  344. if (glue_ok && !first_ns && i != remove_labels &&
  345. nsi != found.second.end()) {
  346. first_ns = nsi->second;
  347. } else if (!glue_ok && i != remove_labels &&
  348. nsi != found.second.end()) {
  349. // Do a NS delegation, but ignore NS in glue_ok mode. Ignore
  350. // delegation in apex
  351. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  352. DATASRC_DATABASE_FOUND_DELEGATION).
  353. arg(accessor_->getDBName()).arg(superdomain);
  354. result_rrset = nsi->second;
  355. result_status = DELEGATION;
  356. // No need to go lower, found
  357. break;
  358. } else if (dni != found.second.end()) {
  359. // Very similar with DNAME
  360. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  361. DATASRC_DATABASE_FOUND_DNAME).
  362. arg(accessor_->getDBName()).arg(superdomain);
  363. result_rrset = dni->second;
  364. result_status = DNAME;
  365. if (result_rrset->getRdataCount() != 1) {
  366. isc_throw(DataSourceError, "DNAME at " << superdomain <<
  367. " has " << result_rrset->getRdataCount() <<
  368. " rdata, 1 expected");
  369. }
  370. break;
  371. }
  372. }
  373. }
  374. if (!result_rrset) { // Only if we didn't find a redirect already
  375. // Try getting the final result and extract it
  376. // It is special if there's a CNAME or NS, DNAME is ignored here
  377. // And we don't consider the NS in origin
  378. static const WantedTypes final_types(WantedTypes() + RRType::CNAME() +
  379. RRType::NS() + RRType::NSEC());
  380. found = getRRsets(name.toText(), final_types + type, name != origin);
  381. records_found = found.first;
  382. // NS records, CNAME record and Wanted Type records
  383. const FoundIterator nsi(found.second.find(RRType::NS()));
  384. const FoundIterator cni(found.second.find(RRType::CNAME()));
  385. const FoundIterator wti(found.second.find(type));
  386. if (name != origin && !glue_ok && nsi != found.second.end()) {
  387. // There's a delegation at the exact node.
  388. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  389. DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
  390. arg(accessor_->getDBName()).arg(name);
  391. result_status = DELEGATION;
  392. result_rrset = nsi->second;
  393. } else if (type != isc::dns::RRType::CNAME() &&
  394. cni != found.second.end()) {
  395. // A CNAME here
  396. result_status = CNAME;
  397. result_rrset = cni->second;
  398. if (result_rrset->getRdataCount() != 1) {
  399. isc_throw(DataSourceError, "CNAME with " <<
  400. result_rrset->getRdataCount() <<
  401. " rdata at " << name << ", expected 1");
  402. }
  403. } else if (wti != found.second.end()) {
  404. // Just get the answer
  405. result_rrset = wti->second;
  406. } else if (!records_found) {
  407. // Nothing lives here.
  408. // But check if something lives below this
  409. // domain and if so, pretend something is here as well.
  410. if (hasSubdomains(name.toText())) {
  411. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  412. DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
  413. arg(accessor_->getDBName()).arg(name);
  414. records_found = true;
  415. get_cover = dnssec_data;
  416. } else {
  417. // It's not empty non-terminal. So check for wildcards.
  418. // We remove labels one by one and look for the wildcard there.
  419. // Go up to first non-empty domain.
  420. remove_labels = current_label_count - last_known;
  421. for (size_t i(1); i <= remove_labels; ++ i) {
  422. // Construct the name with *
  423. const Name superdomain(name.split(i));
  424. const string wildcard("*." + superdomain.toText());
  425. const string construct_name(name.toText());
  426. // TODO What do we do about DNAME here?
  427. static const WantedTypes wildcard_types(WantedTypes() +
  428. RRType::CNAME() +
  429. RRType::NS() +
  430. RRType::NSEC());
  431. found = getRRsets(wildcard, wildcard_types + type, true,
  432. &construct_name);
  433. if (found.first) {
  434. if (first_ns) {
  435. // In case we are under NS, we don't
  436. // wildcard-match, but return delegation
  437. result_rrset = first_ns;
  438. result_status = DELEGATION;
  439. records_found = true;
  440. // We pretend to switch to non-glue_ok mode
  441. glue_ok = false;
  442. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  443. DATASRC_DATABASE_WILDCARD_CANCEL_NS).
  444. arg(accessor_->getDBName()).arg(wildcard).
  445. arg(first_ns->getName());
  446. } else if (!hasSubdomains(name.split(i - 1).toText()))
  447. {
  448. // Nothing we added as part of the * can exist
  449. // directly, as we go up only to first existing
  450. // domain, but it could be empty non-terminal. In
  451. // that case, we need to cancel the match.
  452. records_found = true;
  453. const FoundIterator
  454. cni(found.second.find(RRType::CNAME()));
  455. const FoundIterator
  456. nsi(found.second.find(RRType::NS()));
  457. const FoundIterator
  458. nci(found.second.find(RRType::NSEC()));
  459. const FoundIterator wti(found.second.find(type));
  460. if (cni != found.second.end() &&
  461. type != RRType::CNAME()) {
  462. result_rrset = cni->second;
  463. result_status = CNAME;
  464. } else if (nsi != found.second.end()) {
  465. result_rrset = nsi->second;
  466. result_status = DELEGATION;
  467. } else if (wti != found.second.end()) {
  468. result_rrset = wti->second;
  469. result_status = WILDCARD;
  470. } else {
  471. // NXRRSET case in the wildcard
  472. result_status = WILDCARD_NXRRSET;
  473. if (dnssec_data &&
  474. nci != found.second.end()) {
  475. // User wants a proof the wildcard doesn't
  476. // contain it
  477. //
  478. // However, we need to get the RRset in the
  479. // name of the wildcard, not the constructed
  480. // one, so we walk it again
  481. found = getRRsets(wildcard, nsec_types,
  482. true);
  483. result_rrset =
  484. found.second.find(RRType::NSEC())->
  485. second;
  486. }
  487. }
  488. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  489. DATASRC_DATABASE_WILDCARD).
  490. arg(accessor_->getDBName()).arg(wildcard).
  491. arg(name);
  492. } else {
  493. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  494. DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
  495. arg(accessor_->getDBName()).arg(wildcard).
  496. arg(name).arg(superdomain);
  497. }
  498. break;
  499. } else if (hasSubdomains(wildcard)) {
  500. // Empty non-terminal asterisk
  501. records_found = true;
  502. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  503. DATASRC_DATABASE_WILDCARD_EMPTY).
  504. arg(accessor_->getDBName()).arg(wildcard).
  505. arg(name);
  506. if (dnssec_data) {
  507. // Which one should contain the NSEC record?
  508. const Name
  509. coverName(findPreviousName(Name(wildcard)));
  510. // Get the record and copy it out
  511. found = getRRsets(coverName.toText(), nsec_types,
  512. true);
  513. const FoundIterator
  514. nci(found.second.find(RRType::NSEC()));
  515. if (nci != found.second.end()) {
  516. result_status = WILDCARD_NXRRSET;
  517. result_rrset = nci->second;
  518. } else {
  519. // The previous doesn't contain NSEC, bug?
  520. isc_throw(DataSourceError, "No NSEC in " +
  521. coverName.toText() + ", but it was "
  522. "returned as previous - "
  523. "accessor error?");
  524. }
  525. }
  526. break;
  527. }
  528. }
  529. // This is the NXDOMAIN case (nothing found anywhere). If
  530. // they want DNSSEC data, try getting the NSEC record
  531. if (dnssec_data && !records_found) {
  532. get_cover = true;
  533. }
  534. }
  535. } else if (dnssec_data) {
  536. // This is the "usual" NXRRSET case
  537. // So in case they want DNSSEC, provide the NSEC
  538. // (which should be available already here)
  539. result_status = NXRRSET;
  540. const FoundIterator nci(found.second.find(RRType::NSEC()));
  541. if (nci != found.second.end()) {
  542. result_rrset = nci->second;
  543. }
  544. }
  545. }
  546. if (!result_rrset) {
  547. if (result_status == SUCCESS) {
  548. // Should we look for NSEC covering the name?
  549. if (get_cover) {
  550. try {
  551. // Which one should contain the NSEC record?
  552. const Name coverName(findPreviousName(name));
  553. // Get the record and copy it out
  554. found = getRRsets(coverName.toText(), nsec_types, true);
  555. const FoundIterator
  556. nci(found.second.find(RRType::NSEC()));
  557. if (nci != found.second.end()) {
  558. result_status = NXDOMAIN;
  559. result_rrset = nci->second;
  560. } else {
  561. // The previous doesn't contain NSEC, bug?
  562. isc_throw(DataSourceError, "No NSEC in " +
  563. coverName.toText() + ", but it was "
  564. "returned as previous - "
  565. "accessor error?");
  566. }
  567. }
  568. catch (const isc::NotImplemented&) {
  569. // Well, they want DNSSEC, but there is no available.
  570. // So we don't provide anything.
  571. LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
  572. arg(accessor_->getDBName()).arg(name);
  573. }
  574. }
  575. // Something is not here and we didn't decide yet what
  576. if (records_found) {
  577. logger.debug(DBG_TRACE_DETAILED,
  578. DATASRC_DATABASE_FOUND_NXRRSET)
  579. .arg(accessor_->getDBName()).arg(name)
  580. .arg(getClass()).arg(type);
  581. result_status = NXRRSET;
  582. } else {
  583. logger.debug(DBG_TRACE_DETAILED,
  584. DATASRC_DATABASE_FOUND_NXDOMAIN)
  585. .arg(accessor_->getDBName()).arg(name)
  586. .arg(getClass()).arg(type);
  587. result_status = NXDOMAIN;
  588. }
  589. }
  590. } else {
  591. logger.debug(DBG_TRACE_DETAILED,
  592. DATASRC_DATABASE_FOUND_RRSET)
  593. .arg(accessor_->getDBName()).arg(*result_rrset);
  594. }
  595. return (FindResult(result_status, result_rrset));
  596. }
  597. Name
  598. DatabaseClient::Finder::findPreviousName(const Name& name) const {
  599. const string str(accessor_->findPreviousName(zone_id_,
  600. name.reverse().toText()));
  601. try {
  602. return (Name(str));
  603. }
  604. /*
  605. * To avoid having the same code many times, we just catch all the
  606. * exceptions and handle them in a common code below
  607. */
  608. catch (const isc::dns::EmptyLabel&) {}
  609. catch (const isc::dns::TooLongLabel&) {}
  610. catch (const isc::dns::BadLabelType&) {}
  611. catch (const isc::dns::BadEscape&) {}
  612. catch (const isc::dns::TooLongName&) {}
  613. catch (const isc::dns::IncompleteName&) {}
  614. isc_throw(DataSourceError, "Bad name " + str + " from findPreviousName");
  615. }
  616. Name
  617. DatabaseClient::Finder::getOrigin() const {
  618. return (origin_);
  619. }
  620. isc::dns::RRClass
  621. DatabaseClient::Finder::getClass() const {
  622. // TODO Implement
  623. return isc::dns::RRClass::IN();
  624. }
  625. namespace {
  626. /*
  627. * This needs, beside of converting all data from textual representation, group
  628. * together rdata of the same RRsets. To do this, we hold one row of data ahead
  629. * of iteration. When we get a request to provide data, we create it from this
  630. * data and load a new one. If it is to be put to the same rrset, we add it.
  631. * Otherwise we just return what we have and keep the row as the one ahead
  632. * for next time.
  633. */
  634. class DatabaseIterator : public ZoneIterator {
  635. public:
  636. DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context,
  637. const RRClass& rrclass) :
  638. context_(context),
  639. class_(rrclass),
  640. ready_(true)
  641. {
  642. // Prepare data for the next time
  643. getData();
  644. }
  645. virtual isc::dns::ConstRRsetPtr getNextRRset() {
  646. if (!ready_) {
  647. isc_throw(isc::Unexpected, "Iterating past the zone end");
  648. }
  649. if (!data_ready_) {
  650. // At the end of zone
  651. ready_ = false;
  652. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  653. DATASRC_DATABASE_ITERATE_END);
  654. return (ConstRRsetPtr());
  655. }
  656. string name_str(name_), rtype_str(rtype_), ttl(ttl_);
  657. Name name(name_str);
  658. RRType rtype(rtype_str);
  659. RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl)));
  660. while (data_ready_ && name_ == name_str && rtype_str == rtype_) {
  661. if (ttl_ != ttl) {
  662. if (ttl < ttl_) {
  663. ttl_ = ttl;
  664. rrset->setTTL(RRTTL(ttl));
  665. }
  666. LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
  667. arg(name_).arg(class_).arg(rtype_).arg(rrset->getTTL());
  668. }
  669. rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
  670. getData();
  671. }
  672. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
  673. arg(rrset->getName()).arg(rrset->getType());
  674. return (rrset);
  675. }
  676. private:
  677. // Load next row of data
  678. void getData() {
  679. string data[DatabaseAccessor::COLUMN_COUNT];
  680. data_ready_ = context_->getNext(data);
  681. name_ = data[DatabaseAccessor::NAME_COLUMN];
  682. rtype_ = data[DatabaseAccessor::TYPE_COLUMN];
  683. ttl_ = data[DatabaseAccessor::TTL_COLUMN];
  684. rdata_ = data[DatabaseAccessor::RDATA_COLUMN];
  685. }
  686. // The context
  687. const DatabaseAccessor::IteratorContextPtr context_;
  688. // Class of the zone
  689. RRClass class_;
  690. // Status
  691. bool ready_, data_ready_;
  692. // Data of the next row
  693. string name_, rtype_, rdata_, ttl_;
  694. };
  695. }
  696. ZoneIteratorPtr
  697. DatabaseClient::getIterator(const isc::dns::Name& name) const {
  698. // Get the zone
  699. std::pair<bool, int> zone(accessor_->getZone(name.toText()));
  700. if (!zone.first) {
  701. // No such zone, can't continue
  702. isc_throw(DataSourceError, "Zone " + name.toText() +
  703. " can not be iterated, because it doesn't exist "
  704. "in this data source");
  705. }
  706. // Request the context
  707. DatabaseAccessor::IteratorContextPtr
  708. context(accessor_->getAllRecords(zone.second));
  709. // It must not return NULL, that's a bug of the implementation
  710. if (context == DatabaseAccessor::IteratorContextPtr()) {
  711. isc_throw(isc::Unexpected, "Iterator context null at " +
  712. name.toText());
  713. }
  714. // Create the iterator and return it
  715. // TODO: Once #1062 is merged with this, we need to get the
  716. // actual zone class from the connection, as the DatabaseClient
  717. // doesn't know it and the iterator needs it (so it wouldn't query
  718. // it each time)
  719. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
  720. arg(name);
  721. return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN())));
  722. }
  723. //
  724. // Zone updater using some database system as the underlying data source.
  725. //
  726. class DatabaseUpdater : public ZoneUpdater {
  727. public:
  728. DatabaseUpdater(shared_ptr<DatabaseAccessor> accessor, int zone_id,
  729. const Name& zone_name, const RRClass& zone_class) :
  730. committed_(false), accessor_(accessor), zone_id_(zone_id),
  731. db_name_(accessor->getDBName()), zone_name_(zone_name.toText()),
  732. zone_class_(zone_class),
  733. finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name))
  734. {
  735. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED)
  736. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  737. }
  738. virtual ~DatabaseUpdater() {
  739. if (!committed_) {
  740. try {
  741. accessor_->rollbackUpdateZone();
  742. logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK)
  743. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  744. } catch (const DataSourceError& e) {
  745. // We generally expect that rollback always succeeds, and
  746. // it should in fact succeed in a way we execute it. But
  747. // as the public API allows rollbackUpdateZone() to fail and
  748. // throw, we should expect it. Obviously we cannot re-throw
  749. // it. The best we can do is to log it as a critical error.
  750. logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL)
  751. .arg(zone_name_).arg(zone_class_).arg(db_name_)
  752. .arg(e.what());
  753. }
  754. }
  755. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED)
  756. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  757. }
  758. virtual ZoneFinder& getFinder() { return (*finder_); }
  759. virtual void addRRset(const RRset& rrset);
  760. virtual void deleteRRset(const RRset& rrset);
  761. virtual void commit();
  762. private:
  763. bool committed_;
  764. shared_ptr<DatabaseAccessor> accessor_;
  765. const int zone_id_;
  766. const string db_name_;
  767. const string zone_name_;
  768. const RRClass zone_class_;
  769. boost::scoped_ptr<DatabaseClient::Finder> finder_;
  770. };
  771. void
  772. DatabaseUpdater::addRRset(const RRset& rrset) {
  773. if (committed_) {
  774. isc_throw(DataSourceError, "Add attempt after commit to zone: "
  775. << zone_name_ << "/" << zone_class_);
  776. }
  777. if (rrset.getClass() != zone_class_) {
  778. isc_throw(DataSourceError, "An RRset of a different class is being "
  779. << "added to " << zone_name_ << "/" << zone_class_ << ": "
  780. << rrset.toText());
  781. }
  782. if (rrset.getRRsig()) {
  783. isc_throw(DataSourceError, "An RRset with RRSIG is being added to "
  784. << zone_name_ << "/" << zone_class_ << ": "
  785. << rrset.toText());
  786. }
  787. RdataIteratorPtr it = rrset.getRdataIterator();
  788. if (it->isLast()) {
  789. isc_throw(DataSourceError, "An empty RRset is being added for "
  790. << rrset.getName() << "/" << zone_class_ << "/"
  791. << rrset.getType());
  792. }
  793. string columns[DatabaseAccessor::ADD_COLUMN_COUNT]; // initialized with ""
  794. columns[DatabaseAccessor::ADD_NAME] = rrset.getName().toText();
  795. columns[DatabaseAccessor::ADD_REV_NAME] =
  796. rrset.getName().reverse().toText();
  797. columns[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText();
  798. columns[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText();
  799. for (; !it->isLast(); it->next()) {
  800. if (rrset.getType() == RRType::RRSIG()) {
  801. // XXX: the current interface (based on the current sqlite3
  802. // data source schema) requires a separate "sigtype" column,
  803. // even though it won't be used in a newer implementation.
  804. // We should eventually clean up the schema design and simplify
  805. // the interface, but until then we have to conform to the schema.
  806. const generic::RRSIG& rrsig_rdata =
  807. dynamic_cast<const generic::RRSIG&>(it->getCurrent());
  808. columns[DatabaseAccessor::ADD_SIGTYPE] =
  809. rrsig_rdata.typeCovered().toText();
  810. }
  811. columns[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText();
  812. accessor_->addRecordToZone(columns);
  813. }
  814. }
  815. void
  816. DatabaseUpdater::deleteRRset(const RRset& rrset) {
  817. if (committed_) {
  818. isc_throw(DataSourceError, "Delete attempt after commit on zone: "
  819. << zone_name_ << "/" << zone_class_);
  820. }
  821. if (rrset.getClass() != zone_class_) {
  822. isc_throw(DataSourceError, "An RRset of a different class is being "
  823. << "deleted from " << zone_name_ << "/" << zone_class_
  824. << ": " << rrset.toText());
  825. }
  826. if (rrset.getRRsig()) {
  827. isc_throw(DataSourceError, "An RRset with RRSIG is being deleted from "
  828. << zone_name_ << "/" << zone_class_ << ": "
  829. << rrset.toText());
  830. }
  831. RdataIteratorPtr it = rrset.getRdataIterator();
  832. if (it->isLast()) {
  833. isc_throw(DataSourceError, "An empty RRset is being deleted for "
  834. << rrset.getName() << "/" << zone_class_ << "/"
  835. << rrset.getType());
  836. }
  837. string params[DatabaseAccessor::DEL_PARAM_COUNT]; // initialized with ""
  838. params[DatabaseAccessor::DEL_NAME] = rrset.getName().toText();
  839. params[DatabaseAccessor::DEL_TYPE] = rrset.getType().toText();
  840. for (; !it->isLast(); it->next()) {
  841. params[DatabaseAccessor::DEL_RDATA] = it->getCurrent().toText();
  842. accessor_->deleteRecordInZone(params);
  843. }
  844. }
  845. void
  846. DatabaseUpdater::commit() {
  847. if (committed_) {
  848. isc_throw(DataSourceError, "Duplicate commit attempt for "
  849. << zone_name_ << "/" << zone_class_ << " on "
  850. << db_name_);
  851. }
  852. accessor_->commitUpdateZone();
  853. committed_ = true; // make sure the destructor won't trigger rollback
  854. // We release the accessor immediately after commit is completed so that
  855. // we don't hold the possible internal resource any longer.
  856. accessor_.reset();
  857. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT)
  858. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  859. }
  860. // The updater factory
  861. ZoneUpdaterPtr
  862. DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const {
  863. shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
  864. const std::pair<bool, int> zone(update_accessor->startUpdateZone(
  865. name.toText(), replace));
  866. if (!zone.first) {
  867. return (ZoneUpdaterPtr());
  868. }
  869. return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second,
  870. name, rrclass_)));
  871. }
  872. }
  873. }