database.cc 43 KB

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