database.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  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. std::pair<bool, isc::dns::RRsetPtr>
  163. DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
  164. const isc::dns::RRType* type,
  165. bool want_cname, bool want_dname,
  166. bool want_ns,
  167. const isc::dns::Name* construct_name)
  168. {
  169. RRsigStore sig_store;
  170. bool records_found = false;
  171. isc::dns::RRsetPtr result_rrset;
  172. // Request the context
  173. DatabaseAccessor::IteratorContextPtr
  174. context(accessor_->getRecords(name.toText(), zone_id_));
  175. // It must not return NULL, that's a bug of the implementation
  176. if (!context) {
  177. isc_throw(isc::Unexpected, "Iterator context null at " +
  178. name.toText());
  179. }
  180. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  181. if (construct_name == NULL) {
  182. construct_name = &name;
  183. }
  184. while (context->getNext(columns)) {
  185. if (!records_found) {
  186. records_found = true;
  187. }
  188. try {
  189. const isc::dns::RRType cur_type(columns[DatabaseAccessor::
  190. TYPE_COLUMN]);
  191. const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
  192. TTL_COLUMN]);
  193. // Ths sigtype column was an optimization for finding the
  194. // relevant RRSIG RRs for a lookup. Currently this column is
  195. // not used in this revised datasource implementation. We
  196. // should either start using it again, or remove it from use
  197. // completely (i.e. also remove it from the schema and the
  198. // backend implementation).
  199. // Note that because we don't use it now, we also won't notice
  200. // it if the value is wrong (i.e. if the sigtype column
  201. // contains an rrtype that is different from the actual value
  202. // of the 'type covered' field in the RRSIG Rdata).
  203. //cur_sigtype(columns[SIGTYPE_COLUMN]);
  204. // Check for delegations before checking for the right type.
  205. // This is needed to properly delegate request for the NS
  206. // record itself.
  207. //
  208. // This happens with NS only, CNAME must be alone and DNAME
  209. // is not checked in the exact queried domain.
  210. if (want_ns && cur_type == isc::dns::RRType::NS()) {
  211. if (result_rrset &&
  212. result_rrset->getType() != isc::dns::RRType::NS()) {
  213. isc_throw(DataSourceError, "NS found together with data"
  214. " in non-apex domain " + name.toText());
  215. }
  216. addOrCreate(result_rrset, *construct_name, getClass(),
  217. cur_type, cur_ttl,
  218. columns[DatabaseAccessor::RDATA_COLUMN],
  219. *accessor_);
  220. } else if (type != NULL && cur_type == *type) {
  221. if (result_rrset &&
  222. result_rrset->getType() == isc::dns::RRType::CNAME()) {
  223. isc_throw(DataSourceError, "CNAME found but it is not "
  224. "the only record for " + name.toText());
  225. } else if (result_rrset && want_ns &&
  226. result_rrset->getType() == isc::dns::RRType::NS()) {
  227. isc_throw(DataSourceError, "NS found together with data"
  228. " in non-apex domain " + name.toText());
  229. }
  230. addOrCreate(result_rrset, *construct_name, getClass(),
  231. cur_type, cur_ttl,
  232. columns[DatabaseAccessor::RDATA_COLUMN],
  233. *accessor_);
  234. } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
  235. // There should be no other data, so result_rrset should
  236. // be empty.
  237. if (result_rrset) {
  238. isc_throw(DataSourceError, "CNAME found but it is not "
  239. "the only record for " + name.toText());
  240. }
  241. addOrCreate(result_rrset, *construct_name, getClass(),
  242. cur_type, cur_ttl,
  243. columns[DatabaseAccessor::RDATA_COLUMN],
  244. *accessor_);
  245. } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
  246. // There should be max one RR of DNAME present
  247. if (result_rrset &&
  248. result_rrset->getType() == isc::dns::RRType::DNAME()) {
  249. isc_throw(DataSourceError, "DNAME with multiple RRs in " +
  250. name.toText());
  251. }
  252. addOrCreate(result_rrset, *construct_name, getClass(),
  253. cur_type, cur_ttl,
  254. columns[DatabaseAccessor::RDATA_COLUMN],
  255. *accessor_);
  256. } else if (cur_type == isc::dns::RRType::RRSIG()) {
  257. // If we get signatures before we get the actual data, we
  258. // can't know which ones to keep and which to drop...
  259. // So we keep a separate store of any signature that may be
  260. // relevant and add them to the final RRset when we are
  261. // done.
  262. // A possible optimization here is to not store them for
  263. // types we are certain we don't need
  264. sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
  265. getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
  266. }
  267. } catch (const isc::dns::InvalidRRType& irt) {
  268. isc_throw(DataSourceError, "Invalid RRType in database for " <<
  269. name << ": " << columns[DatabaseAccessor::
  270. TYPE_COLUMN]);
  271. } catch (const isc::dns::InvalidRRTTL& irttl) {
  272. isc_throw(DataSourceError, "Invalid TTL in database for " <<
  273. name << ": " << columns[DatabaseAccessor::
  274. TTL_COLUMN]);
  275. } catch (const isc::dns::rdata::InvalidRdataText& ird) {
  276. isc_throw(DataSourceError, "Invalid rdata in database for " <<
  277. name << ": " << columns[DatabaseAccessor::
  278. RDATA_COLUMN]);
  279. }
  280. }
  281. if (result_rrset) {
  282. sig_store.appendSignatures(result_rrset);
  283. }
  284. return (std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset));
  285. }
  286. bool
  287. DatabaseClient::Finder::hasSubdomains(const std::string& name) {
  288. // Request the context
  289. DatabaseAccessor::IteratorContextPtr
  290. context(accessor_->getRecords(name, zone_id_, true));
  291. // It must not return NULL, that's a bug of the implementation
  292. if (!context) {
  293. isc_throw(isc::Unexpected, "Iterator context null at " + name);
  294. }
  295. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  296. return (context->getNext(columns));
  297. }
  298. ZoneFinder::FindResult
  299. DatabaseClient::Finder::find(const isc::dns::Name& name,
  300. const isc::dns::RRType& type,
  301. isc::dns::RRsetList*,
  302. const FindOptions options)
  303. {
  304. // This variable is used to determine the difference between
  305. // NXDOMAIN and NXRRSET
  306. bool records_found = false;
  307. bool glue_ok(options & FIND_GLUE_OK);
  308. isc::dns::RRsetPtr result_rrset;
  309. ZoneFinder::Result result_status = SUCCESS;
  310. std::pair<bool, isc::dns::RRsetPtr> 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. // First, do we have any kind of delegation (NS/DNAME) here?
  317. Name origin(getOrigin());
  318. size_t origin_label_count(origin.getLabelCount());
  319. // Number of labels in the last known non-empty domain
  320. size_t last_known(origin_label_count);
  321. size_t current_label_count(name.getLabelCount());
  322. // This is how many labels we remove to get origin
  323. size_t remove_labels(current_label_count - origin_label_count);
  324. // Now go trough all superdomains from origin down
  325. for (int i(remove_labels); i > 0; --i) {
  326. Name superdomain(name.split(i));
  327. // Look if there's NS or DNAME (but ignore the NS in origin)
  328. found = getRRset(superdomain, NULL, false, true,
  329. i != remove_labels && !glue_ok);
  330. if (found.first) {
  331. // It contains some RRs, so it exists.
  332. last_known = superdomain.getLabelCount();
  333. // In case we are in GLUE_OK, we want to store the highest
  334. // encountered RRset.
  335. if (glue_ok && !first_ns && i != remove_labels) {
  336. first_ns = getRRset(superdomain, NULL, false, false,
  337. true).second;
  338. }
  339. }
  340. if (found.second) {
  341. // We found something redirecting somewhere else
  342. // (it can be only NS or DNAME here)
  343. result_rrset = found.second;
  344. if (result_rrset->getType() == isc::dns::RRType::NS()) {
  345. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  346. DATASRC_DATABASE_FOUND_DELEGATION).
  347. arg(accessor_->getDBName()).arg(superdomain);
  348. result_status = DELEGATION;
  349. } else {
  350. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  351. DATASRC_DATABASE_FOUND_DNAME).
  352. arg(accessor_->getDBName()).arg(superdomain);
  353. result_status = DNAME;
  354. }
  355. // Don't search more
  356. break;
  357. }
  358. }
  359. if (!result_rrset) { // Only if we didn't find a redirect already
  360. // Try getting the final result and extract it
  361. // It is special if there's a CNAME or NS, DNAME is ignored here
  362. // And we don't consider the NS in origin
  363. found = getRRset(name, &type, true, false, name != origin && !glue_ok);
  364. records_found = found.first;
  365. result_rrset = found.second;
  366. if (result_rrset && name != origin && !glue_ok &&
  367. result_rrset->getType() == isc::dns::RRType::NS()) {
  368. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  369. DATASRC_DATABASE_FOUND_DELEGATION_EXACT).
  370. arg(accessor_->getDBName()).arg(name);
  371. result_status = DELEGATION;
  372. } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
  373. result_rrset->getType() == isc::dns::RRType::CNAME()) {
  374. result_status = CNAME;
  375. }
  376. if (!result_rrset && !records_found) {
  377. // Nothing lives here.
  378. // But check if something lives below this
  379. // domain and if so, pretend something is here as well.
  380. if (hasSubdomains(name.toText())) {
  381. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  382. DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
  383. arg(accessor_->getDBName()).arg(name);
  384. records_found = true;
  385. } else {
  386. // It's not empty non-terminal. So check for wildcards.
  387. // We remove labels one by one and look for the wildcard there.
  388. // Go up to first non-empty domain.
  389. remove_labels = current_label_count - last_known;
  390. Name star("*");
  391. for (size_t i(1); i <= remove_labels; ++ i) {
  392. // Construct the name with *
  393. // TODO: Once the underlying DatabaseAccessor takes
  394. // string, do the concatenation on strings, not
  395. // Names
  396. Name superdomain(name.split(i));
  397. Name wildcard(star.concatenate(superdomain));
  398. // TODO What do we do about DNAME here?
  399. found = getRRset(wildcard, &type, true, false, true,
  400. &name);
  401. if (found.first) {
  402. if (first_ns) {
  403. // In case we are under NS, we don't
  404. // wildcard-match, but return delegation
  405. result_rrset = first_ns;
  406. result_status = DELEGATION;
  407. records_found = true;
  408. // We pretend to switch to non-glue_ok mode
  409. glue_ok = false;
  410. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  411. DATASRC_DATABASE_WILDCARD_CANCEL_NS).
  412. arg(accessor_->getDBName()).arg(wildcard).
  413. arg(first_ns->getName());
  414. } else if (!hasSubdomains(name.split(i - 1).toText()))
  415. {
  416. // Nothing we added as part of the * can exist
  417. // directly, as we go up only to first existing
  418. // domain, but it could be empty non-terminal. In
  419. // that case, we need to cancel the match.
  420. records_found = true;
  421. result_rrset = found.second;
  422. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  423. DATASRC_DATABASE_WILDCARD).
  424. arg(accessor_->getDBName()).arg(wildcard).
  425. arg(name);
  426. } else {
  427. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  428. DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
  429. arg(accessor_->getDBName()).arg(wildcard).
  430. arg(name).arg(superdomain);
  431. }
  432. break;
  433. } else if (hasSubdomains(wildcard.toText())) {
  434. // Empty non-terminal asterisk
  435. records_found = true;
  436. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  437. DATASRC_DATABASE_WILDCARD_EMPTY).
  438. arg(accessor_->getDBName()).arg(wildcard).
  439. arg(name);
  440. break;
  441. }
  442. }
  443. }
  444. }
  445. }
  446. if (!result_rrset) {
  447. if (records_found) {
  448. logger.debug(DBG_TRACE_DETAILED,
  449. DATASRC_DATABASE_FOUND_NXRRSET)
  450. .arg(accessor_->getDBName()).arg(name)
  451. .arg(getClass()).arg(type);
  452. result_status = NXRRSET;
  453. } else {
  454. logger.debug(DBG_TRACE_DETAILED,
  455. DATASRC_DATABASE_FOUND_NXDOMAIN)
  456. .arg(accessor_->getDBName()).arg(name)
  457. .arg(getClass()).arg(type);
  458. result_status = NXDOMAIN;
  459. }
  460. } else {
  461. logger.debug(DBG_TRACE_DETAILED,
  462. DATASRC_DATABASE_FOUND_RRSET)
  463. .arg(accessor_->getDBName()).arg(*result_rrset);
  464. }
  465. return (FindResult(result_status, result_rrset));
  466. }
  467. Name
  468. DatabaseClient::Finder::getOrigin() const {
  469. return (origin_);
  470. }
  471. isc::dns::RRClass
  472. DatabaseClient::Finder::getClass() const {
  473. // TODO Implement
  474. return isc::dns::RRClass::IN();
  475. }
  476. namespace {
  477. /*
  478. * This needs, beside of converting all data from textual representation, group
  479. * together rdata of the same RRsets. To do this, we hold one row of data ahead
  480. * of iteration. When we get a request to provide data, we create it from this
  481. * data and load a new one. If it is to be put to the same rrset, we add it.
  482. * Otherwise we just return what we have and keep the row as the one ahead
  483. * for next time.
  484. */
  485. class DatabaseIterator : public ZoneIterator {
  486. public:
  487. DatabaseIterator(const DatabaseAccessor::IteratorContextPtr& context,
  488. const RRClass& rrclass) :
  489. context_(context),
  490. class_(rrclass),
  491. ready_(true)
  492. {
  493. // Prepare data for the next time
  494. getData();
  495. }
  496. virtual isc::dns::ConstRRsetPtr getNextRRset() {
  497. if (!ready_) {
  498. isc_throw(isc::Unexpected, "Iterating past the zone end");
  499. }
  500. if (!data_ready_) {
  501. // At the end of zone
  502. ready_ = false;
  503. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  504. DATASRC_DATABASE_ITERATE_END);
  505. return (ConstRRsetPtr());
  506. }
  507. string name_str(name_), rtype_str(rtype_), ttl(ttl_);
  508. Name name(name_str);
  509. RRType rtype(rtype_str);
  510. RRsetPtr rrset(new RRset(name, class_, rtype, RRTTL(ttl)));
  511. while (data_ready_ && name_ == name_str && rtype_str == rtype_) {
  512. if (ttl_ != ttl) {
  513. if (ttl < ttl_) {
  514. ttl_ = ttl;
  515. rrset->setTTL(RRTTL(ttl));
  516. }
  517. LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
  518. arg(name_).arg(class_).arg(rtype_).arg(rrset->getTTL());
  519. }
  520. rrset->addRdata(rdata::createRdata(rtype, class_, rdata_));
  521. getData();
  522. }
  523. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
  524. arg(rrset->getName()).arg(rrset->getType());
  525. return (rrset);
  526. }
  527. private:
  528. // Load next row of data
  529. void getData() {
  530. string data[DatabaseAccessor::COLUMN_COUNT];
  531. data_ready_ = context_->getNext(data);
  532. name_ = data[DatabaseAccessor::NAME_COLUMN];
  533. rtype_ = data[DatabaseAccessor::TYPE_COLUMN];
  534. ttl_ = data[DatabaseAccessor::TTL_COLUMN];
  535. rdata_ = data[DatabaseAccessor::RDATA_COLUMN];
  536. }
  537. // The context
  538. const DatabaseAccessor::IteratorContextPtr context_;
  539. // Class of the zone
  540. RRClass class_;
  541. // Status
  542. bool ready_, data_ready_;
  543. // Data of the next row
  544. string name_, rtype_, rdata_, ttl_;
  545. };
  546. }
  547. ZoneIteratorPtr
  548. DatabaseClient::getIterator(const isc::dns::Name& name) const {
  549. // Get the zone
  550. std::pair<bool, int> zone(accessor_->getZone(name.toText()));
  551. if (!zone.first) {
  552. // No such zone, can't continue
  553. isc_throw(DataSourceError, "Zone " + name.toText() +
  554. " can not be iterated, because it doesn't exist "
  555. "in this data source");
  556. }
  557. // Request the context
  558. DatabaseAccessor::IteratorContextPtr
  559. context(accessor_->getAllRecords(zone.second));
  560. // It must not return NULL, that's a bug of the implementation
  561. if (context == DatabaseAccessor::IteratorContextPtr()) {
  562. isc_throw(isc::Unexpected, "Iterator context null at " +
  563. name.toText());
  564. }
  565. // Create the iterator and return it
  566. // TODO: Once #1062 is merged with this, we need to get the
  567. // actual zone class from the connection, as the DatabaseClient
  568. // doesn't know it and the iterator needs it (so it wouldn't query
  569. // it each time)
  570. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
  571. arg(name);
  572. return (ZoneIteratorPtr(new DatabaseIterator(context, RRClass::IN())));
  573. }
  574. //
  575. // Zone updater using some database system as the underlying data source.
  576. //
  577. class DatabaseUpdater : public ZoneUpdater {
  578. public:
  579. DatabaseUpdater(shared_ptr<DatabaseAccessor> accessor, int zone_id,
  580. const Name& zone_name, const RRClass& zone_class) :
  581. committed_(false), accessor_(accessor), zone_id_(zone_id),
  582. db_name_(accessor->getDBName()), zone_name_(zone_name.toText()),
  583. zone_class_(zone_class),
  584. finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name))
  585. {
  586. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED)
  587. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  588. }
  589. virtual ~DatabaseUpdater() {
  590. if (!committed_) {
  591. try {
  592. accessor_->rollbackUpdateZone();
  593. logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK)
  594. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  595. } catch (const DataSourceError& e) {
  596. // We generally expect that rollback always succeeds, and
  597. // it should in fact succeed in a way we execute it. But
  598. // as the public API allows rollbackUpdateZone() to fail and
  599. // throw, we should expect it. Obviously we cannot re-throw
  600. // it. The best we can do is to log it as a critical error.
  601. logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL)
  602. .arg(zone_name_).arg(zone_class_).arg(db_name_)
  603. .arg(e.what());
  604. }
  605. }
  606. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED)
  607. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  608. }
  609. virtual ZoneFinder& getFinder() { return (*finder_); }
  610. virtual void addRRset(const RRset& rrset);
  611. virtual void deleteRRset(const RRset& rrset);
  612. virtual void commit();
  613. private:
  614. bool committed_;
  615. shared_ptr<DatabaseAccessor> accessor_;
  616. const int zone_id_;
  617. const string db_name_;
  618. const string zone_name_;
  619. const RRClass zone_class_;
  620. boost::scoped_ptr<DatabaseClient::Finder> finder_;
  621. string add_columns_[DatabaseAccessor::ADD_COLUMN_COUNT];
  622. string del_params_[DatabaseAccessor::DEL_PARAM_COUNT];
  623. };
  624. void
  625. DatabaseUpdater::addRRset(const RRset& rrset) {
  626. if (committed_) {
  627. isc_throw(DataSourceError, "Add attempt after commit to zone: "
  628. << zone_name_ << "/" << zone_class_);
  629. }
  630. if (rrset.getClass() != zone_class_) {
  631. isc_throw(DataSourceError, "An RRset of a different class is being "
  632. << "added to " << zone_name_ << "/" << zone_class_ << ": "
  633. << rrset.toText());
  634. }
  635. RdataIteratorPtr it = rrset.getRdataIterator();
  636. if (it->isLast()) {
  637. isc_throw(DataSourceError, "An empty RRset is being added for "
  638. << rrset.getName() << "/" << zone_class_ << "/"
  639. << rrset.getType());
  640. }
  641. add_columns_[DatabaseAccessor::ADD_NAME] = rrset.getName().toText();
  642. add_columns_[DatabaseAccessor::ADD_REV_NAME] =
  643. rrset.getName().reverse().toText();
  644. add_columns_[DatabaseAccessor::ADD_TTL] = rrset.getTTL().toText();
  645. add_columns_[DatabaseAccessor::ADD_TYPE] = rrset.getType().toText();
  646. for (; !it->isLast(); it->next()) {
  647. if (rrset.getType() == RRType::RRSIG()) {
  648. // XXX: the current interface (based on the current sqlite3
  649. // data source schema) requires a separate "sigtype" column,
  650. // even though it won't be used in a newer implementation.
  651. // We should eventually clean up the schema design and simplify
  652. // the interface, but until then we have to conform to the schema.
  653. const generic::RRSIG& rrsig_rdata =
  654. dynamic_cast<const generic::RRSIG&>(it->getCurrent());
  655. add_columns_[DatabaseAccessor::ADD_SIGTYPE] =
  656. rrsig_rdata.typeCovered().toText();
  657. }
  658. add_columns_[DatabaseAccessor::ADD_RDATA] = it->getCurrent().toText();
  659. accessor_->addRecordToZone(add_columns_);
  660. }
  661. }
  662. void
  663. DatabaseUpdater::deleteRRset(const RRset& rrset) {
  664. if (committed_) {
  665. isc_throw(DataSourceError, "Delete attempt after commit on zone: "
  666. << zone_name_ << "/" << zone_class_);
  667. }
  668. if (rrset.getClass() != zone_class_) {
  669. isc_throw(DataSourceError, "An RRset of a different class is being "
  670. << "deleted from " << zone_name_ << "/" << zone_class_
  671. << ": " << rrset.toText());
  672. }
  673. RdataIteratorPtr it = rrset.getRdataIterator();
  674. if (it->isLast()) {
  675. isc_throw(DataSourceError, "An empty RRset is being deleted for "
  676. << rrset.getName() << "/" << zone_class_ << "/"
  677. << rrset.getType());
  678. }
  679. del_params_[DatabaseAccessor::DEL_NAME] = rrset.getName().toText();
  680. del_params_[DatabaseAccessor::DEL_TYPE] = rrset.getType().toText();
  681. for (; !it->isLast(); it->next()) {
  682. del_params_[DatabaseAccessor::DEL_RDATA] =
  683. it->getCurrent().toText();
  684. accessor_->deleteRecordInZone(del_params_);
  685. }
  686. }
  687. void
  688. DatabaseUpdater::commit() {
  689. if (committed_) {
  690. isc_throw(DataSourceError, "Duplicate commit attempt for "
  691. << zone_name_ << "/" << zone_class_ << " on "
  692. << db_name_);
  693. }
  694. accessor_->commitUpdateZone();
  695. // We release the accessor immediately after commit is completed so that
  696. // we don't hold the possible internal resource any longer.
  697. accessor_.reset();
  698. committed_ = true;
  699. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT)
  700. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  701. }
  702. // The updater factory
  703. ZoneUpdaterPtr
  704. DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace) const {
  705. shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
  706. const std::pair<bool, int> zone(update_accessor->startUpdateZone(
  707. name.toText(), replace));
  708. if (!zone.first) {
  709. return (ZoneUpdaterPtr());
  710. }
  711. return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second,
  712. name, rrclass_)));
  713. }
  714. }
  715. }