database.cc 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876
  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 <utility>
  16. #include <vector>
  17. #include <datasrc/database.h>
  18. #include <datasrc/exceptions.h>
  19. #include <datasrc/zone_iterator.h>
  20. #include <datasrc/rrset_collection_base.h>
  21. #include <exceptions/exceptions.h>
  22. #include <dns/name.h>
  23. #include <dns/labelsequence.h>
  24. #include <dns/rrclass.h>
  25. #include <dns/rrttl.h>
  26. #include <dns/rrset.h>
  27. #include <dns/rdata.h>
  28. #include <dns/rdataclass.h>
  29. #include <dns/nsec3hash.h>
  30. #include <datasrc/exceptions.h>
  31. #include <datasrc/logger.h>
  32. #include <boost/foreach.hpp>
  33. #include <boost/scoped_ptr.hpp>
  34. using namespace isc::dns;
  35. using namespace std;
  36. using namespace isc::dns::rdata;
  37. using boost::lexical_cast;
  38. using boost::scoped_ptr;
  39. namespace isc {
  40. namespace datasrc {
  41. // RAII-style transaction holder; roll back the transaction unless explicitly
  42. // committed
  43. namespace {
  44. class TransactionHolder {
  45. public:
  46. TransactionHolder(DatabaseAccessor& accessor) : accessor_(accessor),
  47. committed_(false)
  48. {
  49. accessor_.startTransaction();
  50. }
  51. ~TransactionHolder() {
  52. if (!committed_) {
  53. try {
  54. accessor_.rollback();
  55. } catch (const DataSourceError& e) {
  56. // We generally expect that rollback always succeeds, and
  57. // it should in fact succeed in a way we execute it. But
  58. // as the public API allows rollback() to fail and
  59. // throw, we should expect it. Obviously we cannot re-throw
  60. // it. The best we can do is to log it as a critical error.
  61. logger.error(DATASRC_DATABASE_TRANSACTION_ROLLBACKFAIL).
  62. arg(accessor_.getDBName()).
  63. arg(e.what());
  64. }
  65. }
  66. }
  67. void commit() {
  68. accessor_.commit();
  69. committed_ = true;
  70. }
  71. private:
  72. DatabaseAccessor& accessor_;
  73. bool committed_;
  74. };
  75. } // end unnamed namespace
  76. DatabaseClient::DatabaseClient(RRClass rrclass,
  77. boost::shared_ptr<DatabaseAccessor>
  78. accessor) :
  79. rrclass_(rrclass), accessor_(accessor)
  80. {
  81. if (!accessor_) {
  82. isc_throw(isc::InvalidParameter,
  83. "No database provided to DatabaseClient");
  84. }
  85. }
  86. DataSourceClient::FindResult
  87. DatabaseClient::findZone(const Name& name) const {
  88. std::pair<bool, int> zone(accessor_->getZone(name.toText()));
  89. // Try exact first
  90. if (zone.first) {
  91. return (FindResult(result::SUCCESS,
  92. ZoneFinderPtr(new Finder(accessor_,
  93. zone.second, name))));
  94. }
  95. // Then super domains
  96. // Start from 1, as 0 is covered above
  97. for (size_t i(1); i < name.getLabelCount(); ++i) {
  98. isc::dns::Name superdomain(name.split(i));
  99. zone = accessor_->getZone(superdomain.toText());
  100. if (zone.first) {
  101. return (FindResult(result::PARTIALMATCH,
  102. ZoneFinderPtr(new Finder(accessor_,
  103. zone.second,
  104. superdomain))));
  105. }
  106. }
  107. // No, really nothing
  108. return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
  109. }
  110. bool
  111. DatabaseClient::createZone(const Name& zone_name) {
  112. TransactionHolder transaction(*accessor_);
  113. const std::pair<bool, int> zone(accessor_->getZone(zone_name.toText()));
  114. if (zone.first) {
  115. return (false);
  116. }
  117. accessor_->addZone(zone_name.toText());
  118. transaction.commit();
  119. return (true);
  120. }
  121. bool
  122. DatabaseClient::deleteZone(const Name& zone_name) {
  123. TransactionHolder transaction(*accessor_);
  124. const std::pair<bool, int> zinfo(accessor_->getZone(zone_name.toText()));
  125. if (!zinfo.first) { // if it doesn't exist just return false
  126. return (false);
  127. }
  128. accessor_->deleteZone(zinfo.second);
  129. transaction.commit();
  130. return (true);
  131. }
  132. DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor> accessor,
  133. int zone_id, const isc::dns::Name& origin) :
  134. accessor_(accessor),
  135. zone_id_(zone_id),
  136. origin_(origin)
  137. { }
  138. namespace {
  139. // Adds the given Rdata to the given RRset
  140. // If the rrset is an empty pointer, a new one is
  141. // created with the given name, class, type and ttl
  142. // The type is checked if the rrset exists, but the
  143. // name is not.
  144. //
  145. // Then adds the given rdata to the set
  146. //
  147. // Raises a DataSourceError if the type does not
  148. // match, or if the given rdata string does not
  149. // parse correctly for the given type and class
  150. //
  151. // The DatabaseAccessor is passed to print the
  152. // database name in the log message if the TTL is
  153. // modified
  154. void addOrCreate(isc::dns::RRsetPtr& rrset,
  155. const isc::dns::Name& name,
  156. const isc::dns::RRClass& cls,
  157. const isc::dns::RRType& type,
  158. const isc::dns::RRTTL& ttl,
  159. const std::string& rdata_str,
  160. const DatabaseAccessor& db
  161. )
  162. {
  163. if (!rrset) {
  164. rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
  165. } else {
  166. // This is a check to make sure find() is not messing things up
  167. assert(type == rrset->getType());
  168. if (ttl != rrset->getTTL()) {
  169. if (ttl < rrset->getTTL()) {
  170. rrset->setTTL(ttl);
  171. }
  172. logger.warn(DATASRC_DATABASE_FIND_TTL_MISMATCH)
  173. .arg(db.getDBName()).arg(name).arg(cls)
  174. .arg(type).arg(rrset->getTTL());
  175. }
  176. }
  177. try {
  178. rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
  179. } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
  180. // at this point, rrset may have been initialised for no reason,
  181. // and won't be used. But the caller would drop the shared_ptr
  182. // on such an error anyway, so we don't care.
  183. isc_throw(DataSourceError,
  184. "bad rdata in database for " << name << " "
  185. << type << ": " << ivrt.what());
  186. }
  187. }
  188. // This class keeps a short-lived store of RRSIG records encountered
  189. // during a call to find(). If the backend happens to return signatures
  190. // before the actual data, we might not know which signatures we will need
  191. // So if they may be relevant, we store the in this class.
  192. //
  193. // (If this class seems useful in other places, we might want to move
  194. // it to util. That would also provide an opportunity to add unit tests)
  195. class RRsigStore {
  196. public:
  197. // Adds the given signature Rdata to the store
  198. // The signature rdata MUST be of the RRSIG rdata type
  199. // (the caller must make sure of this).
  200. // NOTE: if we move this class to a public namespace,
  201. // we should add a type_covered argument, so as not
  202. // to have to do this cast here.
  203. void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
  204. const isc::dns::RRType& type_covered =
  205. static_cast<isc::dns::rdata::generic::RRSIG*>(
  206. sig_rdata.get())->typeCovered();
  207. sigs_[type_covered].push_back(sig_rdata);
  208. }
  209. // If the store contains signatures for the type of the given
  210. // rrset, they are appended to it.
  211. void appendSignatures(isc::dns::RRsetPtr& rrset) const {
  212. std::map<isc::dns::RRType,
  213. std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
  214. found = sigs_.find(rrset->getType());
  215. if (found != sigs_.end()) {
  216. BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
  217. rrset->addRRsig(sig);
  218. }
  219. }
  220. }
  221. bool empty() const {
  222. return (sigs_.empty());
  223. }
  224. private:
  225. std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs_;
  226. };
  227. }
  228. DatabaseClient::Finder::FoundRRsets
  229. DatabaseClient::Finder::getRRsets(const string& name, const WantedTypes& types,
  230. bool sigs,
  231. const string* construct_name, bool any,
  232. DatabaseAccessor::IteratorContextPtr context)
  233. {
  234. RRsigStore sig_store;
  235. bool records_found = false;
  236. std::map<RRType, RRsetPtr> result;
  237. // Request the context in case we didn't get one
  238. if (!context) {
  239. context = accessor_->getRecords(name, zone_id_);
  240. }
  241. // It must not return NULL, that's a bug of the implementation
  242. if (!context) {
  243. isc_throw(isc::Unexpected, "Iterator context null at " + name);
  244. }
  245. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  246. if (construct_name == NULL) {
  247. construct_name = &name;
  248. }
  249. const Name construct_name_object(*construct_name);
  250. bool seen_cname(false);
  251. bool seen_other(false);
  252. while (context->getNext(columns)) {
  253. // The domain is not empty
  254. records_found = true;
  255. try {
  256. const RRType cur_type(columns[DatabaseAccessor::TYPE_COLUMN]);
  257. if (sigs && (cur_type == RRType::RRSIG())) {
  258. // If we get signatures before we get the actual data, we
  259. // can't know which ones to keep and which to drop...
  260. // So we keep a separate store of any signature that may be
  261. // relevant and add them to the final RRset when we are
  262. // done.
  263. // A possible optimization here is to not store them for
  264. // types we are certain we don't need
  265. sig_store.addSig(rdata::createRdata(cur_type, getClass(),
  266. columns[DatabaseAccessor::RDATA_COLUMN]));
  267. }
  268. if (types.find(cur_type) != types.end() || any) {
  269. // This type is requested, so put it into result
  270. const RRTTL cur_ttl(columns[DatabaseAccessor::TTL_COLUMN]);
  271. // The sigtype column was an optimization for finding the
  272. // relevant RRSIG RRs for a lookup. Currently this column is
  273. // not used in this revised datasource implementation. We
  274. // should either start using it again, or remove it from use
  275. // completely (i.e. also remove it from the schema and the
  276. // backend implementation).
  277. // Note that because we don't use it now, we also won't notice
  278. // it if the value is wrong (i.e. if the sigtype column
  279. // contains an rrtype that is different from the actual value
  280. // of the 'type covered' field in the RRSIG Rdata).
  281. //cur_sigtype(columns[SIGTYPE_COLUMN]);
  282. addOrCreate(result[cur_type], construct_name_object,
  283. getClass(), cur_type, cur_ttl,
  284. columns[DatabaseAccessor::RDATA_COLUMN],
  285. *accessor_);
  286. }
  287. if (cur_type == RRType::CNAME()) {
  288. seen_cname = true;
  289. } else if (cur_type != RRType::RRSIG() &&
  290. cur_type != RRType::NSEC3() &&
  291. cur_type != RRType::NSEC()) {
  292. // NSEC and RRSIG can coexist with anything, otherwise
  293. // we've seen something that can't live together with potential
  294. // CNAME.
  295. //
  296. // NSEC3 lives in separate namespace from everything, therefore
  297. // we just ignore it here for these checks as well.
  298. seen_other = true;
  299. }
  300. } catch (const InvalidRRType&) {
  301. isc_throw(DataSourceError, "Invalid RRType in database for " <<
  302. name << ": " << columns[DatabaseAccessor::
  303. TYPE_COLUMN]);
  304. } catch (const InvalidRRTTL&) {
  305. isc_throw(DataSourceError, "Invalid TTL in database for " <<
  306. name << ": " << columns[DatabaseAccessor::
  307. TTL_COLUMN]);
  308. } catch (const rdata::InvalidRdataText&) {
  309. isc_throw(DataSourceError, "Invalid rdata in database for " <<
  310. name << ": " << columns[DatabaseAccessor::
  311. RDATA_COLUMN]);
  312. }
  313. }
  314. if (seen_cname && seen_other) {
  315. isc_throw(DataSourceError, "CNAME shares domain " << name <<
  316. " with something else");
  317. }
  318. if (!sig_store.empty()) {
  319. // Add signatures to all found RRsets
  320. for (std::map<RRType, RRsetPtr>::iterator i(result.begin());
  321. i != result.end(); ++ i) {
  322. sig_store.appendSignatures(i->second);
  323. }
  324. }
  325. if (records_found && any) {
  326. result[RRType::ANY()] = RRsetPtr();
  327. // These will be sitting on the other RRsets.
  328. result.erase(RRType::RRSIG());
  329. }
  330. return (FoundRRsets(records_found, result));
  331. }
  332. bool
  333. DatabaseClient::Finder::hasSubdomains(const std::string& name) {
  334. // Request the context
  335. DatabaseAccessor::IteratorContextPtr
  336. context(accessor_->getRecords(name, zone_id_, true));
  337. // It must not return NULL, that's a bug of the implementation
  338. if (!context) {
  339. isc_throw(isc::Unexpected, "Iterator context null at " + name);
  340. }
  341. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  342. return (context->getNext(columns));
  343. }
  344. // Some manipulation with RRType sets
  345. namespace {
  346. // Bunch of functions to construct specific sets of RRTypes we will
  347. // ask from it.
  348. typedef std::set<RRType> WantedTypes;
  349. const WantedTypes&
  350. NSEC3_TYPES() {
  351. static bool initialized(false);
  352. static WantedTypes result;
  353. if (!initialized) {
  354. result.insert(RRType::NSEC3());
  355. initialized = true;
  356. }
  357. return (result);
  358. }
  359. const WantedTypes&
  360. NSEC3PARAM_TYPES() {
  361. static bool initialized(false);
  362. static WantedTypes result;
  363. if (!initialized) {
  364. result.insert(RRType::NSEC3PARAM());
  365. initialized = true;
  366. }
  367. return (result);
  368. }
  369. const WantedTypes&
  370. NSEC_TYPES() {
  371. static bool initialized(false);
  372. static WantedTypes result;
  373. if (!initialized) {
  374. result.insert(RRType::NSEC());
  375. initialized = true;
  376. }
  377. return (result);
  378. }
  379. const WantedTypes&
  380. DELEGATION_TYPES() {
  381. static bool initialized(false);
  382. static WantedTypes result;
  383. if (!initialized) {
  384. result.insert(RRType::DNAME());
  385. result.insert(RRType::NS());
  386. initialized = true;
  387. }
  388. return (result);
  389. }
  390. const WantedTypes&
  391. FINAL_TYPES() {
  392. static bool initialized(false);
  393. static WantedTypes result;
  394. if (!initialized) {
  395. result.insert(RRType::CNAME());
  396. result.insert(RRType::NS());
  397. result.insert(RRType::NSEC());
  398. initialized = true;
  399. }
  400. return (result);
  401. }
  402. }
  403. ZoneFinderContextPtr
  404. DatabaseClient::Finder::findAll(const isc::dns::Name& name,
  405. std::vector<isc::dns::ConstRRsetPtr>& target,
  406. const FindOptions options)
  407. {
  408. return (ZoneFinderContextPtr(new GenericContext(
  409. *this, options,
  410. findInternal(name, RRType::ANY(),
  411. &target, options),
  412. target)));
  413. }
  414. ZoneFinderContextPtr
  415. DatabaseClient::Finder::find(const isc::dns::Name& name,
  416. const isc::dns::RRType& type,
  417. const FindOptions options)
  418. {
  419. if (type == RRType::ANY()) {
  420. isc_throw(isc::Unexpected, "Use findAll to answer ANY");
  421. }
  422. return (ZoneFinderContextPtr(new GenericContext(
  423. *this, options,
  424. findInternal(name, type, NULL,
  425. options))));
  426. }
  427. DatabaseClient::Finder::DelegationSearchResult
  428. DatabaseClient::Finder::findDelegationPoint(const isc::dns::Name& name,
  429. const FindOptions options)
  430. {
  431. // Result of search
  432. isc::dns::ConstRRsetPtr result_rrset;
  433. ZoneFinder::Result result_status = SUCCESS;
  434. // Are we searching for glue?
  435. const bool glue_ok = ((options & FIND_GLUE_OK) != 0);
  436. // This next declaration is an optimisation. When we search the database
  437. // for glue records, we generally ignore delegations. (This allows for
  438. // the case where e.g. the delegation to zone example.com refers to
  439. // nameservers within the zone, e.g. ns1.example.com. When conducting the
  440. // search for ns1.example.com, we have to search past the NS records at
  441. // example.com.)
  442. //
  443. // The one case where this is forbidden is when we search past the zone
  444. // cut but the match we find for the glue is a wildcard match. In that
  445. // case, we return the delegation instead (see RFC 1034, section 4.3.3).
  446. // To save a new search, we record the location of the delegation cut when
  447. // we encounter it here.
  448. isc::dns::ConstRRsetPtr first_ns;
  449. // We want to search from the apex down. We are given the full domain
  450. // name so we have to do some manipulation to ensure that when we start
  451. // checking superdomains, we start from the the domain name of the zone
  452. // (e.g. if the name is b.a.example.com. and we are in the example.com.
  453. // zone, we check example.com., a.example.com. and b.a.example.com. We
  454. // don't need to check com. or .).
  455. //
  456. // Set the number of labels in the origin (i.e. apex of the zone) and in
  457. // the last known non-empty domain (which, at this point, is the origin).
  458. const size_t origin_label_count = getOrigin().getLabelCount();
  459. size_t last_known = origin_label_count;
  460. // Set how many labels we remove to get origin: this is the number of
  461. // labels we have to process in our search.
  462. const size_t remove_labels = name.getLabelCount() - origin_label_count;
  463. // Go through all superdomains from the origin down searching for nodes
  464. // that indicate a delegation (.e. NS or DNAME). Note that we only check
  465. // pure superdomains; delegation on an exact match will be detected later.
  466. for (int i = remove_labels; i > 0; --i) {
  467. const Name superdomain(name.split(i));
  468. // Look if there's NS or DNAME at this point of the tree, but ignore
  469. // the NS RRs at the apex of the zone.
  470. const FoundRRsets found = getRRsets(superdomain.toText(),
  471. DELEGATION_TYPES(),
  472. ((options & FIND_DNSSEC) ==
  473. FIND_DNSSEC));
  474. if (found.first) {
  475. // This node contains either NS or DNAME RRs so it does exist.
  476. const FoundIterator nsi(found.second.find(RRType::NS()));
  477. const FoundIterator dni(found.second.find(RRType::DNAME()));
  478. // Note if this is the origin. (We don't count NS records at the
  479. // origin as a delegation so this controls whether NS RRs are
  480. // included in the results of some searches.)
  481. const bool not_origin = (i != remove_labels);
  482. // An optimisation. We know that there is an exact match for
  483. // something at this point in the tree so remember it. If we have
  484. // to do a wildcard search, as we search upwards through the tree
  485. // we don't need to pass this point, which is an exact match for
  486. // the domain name.
  487. last_known = superdomain.getLabelCount();
  488. if (glue_ok && !first_ns && not_origin &&
  489. nsi != found.second.end()) {
  490. // If we are searching for glue ("glue OK" mode), store the
  491. // highest NS record that we find that is not the apex. This
  492. // is another optimisation for later, where we need the
  493. // information if the domain we are looking for matches through
  494. // a wildcard.
  495. first_ns = nsi->second;
  496. } else if (!glue_ok && not_origin && nsi != found.second.end()) {
  497. // Not searching for glue and we have found an NS RRset that is
  498. // not at the apex. We have found a delegation - return that
  499. // fact, there is no need to search further down the tree.
  500. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  501. DATASRC_DATABASE_FOUND_DELEGATION).
  502. arg(accessor_->getDBName()).arg(superdomain);
  503. result_rrset = nsi->second;
  504. result_status = DELEGATION;
  505. break;
  506. } else if (dni != found.second.end()) {
  507. // We have found a DNAME so again stop searching down the tree
  508. // and return the information.
  509. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  510. DATASRC_DATABASE_FOUND_DNAME).
  511. arg(accessor_->getDBName()).arg(superdomain);
  512. result_rrset = dni->second;
  513. result_status = DNAME;
  514. if (result_rrset->getRdataCount() != 1) {
  515. isc_throw(DataSourceError, "DNAME at " << superdomain <<
  516. " has " << result_rrset->getRdataCount() <<
  517. " rdata, 1 expected");
  518. }
  519. break;
  520. }
  521. }
  522. }
  523. return (DelegationSearchResult(result_status, result_rrset, first_ns,
  524. last_known));
  525. }
  526. // This method is called when we have not found an exact match and when we
  527. // know that the name is not an empty non-terminal. So the only way that
  528. // the name can match something in the zone is through a wildcard match.
  529. //
  530. // During an earlier stage in the search for this name, we made a record of
  531. // the lowest superdomain for which we know an RR exists. (Note the "we
  532. // know" qualification - there may be lower superdomains (ones with more
  533. // labels) that hold an RR, but as we weren't searching for them, we don't
  534. // know about them.)
  535. //
  536. // In the search for a wildcard match (which starts at the given domain
  537. // name and goes up the tree to successive superdomains), this is the level
  538. // at which we can stop - there can't be a wildcard at or beyond that
  539. // point.
  540. //
  541. // At each level that can stop the search, we should consider several cases:
  542. //
  543. // - If we found a wildcard match for a glue record below a
  544. // delegation point, we don't return the match,
  545. // instead we return the delegation. (Note that if we didn't
  546. // a wildcard match at all, we would return NXDOMAIN, not the
  547. // the delegation.)
  548. //
  549. // - If we found a wildcard match and we are sure that the match
  550. // is not an empty non-terminal, return the result taking into account CNAME,
  551. // on a zone cut, and NXRRSET.
  552. // (E.g. searching for a match
  553. // for c.b.a.example.com, we found that b.a.example.com did
  554. // not exist but that *.a.example.com. did. Checking
  555. // b.a.example.com revealed no subdomains, so we can use the
  556. // wilcard match we found.)
  557. //
  558. // - If we found a more specified match, the wildcard search
  559. // is canceled, resulting in NXDOMAIN. (E.g. searching for a match
  560. // for c.b.a.example.com, we found that b.a.example.com did
  561. // not exist but that *.a.example.com. did. Checking
  562. // b.a.example.com found subdomains. So b.example.com is
  563. // an empty non-terminal and so should not be returned in
  564. // the wildcard matching process. In other words,
  565. // b.example.com does exist in the DNS space, it just doesn't
  566. // have any RRs associated with it.)
  567. //
  568. // - If we found a match, but it is an empty non-terminal asterisk (E.g.#
  569. // subdomain.*.example.com. is present, but there is nothing at
  570. // *.example.com.), return an NXRRSET indication;
  571. // the wildcard exists in the DNS space, there's just nothing
  572. // associated with it. If DNSSEC data is required, return the
  573. // covering NSEC record.
  574. //
  575. // If none of the above applies in any level, the search fails with NXDOMAIN.
  576. ZoneFinder::ResultContext
  577. DatabaseClient::Finder::findWildcardMatch(
  578. const Name& name, const RRType& type, const FindOptions options,
  579. const DelegationSearchResult& dresult, vector<ConstRRsetPtr>* target,
  580. FindDNSSECContext& dnssec_ctx)
  581. {
  582. // Note that during the search we are going to search not only for the
  583. // requested type, but also for types that indicate a delegation -
  584. // NS and DNAME.
  585. WantedTypes final_types(FINAL_TYPES());
  586. final_types.insert(type);
  587. const size_t remove_labels = name.getLabelCount() - dresult.last_known;
  588. for (size_t i = 1; i <= remove_labels; ++i) {
  589. // Strip off the left-more label(s) in the name and replace with a "*".
  590. const Name superdomain(name.split(i));
  591. const string wildcard("*." + superdomain.toText());
  592. const string construct_name(name.toText());
  593. // TODO Add a check for DNAME, as DNAME wildcards are discouraged (see
  594. // RFC 4592 section 4.4).
  595. // Search for a match. The types are the same as with original query.
  596. const FoundRRsets found = getRRsets(wildcard, final_types,
  597. ((options & FIND_DNSSEC) ==
  598. FIND_DNSSEC),
  599. &construct_name,
  600. type == RRType::ANY());
  601. if (found.first) {
  602. // Found something - but what?
  603. if (dresult.first_ns) {
  604. // About to use first_ns. The only way this can be set is if
  605. // we are searching for glue, so do a sanity check.
  606. if ((options & FIND_GLUE_OK) == 0) {
  607. isc_throw(Unexpected, "Inconsistent conditions during "
  608. "cancel of wilcard search for " <<
  609. name.toText() << ": find_ns non-null when not "
  610. "processing glue request");
  611. }
  612. // Wildcard match for a glue below a delegation point
  613. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  614. DATASRC_DATABASE_WILDCARD_CANCEL_NS).
  615. arg(accessor_->getDBName()).arg(wildcard).
  616. arg(dresult.first_ns->getName());
  617. return (ResultContext(DELEGATION, dresult.first_ns));
  618. } else if (!hasSubdomains(name.split(i - 1).toText())) {
  619. // The wildcard match is the best one, find the final result
  620. // at it. Note that wildcard should never be the zone origin.
  621. return (findOnNameResult(name, type, options, false, found,
  622. &wildcard, target, dnssec_ctx));
  623. } else {
  624. // more specified match found, cancel wildcard match
  625. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  626. DATASRC_DATABASE_WILDCARD_CANCEL_SUB).
  627. arg(accessor_->getDBName()).arg(wildcard).
  628. arg(name).arg(superdomain);
  629. return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
  630. }
  631. } else if (hasSubdomains(wildcard)) {
  632. // an empty non-terminal asterisk
  633. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  634. DATASRC_DATABASE_WILDCARD_EMPTY).
  635. arg(accessor_->getDBName()).arg(wildcard).arg(name);
  636. const FindResultFlags flags = (RESULT_WILDCARD |
  637. dnssec_ctx.getResultFlags());
  638. return (ResultContext(NXRRSET,
  639. dnssec_ctx.getDNSSECRRset(Name(wildcard),
  640. true), flags));
  641. }
  642. }
  643. // Nothing found at any level.
  644. return (ResultContext(NXDOMAIN, ConstRRsetPtr()));
  645. }
  646. ZoneFinder::ResultContext
  647. DatabaseClient::Finder::logAndCreateResult(
  648. const Name& name, const string* wildname, const RRType& type,
  649. ZoneFinder::Result code, ConstRRsetPtr rrset,
  650. const isc::log::MessageID& log_id, FindResultFlags flags) const
  651. {
  652. if (rrset) {
  653. if (wildname == NULL) {
  654. LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
  655. arg(accessor_->getDBName()).arg(name).arg(type).
  656. arg(getClass()).arg(*rrset);
  657. } else {
  658. LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
  659. arg(accessor_->getDBName()).arg(name).arg(type).
  660. arg(getClass()).arg(*wildname).arg(*rrset);
  661. }
  662. } else {
  663. if (wildname == NULL) {
  664. LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
  665. arg(accessor_->getDBName()).arg(name).arg(type).
  666. arg(getClass());
  667. } else {
  668. LOG_DEBUG(logger, DBG_TRACE_DETAILED, log_id).
  669. arg(accessor_->getDBName()).arg(name).arg(type).
  670. arg(getClass()).arg(*wildname);
  671. }
  672. }
  673. return (ResultContext(code, rrset, flags));
  674. }
  675. DatabaseClient::Finder::FindDNSSECContext::FindDNSSECContext(
  676. DatabaseClient::Finder& finder,
  677. const FindOptions options) :
  678. finder_(finder),
  679. need_dnssec_((options & FIND_DNSSEC) != 0),
  680. is_nsec3_(false),
  681. is_nsec_(false),
  682. probed_(false)
  683. {}
  684. void
  685. DatabaseClient::Finder::FindDNSSECContext::probe() {
  686. if (!probed_) {
  687. probed_ = true;
  688. if (need_dnssec_) {
  689. // If an NSEC3PARAM RR exists at the zone apex, it's quite likely
  690. // that the zone is signed with NSEC3. (If not the zone is more
  691. // or less broken, but it's caller's responsibility how to handle
  692. // such cases).
  693. const string origin = finder_.getOrigin().toText();
  694. const FoundRRsets nsec3_found =
  695. finder_.getRRsets(origin, NSEC3PARAM_TYPES(), true);
  696. const FoundIterator nfi=
  697. nsec3_found.second.find(RRType::NSEC3PARAM());
  698. is_nsec3_ = (nfi != nsec3_found.second.end());
  699. // Likewise for NSEC, depending on the apex has an NSEC RR.
  700. // If we know the zone is NSEC3-signed, however, we don't bother
  701. // to check that. This is aligned with the transition guideline
  702. // described in Section 10.4 of RFC 5155.
  703. if (!is_nsec3_) {
  704. const FoundRRsets nsec_found =
  705. finder_.getRRsets(origin, NSEC_TYPES(), true);
  706. const FoundIterator nfi =
  707. nsec_found.second.find(RRType::NSEC());
  708. is_nsec_ = (nfi != nsec_found.second.end());
  709. }
  710. }
  711. }
  712. }
  713. bool
  714. DatabaseClient::Finder::FindDNSSECContext::isNSEC3() {
  715. if (!probed_) {
  716. probe();
  717. }
  718. return (is_nsec3_);
  719. }
  720. bool
  721. DatabaseClient::Finder::FindDNSSECContext::isNSEC() {
  722. if (!probed_) {
  723. probe();
  724. }
  725. return (is_nsec_);
  726. }
  727. isc::dns::ConstRRsetPtr
  728. DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(
  729. const FoundRRsets& found_set)
  730. {
  731. if (!isNSEC()) {
  732. return (ConstRRsetPtr());
  733. }
  734. const FoundIterator nci = found_set.second.find(RRType::NSEC());
  735. if (nci != found_set.second.end()) {
  736. return (nci->second);
  737. } else {
  738. return (ConstRRsetPtr());
  739. }
  740. }
  741. isc::dns::ConstRRsetPtr
  742. DatabaseClient::Finder::FindDNSSECContext::getDNSSECRRset(const Name &name,
  743. bool covering)
  744. {
  745. if (!isNSEC()) {
  746. return (ConstRRsetPtr());
  747. }
  748. try {
  749. const Name& nsec_name =
  750. covering ? finder_.findPreviousName(name) : name;
  751. const FoundRRsets found = finder_.getRRsets(nsec_name.toText(),
  752. NSEC_TYPES(), true);
  753. const FoundIterator nci = found.second.find(RRType::NSEC());
  754. if (nci != found.second.end()) {
  755. return (nci->second);
  756. }
  757. } catch (const isc::NotImplemented&) {
  758. // This happens when the underlying database accessor doesn't support
  759. // findPreviousName() (it probably doesn't support DNSSEC at all) but
  760. // there is somehow an NSEC RR at the zone apex. We log the fact but
  761. // otherwise let the caller decide what to do (so, for example, a
  762. // higher level query processing won't completely fail but can return
  763. // anything it can get).
  764. LOG_INFO(logger, DATASRC_DATABASE_COVER_NSEC_UNSUPPORTED).
  765. arg(finder_.accessor_->getDBName()).arg(name);
  766. }
  767. return (ConstRRsetPtr());
  768. }
  769. ZoneFinder::FindResultFlags
  770. DatabaseClient::Finder::FindDNSSECContext::getResultFlags() {
  771. if (isNSEC3()) {
  772. return (RESULT_NSEC3_SIGNED);
  773. } else if (isNSEC()) {
  774. return (RESULT_NSEC_SIGNED);
  775. }
  776. return (RESULT_DEFAULT);
  777. }
  778. ZoneFinder::ResultContext
  779. DatabaseClient::Finder::findOnNameResult(const Name& name,
  780. const RRType& type,
  781. const FindOptions options,
  782. const bool is_origin,
  783. const FoundRRsets& found,
  784. const string* wildname,
  785. std::vector<isc::dns::ConstRRsetPtr>*
  786. target, FindDNSSECContext& dnssec_ctx)
  787. {
  788. const bool wild = (wildname != NULL);
  789. // For wildcard case with DNSSEC required, the caller would need to
  790. // know whether it's NSEC or NSEC3 signed. getResultFlags returns
  791. // appropriate flag based on the query context and zone status.
  792. const FindResultFlags flags =
  793. wild ? (RESULT_WILDCARD | dnssec_ctx.getResultFlags()) : RESULT_DEFAULT;
  794. // Get iterators for the different types of records we are interested in -
  795. // CNAME, NS and Wanted types.
  796. const FoundIterator nsi(found.second.find(RRType::NS()));
  797. const FoundIterator cni(found.second.find(RRType::CNAME()));
  798. const FoundIterator wti(found.second.find(type));
  799. if (!is_origin && (options & FIND_GLUE_OK) == 0 && type != RRType::DS() &&
  800. nsi != found.second.end()) {
  801. // A NS RRset was found at the domain we were searching for. As it is
  802. // not at the origin of the zone, it is a delegation and indicates that
  803. // this zone is not authoritative for the data. Just return the
  804. // delegation information, except:
  805. // - when we are looking for glue records (FIND_GLUE_OK), or
  806. // - when the query type is DS (which cancels the delegation)
  807. return (logAndCreateResult(name, wildname, type, DELEGATION,
  808. nsi->second,
  809. wild ? DATASRC_DATABASE_WILDCARD_NS :
  810. DATASRC_DATABASE_FOUND_DELEGATION_EXACT,
  811. flags));
  812. } else if (type != RRType::CNAME() && cni != found.second.end()) {
  813. // We are not searching for a CNAME but nevertheless we have found one
  814. // at the name we are searching so we return it. (The caller may
  815. // want to continue the lookup by replacing the query name with the
  816. // canonical name and the original RR type.) First though, do a sanity
  817. // check to ensure that there is only one RR in the CNAME RRset.
  818. if (cni->second->getRdataCount() != 1) {
  819. isc_throw(DataSourceError, "CNAME with " <<
  820. cni->second->getRdataCount() << " rdata at " << name <<
  821. ", expected 1");
  822. }
  823. return (logAndCreateResult(name, wildname, type, CNAME, cni->second,
  824. wild ? DATASRC_DATABASE_WILDCARD_CNAME :
  825. DATASRC_DATABASE_FOUND_CNAME,
  826. flags));
  827. } else if (wti != found.second.end()) {
  828. bool any(type == RRType::ANY());
  829. if (any) {
  830. // An ANY query, copy everything to the target instead of returning
  831. // directly.
  832. for (FoundIterator it(found.second.begin());
  833. it != found.second.end(); ++it) {
  834. if (it->second) {
  835. // Skip over the empty ANY
  836. target->push_back(it->second);
  837. }
  838. }
  839. if (wild) {
  840. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  841. DATASRC_DATABASE_WILDCARD_ANY).
  842. arg(accessor_->getDBName()).arg(name);
  843. } else {
  844. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  845. DATASRC_DATABASE_FOUND_ANY).
  846. arg(accessor_->getDBName()).arg(name);
  847. }
  848. } else {
  849. if (wild) {
  850. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  851. DATASRC_DATABASE_WILDCARD_MATCH).
  852. arg(accessor_->getDBName()).arg(*wildname).
  853. arg(wti->second);
  854. } else {
  855. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  856. DATASRC_DATABASE_FOUND_RRSET).
  857. arg(accessor_->getDBName()).arg(*wti->second);
  858. }
  859. }
  860. // Found an RR matching the query, so return it. (Note that this
  861. // includes the case where we were explicitly querying for a CNAME and
  862. // found it. It also includes the case where we were querying for an
  863. // NS RRset and found it at the apex of the zone.)
  864. return (ResultContext(SUCCESS, wti->second, flags));
  865. }
  866. // If we get here, we have found something at the requested name but not
  867. // one of the RR types we were interested in. This is the NXRRSET case so
  868. // return the appropriate status. If DNSSEC information was requested,
  869. // provide the NSEC records. If it's for wildcard, we need to get the
  870. // NSEC records in the name of the wildcard, not the substituted one,
  871. // so we need to search the tree again.
  872. const ConstRRsetPtr dnssec_rrset =
  873. wild ? dnssec_ctx.getDNSSECRRset(Name(*wildname), false) :
  874. dnssec_ctx.getDNSSECRRset(found);
  875. if (dnssec_rrset) {
  876. // This log message covers both normal and wildcard cases, so we pass
  877. // NULL for 'wildname'.
  878. return (logAndCreateResult(name, NULL, type, NXRRSET, dnssec_rrset,
  879. DATASRC_DATABASE_FOUND_NXRRSET_NSEC,
  880. flags | RESULT_NSEC_SIGNED));
  881. }
  882. return (logAndCreateResult(name, wildname, type, NXRRSET, dnssec_rrset,
  883. wild ? DATASRC_DATABASE_WILDCARD_NXRRSET :
  884. DATASRC_DATABASE_FOUND_NXRRSET,
  885. flags | dnssec_ctx.getResultFlags()));
  886. }
  887. ZoneFinder::ResultContext
  888. DatabaseClient::Finder::findNoNameResult(const Name& name, const RRType& type,
  889. FindOptions options,
  890. const DelegationSearchResult& dresult,
  891. std::vector<isc::dns::ConstRRsetPtr>*
  892. target, FindDNSSECContext& dnssec_ctx)
  893. {
  894. // On entry to this method, we know that the database doesn't have any
  895. // entry for this name. Before returning NXDOMAIN, we need to check
  896. // for special cases.
  897. if (hasSubdomains(name.toText())) {
  898. // Does the domain have a subdomain (i.e. it is an empty non-terminal)?
  899. // If so, return NXRRSET instead of NXDOMAIN (as although the name does
  900. // not exist in the database, it does exist in the DNS tree).
  901. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  902. DATASRC_DATABASE_FOUND_EMPTY_NONTERMINAL).
  903. arg(accessor_->getDBName()).arg(name);
  904. return (ResultContext(NXRRSET, dnssec_ctx.getDNSSECRRset(name, true),
  905. dnssec_ctx.getResultFlags()));
  906. } else if ((options & NO_WILDCARD) == 0) {
  907. // It's not an empty non-terminal and wildcard matching is not
  908. // disabled, so check for wildcards. If there is a wildcard match
  909. // (i.e. all results except NXDOMAIN) return it; otherwise fall
  910. // through to the NXDOMAIN case below.
  911. const ResultContext wcontext =
  912. findWildcardMatch(name, type, options, dresult, target,
  913. dnssec_ctx);
  914. if (wcontext.code != NXDOMAIN) {
  915. return (wcontext);
  916. }
  917. }
  918. // All avenues to find a match are now exhausted, return NXDOMAIN (plus
  919. // NSEC records if requested).
  920. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_NO_MATCH).
  921. arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
  922. return (ResultContext(NXDOMAIN, dnssec_ctx.getDNSSECRRset(name, true),
  923. dnssec_ctx.getResultFlags()));
  924. }
  925. ZoneFinder::ResultContext
  926. DatabaseClient::Finder::findInternal(const Name& name, const RRType& type,
  927. std::vector<ConstRRsetPtr>* target,
  928. const FindOptions options)
  929. {
  930. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
  931. .arg(accessor_->getDBName()).arg(name).arg(type).arg(getClass());
  932. // find() variants generally expect 'name' to be included in the zone.
  933. // Otherwise the search algorithm below won't work correctly, so we
  934. // reject the unexpected case first.
  935. const NameComparisonResult::NameRelation reln =
  936. name.compare(getOrigin()).getRelation();
  937. if (reln != NameComparisonResult::SUBDOMAIN &&
  938. reln != NameComparisonResult::EQUAL) {
  939. isc_throw(OutOfZone, name.toText() << " not in " << getOrigin());
  940. }
  941. // First, go through all superdomains from the origin down, searching for
  942. // nodes that indicate a delegation (i.e. NS or DNAME, ignoring NS records
  943. // at the apex). If one is found, the search stops there.
  944. //
  945. // (In fact there could be RRs in the database corresponding to subdomains
  946. // of the delegation. The reason we do the search for the delegations
  947. // first is because the delegation means that another zone is authoritative
  948. // for the data and so should be consulted to retrieve it. RRs below
  949. // this delegation point can be found in a search for glue but not
  950. // otherwise; in the latter case they are said to be occluded by the
  951. // presence of the delegation.)
  952. const DelegationSearchResult dresult = findDelegationPoint(name, options);
  953. if (dresult.rrset) {
  954. // In this case no special flags are needed.
  955. return (ResultContext(dresult.code, dresult.rrset));
  956. }
  957. // If there is no delegation, look for the exact match to the request
  958. // name/type/class. However, there are special cases:
  959. // - Requested name has a singleton CNAME record associated with it
  960. // - Requested name is a delegation point (NS only but not at the zone
  961. // apex - DNAME is ignored here as it redirects DNS names subordinate to
  962. // the owner name - the owner name itself is not redirected.)
  963. WantedTypes final_types(FINAL_TYPES());
  964. final_types.insert(type);
  965. const FoundRRsets found = getRRsets(name.toText(), final_types,
  966. ((options & FIND_DNSSEC) ==
  967. FIND_DNSSEC),
  968. NULL, type == RRType::ANY());
  969. FindDNSSECContext dnssec_ctx(*this, options);
  970. if (found.first) {
  971. // Something found at the domain name. Look into it further to get
  972. // the final result.
  973. const bool is_origin = (name == getOrigin());
  974. return (findOnNameResult(name, type, options, is_origin, found, NULL,
  975. target, dnssec_ctx));
  976. } else {
  977. // Did not find anything at all at the domain name, so check for
  978. // subdomains or wildcards.
  979. return (findNoNameResult(name, type, options, dresult, target,
  980. dnssec_ctx));
  981. }
  982. }
  983. // The behaviour is inspired by the one in the in-memory implementation.
  984. ZoneFinder::FindNSEC3Result
  985. DatabaseClient::Finder::findNSEC3(const Name& name, bool recursive) {
  986. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3).arg(name).
  987. arg(recursive ? "recursive" : "non-recursive");
  988. // First, validate the input
  989. const NameComparisonResult cmp_result(name.compare(getOrigin()));
  990. if (cmp_result.getRelation() != NameComparisonResult::EQUAL &&
  991. cmp_result.getRelation() != NameComparisonResult::SUBDOMAIN) {
  992. isc_throw(OutOfZone, "findNSEC3 attempt for out-of-zone name: " <<
  993. name << ", zone: " << getOrigin() << "/" << getClass());
  994. }
  995. // Now, we need to get the NSEC3 params from the apex and create the hash
  996. // creator for it.
  997. const FoundRRsets nsec3param(getRRsets(getOrigin().toText(),
  998. NSEC3PARAM_TYPES(),
  999. true));
  1000. const FoundIterator param(nsec3param.second.find(RRType::NSEC3PARAM()));
  1001. if (!nsec3param.first || param == nsec3param.second.end()) {
  1002. // No NSEC3 params? :-(
  1003. isc_throw(DataSourceError, "findNSEC3 attempt for non NSEC3 signed " <<
  1004. "zone: " << getOrigin() << "/" << getClass());
  1005. }
  1006. // This takes the RRset received from the find method, takes the first RR
  1007. // in it, casts it to NSEC3PARAM (as it should be that one) and then creates
  1008. // the hash calculator class from it.
  1009. const scoped_ptr<NSEC3Hash> calculator(NSEC3Hash::create(
  1010. dynamic_cast<const generic::NSEC3PARAM&>(
  1011. param->second->getRdataIterator()->getCurrent())));
  1012. // Few shortcut variables
  1013. const unsigned olabels(getOrigin().getLabelCount());
  1014. const unsigned qlabels(name.getLabelCount());
  1015. const string otext(getOrigin().toText());
  1016. // This will be set to the one covering the query name
  1017. ConstRRsetPtr covering_proof;
  1018. LabelSequence name_ls(name);
  1019. // We keep stripping the leftmost label until we find something.
  1020. // In case it is recursive, we'll exit the loop at the first iteration.
  1021. for (unsigned int labels = qlabels; labels >= olabels;
  1022. --labels, name_ls.stripLeft(1))
  1023. {
  1024. const std::string hash = calculator->calculate(name_ls);
  1025. // Get the exact match for the name.
  1026. LOG_DEBUG(logger, DBG_TRACE_BASIC, DATASRC_DATABASE_FINDNSEC3_TRYHASH).
  1027. arg(name).arg(labels).arg(hash);
  1028. DatabaseAccessor::IteratorContextPtr
  1029. context(accessor_->getNSEC3Records(hash, zone_id_));
  1030. if (!context) {
  1031. isc_throw(Unexpected, "Iterator context null for hash " + hash);
  1032. }
  1033. const FoundRRsets nsec3(getRRsets(hash + "." + otext, NSEC3_TYPES(),
  1034. true,
  1035. NULL, false, context));
  1036. if (nsec3.first) {
  1037. // We found an exact match against the current label.
  1038. const FoundIterator it(nsec3.second.find(RRType::NSEC3()));
  1039. if (it == nsec3.second.end()) {
  1040. isc_throw(DataSourceError, "Hash " + hash +
  1041. "exists, but no NSEC3 there");
  1042. }
  1043. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  1044. DATASRC_DATABASE_FINDNSEC3_MATCH).arg(name).arg(labels).
  1045. arg(*it->second);
  1046. // Yes, we win
  1047. return (FindNSEC3Result(true, labels, it->second, covering_proof));
  1048. } else {
  1049. // There's no exact match. We try a previous one. We must find it
  1050. // (if the zone is properly signed).
  1051. const string prevHash(accessor_->findPreviousNSEC3Hash(zone_id_,
  1052. hash));
  1053. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  1054. DATASRC_DATABASE_FINDNSEC3_TRYHASH_PREV).arg(name).
  1055. arg(labels).arg(prevHash);
  1056. context = accessor_->getNSEC3Records(prevHash, zone_id_);
  1057. const FoundRRsets prev_nsec3(getRRsets(prevHash + "." + otext,
  1058. NSEC3_TYPES(), true,
  1059. NULL, false,
  1060. context));
  1061. if (!prev_nsec3.first) {
  1062. isc_throw(DataSourceError, "Hash " + prevHash + " returned "
  1063. "from findPreviousNSEC3Hash, but it is empty");
  1064. }
  1065. const FoundIterator
  1066. prev_it(prev_nsec3.second.find(RRType::NSEC3()));
  1067. if (prev_it == prev_nsec3.second.end()) {
  1068. isc_throw(DataSourceError, "The previous hash " + prevHash +
  1069. "exists, but does not contain the NSEC3");
  1070. }
  1071. covering_proof = prev_it->second;
  1072. // In case it is recursive, we try to get an exact match a level
  1073. // up. If it is not recursive, the caller is ok with a covering
  1074. // one, so we just return it.
  1075. if (!recursive) {
  1076. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  1077. DATASRC_DATABASE_FINDNSEC3_COVER).arg(name).
  1078. arg(labels).arg(*covering_proof);
  1079. return (FindNSEC3Result(false, labels, covering_proof,
  1080. ConstRRsetPtr()));
  1081. }
  1082. }
  1083. }
  1084. // The zone must contain at least the apex and that one should match
  1085. // exactly. If that doesn't happen, we have a problem.
  1086. isc_throw(DataSourceError, "recursive findNSEC3 mode didn't stop, likely a "
  1087. "broken NSEC3 zone: " << otext << "/" << getClass());
  1088. }
  1089. Name
  1090. DatabaseClient::Finder::findPreviousName(const Name& name) const {
  1091. const string str(accessor_->findPreviousName(zone_id_,
  1092. name.reverse().toText()));
  1093. try {
  1094. return (Name(str));
  1095. } catch (const isc::dns::NameParserException&) {
  1096. isc_throw(DataSourceError, "Bad name " + str +
  1097. " from findPreviousName");
  1098. }
  1099. }
  1100. Name
  1101. DatabaseClient::Finder::getOrigin() const {
  1102. return (origin_);
  1103. }
  1104. isc::dns::RRClass
  1105. DatabaseClient::Finder::getClass() const {
  1106. // TODO Implement
  1107. return isc::dns::RRClass::IN();
  1108. }
  1109. namespace {
  1110. /// This needs, beside of converting all data from textual representation, group
  1111. /// together rdata of the same RRsets. To do this, we hold one row of data ahead
  1112. /// of iteration. When we get a request to provide data, we create it from this
  1113. /// data and load a new one. If it is to be put to the same rrset, we add it.
  1114. /// Otherwise we just return what we have and keep the row as the one ahead
  1115. /// for next time.
  1116. class DatabaseIterator : public ZoneIterator {
  1117. public:
  1118. DatabaseIterator(boost::shared_ptr<DatabaseAccessor> accessor,
  1119. const Name& zone_name,
  1120. const RRClass& rrclass,
  1121. bool separate_rrs) :
  1122. accessor_(accessor),
  1123. class_(rrclass),
  1124. ready_(true),
  1125. separate_rrs_(separate_rrs)
  1126. {
  1127. // Get the zone
  1128. const pair<bool, int> zone(accessor_->getZone(zone_name.toText()));
  1129. if (!zone.first) {
  1130. // No such zone, can't continue
  1131. isc_throw(NoSuchZone, "Zone " + zone_name.toText() +
  1132. " can not be iterated, because it doesn't exist "
  1133. "in this data source");
  1134. }
  1135. // Start a separate transaction.
  1136. accessor_->startTransaction();
  1137. // Find the SOA of the zone (may or may not succeed). Note that
  1138. // this must be done before starting the iteration context.
  1139. soa_ = DatabaseClient::Finder(accessor_, zone.second, zone_name).
  1140. find(zone_name, RRType::SOA())->rrset;
  1141. // Request the context
  1142. context_ = accessor_->getAllRecords(zone.second);
  1143. // It must not return NULL, that's a bug of the implementation
  1144. if (!context_) {
  1145. isc_throw(isc::Unexpected, "Iterator context null at " +
  1146. zone_name.toText());
  1147. }
  1148. // Prepare data for the next time
  1149. getData();
  1150. }
  1151. virtual ~DatabaseIterator() {
  1152. if (ready_) {
  1153. accessor_->commit();
  1154. }
  1155. }
  1156. virtual ConstRRsetPtr getSOA() const {
  1157. return (soa_);
  1158. }
  1159. virtual isc::dns::ConstRRsetPtr getNextRRset() {
  1160. if (!ready_) {
  1161. isc_throw(isc::Unexpected, "Iterating past the zone end");
  1162. }
  1163. if (!data_ready_) {
  1164. // At the end of zone
  1165. accessor_->commit();
  1166. ready_ = false;
  1167. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_END);
  1168. return (ConstRRsetPtr());
  1169. }
  1170. const RRType rtype(rtype_txt_);
  1171. RRsetPtr rrset(new RRset(Name(name_txt_), class_, rtype,
  1172. RRTTL(ttl_txt_)));
  1173. // Remember the first RDATA of the RRset for comparison:
  1174. const ConstRdataPtr rdata_base = rdata_;
  1175. while (true) {
  1176. // Extend the RRset with the new RDATA.
  1177. rrset->addRdata(rdata_);
  1178. // Retrieve the next record from the database. If we reach the
  1179. // end of the zone, done; if we were requested to separate all RRs,
  1180. // just remember this record and return the single RR.
  1181. getData();
  1182. if (separate_rrs_ || !data_ready_) {
  1183. break;
  1184. }
  1185. // Check if the next record belongs to the same RRset. If not,
  1186. // we are done. The next RDATA has been stored in rdata_, which
  1187. // is used within this loop (if it belongs to the same RRset) or
  1188. // in the next call.
  1189. if (Name(name_txt_) != rrset->getName() ||
  1190. !isSameType(rtype, rdata_base, RRType(rtype_txt_), rdata_)) {
  1191. break;
  1192. }
  1193. // Adjust TTL if necessary
  1194. const RRTTL next_ttl(ttl_txt_);
  1195. if (next_ttl != rrset->getTTL()) {
  1196. if (next_ttl < rrset->getTTL()) {
  1197. rrset->setTTL(next_ttl);
  1198. }
  1199. LOG_WARN(logger, DATASRC_DATABASE_ITERATE_TTL_MISMATCH).
  1200. arg(name_txt_).arg(class_).arg(rtype).arg(rrset->getTTL());
  1201. }
  1202. }
  1203. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE_NEXT).
  1204. arg(rrset->getName()).arg(rrset->getType());
  1205. return (rrset);
  1206. }
  1207. private:
  1208. // Check two RDATA types are equivalent. Basically it's a trivial
  1209. // comparison, but if both are of RRSIG, we should also compare the types
  1210. // covered.
  1211. static bool isSameType(RRType type1, ConstRdataPtr rdata1,
  1212. RRType type2, ConstRdataPtr rdata2)
  1213. {
  1214. if (type1 != type2) {
  1215. return (false);
  1216. }
  1217. if (type1 == RRType::RRSIG()) {
  1218. return (dynamic_cast<const generic::RRSIG&>(*rdata1).typeCovered()
  1219. == dynamic_cast<const generic::RRSIG&>(*rdata2).
  1220. typeCovered());
  1221. }
  1222. return (true);
  1223. }
  1224. // Load next row of data
  1225. void getData() {
  1226. string data[DatabaseAccessor::COLUMN_COUNT];
  1227. data_ready_ = context_->getNext(data);
  1228. if (data_ready_) {
  1229. name_txt_ = data[DatabaseAccessor::NAME_COLUMN];
  1230. rtype_txt_ = data[DatabaseAccessor::TYPE_COLUMN];
  1231. ttl_txt_ = data[DatabaseAccessor::TTL_COLUMN];
  1232. rdata_ = rdata::createRdata(RRType(rtype_txt_), class_,
  1233. data[DatabaseAccessor::RDATA_COLUMN]);
  1234. }
  1235. }
  1236. // The dedicated accessor
  1237. boost::shared_ptr<DatabaseAccessor> accessor_;
  1238. // The context
  1239. DatabaseAccessor::IteratorContextPtr context_;
  1240. // Class of the zone
  1241. const RRClass class_;
  1242. // SOA of the zone, if any (it should normally exist)
  1243. ConstRRsetPtr soa_;
  1244. // Status
  1245. bool ready_, data_ready_;
  1246. // Data of the next row
  1247. string name_txt_, rtype_txt_, ttl_txt_;
  1248. // RDATA of the next row
  1249. ConstRdataPtr rdata_;
  1250. // Whether to modify differing TTL values, or treat a different TTL as
  1251. // a different RRset
  1252. const bool separate_rrs_;
  1253. };
  1254. }
  1255. ZoneIteratorPtr
  1256. DatabaseClient::getIterator(const isc::dns::Name& name,
  1257. bool separate_rrs) const
  1258. {
  1259. ZoneIteratorPtr iterator = ZoneIteratorPtr(new DatabaseIterator(
  1260. accessor_->clone(), name,
  1261. rrclass_, separate_rrs));
  1262. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ITERATE).
  1263. arg(name);
  1264. return (iterator);
  1265. }
  1266. /// \brief Database implementation of RRsetCollectionBase.
  1267. class RRsetCollection : public isc::datasrc::RRsetCollectionBase {
  1268. public:
  1269. /// \brief Constructor.
  1270. RRsetCollection(ZoneUpdater& updater, const isc::dns::RRClass& rrclass) :
  1271. isc::datasrc::RRsetCollectionBase(updater, rrclass)
  1272. {}
  1273. /// \brief A wrapper around \c disable() so that it can be used as a
  1274. /// public method. \c disable() is protected.
  1275. void disableWrapper() {
  1276. disable();
  1277. }
  1278. };
  1279. //
  1280. // Zone updater using some database system as the underlying data source.
  1281. //
  1282. class DatabaseUpdater : public ZoneUpdater {
  1283. public:
  1284. DatabaseUpdater(boost::shared_ptr<DatabaseAccessor> accessor, int zone_id,
  1285. const Name& zone_name, const RRClass& zone_class,
  1286. bool journaling) :
  1287. committed_(false), accessor_(accessor), zone_id_(zone_id),
  1288. db_name_(accessor->getDBName()), zone_name_(zone_name.toText()),
  1289. zone_class_(zone_class), journaling_(journaling),
  1290. diff_phase_(NOT_STARTED), serial_(0),
  1291. finder_(new DatabaseClient::Finder(accessor_, zone_id_, zone_name))
  1292. {
  1293. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_CREATED)
  1294. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  1295. }
  1296. virtual ~DatabaseUpdater() {
  1297. if (!committed_) {
  1298. try {
  1299. accessor_->rollback();
  1300. logger.info(DATASRC_DATABASE_UPDATER_ROLLBACK)
  1301. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  1302. } catch (const DataSourceError& e) {
  1303. // See The destructor ~TransactionHolder() for the
  1304. // reason to catch this.
  1305. logger.error(DATASRC_DATABASE_UPDATER_ROLLBACKFAIL)
  1306. .arg(zone_name_).arg(zone_class_).arg(db_name_)
  1307. .arg(e.what());
  1308. }
  1309. }
  1310. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_DESTROYED)
  1311. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  1312. }
  1313. virtual ZoneFinder& getFinder() { return (*finder_); }
  1314. virtual isc::dns::RRsetCollectionBase& getRRsetCollection() {
  1315. if (!rrset_collection_) {
  1316. // This is only assigned the first time and remains for the
  1317. // lifetime of the DatabaseUpdater.
  1318. rrset_collection_.reset(new RRsetCollection(*this, zone_class_));
  1319. }
  1320. return (*rrset_collection_);
  1321. }
  1322. virtual void addRRset(const AbstractRRset& rrset);
  1323. virtual void deleteRRset(const AbstractRRset& rrset);
  1324. virtual void commit();
  1325. private:
  1326. // A short cut typedef only for making the code shorter.
  1327. typedef DatabaseAccessor Accessor;
  1328. bool committed_;
  1329. boost::shared_ptr<DatabaseAccessor> accessor_;
  1330. const int zone_id_;
  1331. const string db_name_;
  1332. const string zone_name_;
  1333. const RRClass zone_class_;
  1334. const bool journaling_;
  1335. // For the journals
  1336. enum DiffPhase {
  1337. NOT_STARTED,
  1338. DELETE,
  1339. ADD
  1340. };
  1341. DiffPhase diff_phase_;
  1342. Serial serial_;
  1343. boost::scoped_ptr<DatabaseClient::Finder> finder_;
  1344. boost::shared_ptr<isc::datasrc::RRsetCollection> rrset_collection_;
  1345. // This is a set of validation checks commonly used for addRRset() and
  1346. // deleteRRset to minimize duplicate code logic and to make the main
  1347. // code concise.
  1348. void validateAddOrDelete(const char* const op_str,
  1349. const AbstractRRset& rrset,
  1350. DiffPhase prev_phase,
  1351. DiffPhase current_phase) const;
  1352. };
  1353. void
  1354. DatabaseUpdater::validateAddOrDelete(const char* const op_str,
  1355. const AbstractRRset& rrset,
  1356. DiffPhase prev_phase,
  1357. DiffPhase current_phase) const
  1358. {
  1359. if (committed_) {
  1360. isc_throw(DataSourceError, op_str << " attempt after commit to zone: "
  1361. << zone_name_ << "/" << zone_class_);
  1362. }
  1363. if (rrset.getRdataCount() == 0) {
  1364. isc_throw(DataSourceError, op_str << " attempt with an empty RRset: "
  1365. << rrset.getName() << "/" << zone_class_ << "/"
  1366. << rrset.getType());
  1367. }
  1368. if (rrset.getClass() != zone_class_) {
  1369. isc_throw(DataSourceError, op_str << " attempt for a different class "
  1370. << zone_name_ << "/" << zone_class_ << ": "
  1371. << rrset.toText());
  1372. }
  1373. if (rrset.getRRsig()) {
  1374. isc_throw(DataSourceError, op_str << " attempt for RRset with RRSIG "
  1375. << zone_name_ << "/" << zone_class_ << ": "
  1376. << rrset.toText());
  1377. }
  1378. if (journaling_) {
  1379. const RRType rrtype(rrset.getType());
  1380. if (rrtype == RRType::SOA() && diff_phase_ != prev_phase) {
  1381. isc_throw(isc::BadValue, op_str << " attempt in an invalid "
  1382. << "diff phase: " << diff_phase_ << ", rrset: " <<
  1383. rrset.toText());
  1384. }
  1385. if (rrtype != RRType::SOA() && diff_phase_ != current_phase) {
  1386. isc_throw(isc::BadValue, "diff state change by non SOA: "
  1387. << rrset.toText());
  1388. }
  1389. }
  1390. }
  1391. // This is a helper class used in adding/deleting RRsets to/from a database.
  1392. // The purpose of this class is to provide conversion interface from various
  1393. // parameters of the RRset to corresponding textual representations that the
  1394. // underlying database interface expects. The necessary parameters and how
  1395. // to convert them depend on several things, such as whether it's NSEC3 related
  1396. // or not, or whether journaling is requested. In order to avoid unnecessary
  1397. // conversion, this class also performs the conversion in a lazy manner.
  1398. // Also, in order to avoid redundant conversion when the conversion is
  1399. // requested for the same parameter multiple times, it remembers the
  1400. // conversion result first time, and reuses it for subsequent requests
  1401. // (this implicitly assumes copying std::string objects is not very expensive;
  1402. // this is often the case in some common implementations that have
  1403. // copy-on-write semantics for the string class).
  1404. class RRParameterConverter {
  1405. public:
  1406. RRParameterConverter(const AbstractRRset& rrset) : rrset_(rrset)
  1407. {}
  1408. const string& getName() {
  1409. if (name_.empty()) {
  1410. name_ = rrset_.getName().toText();
  1411. }
  1412. return (name_);
  1413. }
  1414. const string& getNSEC3Name() {
  1415. if (nsec3_name_.empty()) {
  1416. nsec3_name_ = rrset_.getName().split(0, 1).toText(true);
  1417. }
  1418. return (nsec3_name_);
  1419. }
  1420. const string& getRevName() {
  1421. if (revname_.empty()) {
  1422. revname_ = rrset_.getName().reverse().toText();
  1423. }
  1424. return (revname_);
  1425. }
  1426. const string& getTTL() {
  1427. if (ttl_.empty()) {
  1428. ttl_ = rrset_.getTTL().toText();
  1429. }
  1430. return (ttl_);
  1431. }
  1432. const string& getType() {
  1433. if (type_.empty()) {
  1434. type_ = rrset_.getType().toText();
  1435. }
  1436. return (type_);
  1437. }
  1438. private:
  1439. string name_;
  1440. string nsec3_name_;
  1441. string revname_;
  1442. string ttl_;
  1443. string type_;
  1444. const AbstractRRset& rrset_;
  1445. };
  1446. namespace {
  1447. // A shared shortcut to detect if the given type of RDATA is NSEC3 or
  1448. // RRSIG covering NSEC3. RRSIG for NSEC3 should go to the (conceptual)
  1449. // separate namespace, so we need to check the covered type.
  1450. // Note: in principle the type covered should be the same for
  1451. // all RDATA, but the RRset interface doesn't ensure that condition.
  1452. // So we explicitly check that for every RDATA below.
  1453. bool
  1454. isNSEC3KindType(RRType rrtype, const Rdata& rdata) {
  1455. if (rrtype == RRType::NSEC3()) {
  1456. return (true);
  1457. }
  1458. if (rrtype == RRType::RRSIG() &&
  1459. dynamic_cast<const generic::RRSIG&>(rdata).typeCovered() ==
  1460. RRType::NSEC3())
  1461. {
  1462. return (true);
  1463. }
  1464. return (false);
  1465. }
  1466. }
  1467. void
  1468. DatabaseUpdater::addRRset(const AbstractRRset& rrset) {
  1469. if (rrset_collection_) {
  1470. isc_throw(InvalidOperation,
  1471. "Cannot add RRset after an RRsetCollection has been "
  1472. "requested for ZoneUpdater for "
  1473. << zone_name_ << "/" << zone_class_ << " on "
  1474. << db_name_);
  1475. }
  1476. validateAddOrDelete("add", rrset, DELETE, ADD);
  1477. // It's guaranteed rrset has at least one RDATA at this point.
  1478. RdataIteratorPtr it = rrset.getRdataIterator();
  1479. if (journaling_) {
  1480. diff_phase_ = ADD;
  1481. if (rrset.getType() == RRType::SOA()) {
  1482. serial_ = dynamic_cast<const generic::SOA&>(it->getCurrent()).
  1483. getSerial();
  1484. }
  1485. }
  1486. RRParameterConverter cvtr(rrset);
  1487. for (; !it->isLast(); it->next()) {
  1488. const Rdata& rdata = it->getCurrent();
  1489. const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
  1490. string sigtype;
  1491. if (rrset.getType() == RRType::RRSIG()) {
  1492. // XXX: the current interface (based on the current sqlite3
  1493. // data source schema) requires a separate "sigtype" column,
  1494. // even though it won't be used in a newer implementation.
  1495. // We should eventually clean up the schema design and simplify
  1496. // the interface, but until then we have to conform to the schema.
  1497. sigtype = dynamic_cast<const generic::RRSIG&>(rdata).
  1498. typeCovered().toText();
  1499. }
  1500. const string& rdata_txt = rdata.toText();
  1501. if (journaling_) {
  1502. const string journal[Accessor::DIFF_PARAM_COUNT] =
  1503. { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
  1504. accessor_->addRecordDiff(zone_id_, serial_.getValue(),
  1505. Accessor::DIFF_ADD, journal);
  1506. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ADDDIFF).
  1507. arg(cvtr.getName()).arg(cvtr.getType()).arg(rdata_txt);
  1508. }
  1509. if (nsec3_type) {
  1510. const string nsec3_columns[Accessor::ADD_NSEC3_COLUMN_COUNT] =
  1511. { cvtr.getNSEC3Name(), cvtr.getTTL(), cvtr.getType(),
  1512. rdata_txt };
  1513. accessor_->addNSEC3RecordToZone(nsec3_columns);
  1514. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ADDNSEC3).
  1515. arg(cvtr.getNSEC3Name()).arg(rdata_txt);
  1516. } else {
  1517. const string columns[Accessor::ADD_COLUMN_COUNT] =
  1518. { cvtr.getName(), cvtr.getRevName(), cvtr.getTTL(),
  1519. cvtr.getType(), sigtype, rdata_txt };
  1520. accessor_->addRecordToZone(columns);
  1521. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_ADDRR).
  1522. arg(cvtr.getName()).arg(cvtr.getType()).arg(rdata_txt);
  1523. }
  1524. }
  1525. }
  1526. void
  1527. DatabaseUpdater::deleteRRset(const AbstractRRset& rrset) {
  1528. if (rrset_collection_) {
  1529. isc_throw(InvalidOperation,
  1530. "Cannot delete RRset after an RRsetCollection has been "
  1531. "requested for ZoneUpdater for "
  1532. << zone_name_ << "/" << zone_class_ << " on "
  1533. << db_name_);
  1534. }
  1535. // If this is the first operation, pretend we are starting a new delete
  1536. // sequence after adds. This will simplify the validation below.
  1537. if (diff_phase_ == NOT_STARTED) {
  1538. diff_phase_ = ADD;
  1539. }
  1540. validateAddOrDelete("delete", rrset, ADD, DELETE);
  1541. RdataIteratorPtr it = rrset.getRdataIterator();
  1542. if (journaling_) {
  1543. diff_phase_ = DELETE;
  1544. if (rrset.getType() == RRType::SOA()) {
  1545. serial_ =
  1546. dynamic_cast<const generic::SOA&>(it->getCurrent()).
  1547. getSerial();
  1548. }
  1549. }
  1550. RRParameterConverter cvtr(rrset);
  1551. for (; !it->isLast(); it->next()) {
  1552. const Rdata& rdata = it->getCurrent();
  1553. const bool nsec3_type = isNSEC3KindType(rrset.getType(), rdata);
  1554. const string& rdata_txt = it->getCurrent().toText();
  1555. if (journaling_) {
  1556. const string journal[Accessor::DIFF_PARAM_COUNT] =
  1557. { cvtr.getName(), cvtr.getType(), cvtr.getTTL(), rdata_txt };
  1558. accessor_->addRecordDiff(zone_id_, serial_.getValue(),
  1559. Accessor::DIFF_DELETE, journal);
  1560. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_DELETEDIFF).
  1561. arg(cvtr.getName()).arg(cvtr.getType()).arg(rdata_txt);
  1562. }
  1563. if (nsec3_type) {
  1564. const string params[Accessor::DEL_NSEC3_PARAM_COUNT] =
  1565. { cvtr.getNSEC3Name(), cvtr.getType(), rdata_txt };
  1566. accessor_->deleteNSEC3RecordInZone(params);
  1567. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_DELETENSEC3).
  1568. arg(cvtr.getNSEC3Name()).arg(rdata_txt);
  1569. } else {
  1570. const string params[Accessor::DEL_PARAM_COUNT] =
  1571. { cvtr.getName(), cvtr.getType(), rdata_txt,
  1572. cvtr.getRevName() };
  1573. accessor_->deleteRecordInZone(params);
  1574. LOG_DEBUG(logger, DBG_TRACE_DETAILED, DATASRC_DATABASE_DELETERR).
  1575. arg(cvtr.getName()).arg(cvtr.getType()).arg(rdata_txt);
  1576. }
  1577. }
  1578. }
  1579. void
  1580. DatabaseUpdater::commit() {
  1581. if (committed_) {
  1582. isc_throw(DataSourceError, "Duplicate commit attempt for "
  1583. << zone_name_ << "/" << zone_class_ << " on "
  1584. << db_name_);
  1585. }
  1586. if (journaling_ && diff_phase_ == DELETE) {
  1587. isc_throw(isc::BadValue, "Update sequence not complete");
  1588. }
  1589. accessor_->commit();
  1590. committed_ = true; // make sure the destructor won't trigger rollback
  1591. // Disable the RRsetCollection if it exists.
  1592. if (rrset_collection_) {
  1593. rrset_collection_->disableWrapper();
  1594. }
  1595. // We release the accessor immediately after commit is completed so that
  1596. // we don't hold the possible internal resource any longer.
  1597. accessor_.reset();
  1598. logger.debug(DBG_TRACE_DATA, DATASRC_DATABASE_UPDATER_COMMIT)
  1599. .arg(zone_name_).arg(zone_class_).arg(db_name_);
  1600. }
  1601. // The updater factory
  1602. ZoneUpdaterPtr
  1603. DatabaseClient::getUpdater(const isc::dns::Name& name, bool replace,
  1604. bool journaling) const
  1605. {
  1606. if (replace && journaling) {
  1607. isc_throw(isc::BadValue, "Can't store journal and replace the whole "
  1608. "zone at the same time");
  1609. }
  1610. boost::shared_ptr<DatabaseAccessor> update_accessor(accessor_->clone());
  1611. const std::pair<bool, int> zone(update_accessor->startUpdateZone(
  1612. name.toText(), replace));
  1613. if (!zone.first) {
  1614. return (ZoneUpdaterPtr());
  1615. }
  1616. return (ZoneUpdaterPtr(new DatabaseUpdater(update_accessor, zone.second,
  1617. name, rrclass_, journaling)));
  1618. }
  1619. //
  1620. // Zone journal reader using some database system as the underlying data
  1621. // source.
  1622. //
  1623. class DatabaseJournalReader : public ZoneJournalReader {
  1624. private:
  1625. // A shortcut typedef to keep the code concise.
  1626. typedef DatabaseAccessor Accessor;
  1627. public:
  1628. DatabaseJournalReader(boost::shared_ptr<Accessor> accessor, const Name& zone,
  1629. int zone_id, const RRClass& rrclass, uint32_t begin,
  1630. uint32_t end) :
  1631. accessor_(accessor), zone_(zone), rrclass_(rrclass),
  1632. begin_(begin), end_(end), finished_(false)
  1633. {
  1634. context_ = accessor_->getDiffs(zone_id, begin, end);
  1635. }
  1636. virtual ~DatabaseJournalReader() {}
  1637. virtual ConstRRsetPtr getNextDiff() {
  1638. if (finished_) {
  1639. isc_throw(InvalidOperation,
  1640. "Diff read attempt past the end of sequence on "
  1641. << accessor_->getDBName());
  1642. }
  1643. string data[Accessor::COLUMN_COUNT];
  1644. if (!context_->getNext(data)) {
  1645. finished_ = true;
  1646. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  1647. DATASRC_DATABASE_JOURNALREADER_END).
  1648. arg(zone_).arg(rrclass_).arg(accessor_->getDBName()).
  1649. arg(begin_).arg(end_);
  1650. return (ConstRRsetPtr());
  1651. }
  1652. try {
  1653. RRsetPtr rrset(new RRset(Name(data[Accessor::NAME_COLUMN]),
  1654. rrclass_,
  1655. RRType(data[Accessor::TYPE_COLUMN]),
  1656. RRTTL(data[Accessor::TTL_COLUMN])));
  1657. rrset->addRdata(rdata::createRdata(rrset->getType(), rrclass_,
  1658. data[Accessor::RDATA_COLUMN]));
  1659. LOG_DEBUG(logger, DBG_TRACE_DETAILED,
  1660. DATASRC_DATABASE_JOURNALREADER_NEXT).
  1661. arg(rrset->getName()).arg(rrset->getType()).
  1662. arg(zone_).arg(rrclass_).arg(accessor_->getDBName());
  1663. return (rrset);
  1664. } catch (const Exception& ex) {
  1665. LOG_ERROR(logger, DATASRC_DATABASE_JOURNALREADER_BADDATA).
  1666. arg(zone_).arg(rrclass_).arg(accessor_->getDBName()).
  1667. arg(begin_).arg(end_).arg(ex.what());
  1668. isc_throw(DataSourceError, "Failed to create RRset from diff on "
  1669. << accessor_->getDBName());
  1670. }
  1671. }
  1672. private:
  1673. boost::shared_ptr<Accessor> accessor_;
  1674. const Name zone_;
  1675. const RRClass rrclass_;
  1676. Accessor::IteratorContextPtr context_;
  1677. const uint32_t begin_;
  1678. const uint32_t end_;
  1679. bool finished_;
  1680. };
  1681. // The JournalReader factory
  1682. pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
  1683. DatabaseClient::getJournalReader(const isc::dns::Name& zone,
  1684. uint32_t begin_serial,
  1685. uint32_t end_serial) const
  1686. {
  1687. boost::shared_ptr<DatabaseAccessor> jnl_accessor(accessor_->clone());
  1688. const pair<bool, int> zoneinfo(jnl_accessor->getZone(zone.toText()));
  1689. if (!zoneinfo.first) {
  1690. return (pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>(
  1691. ZoneJournalReader::NO_SUCH_ZONE,
  1692. ZoneJournalReaderPtr()));
  1693. }
  1694. try {
  1695. const pair<ZoneJournalReader::Result, ZoneJournalReaderPtr> ret(
  1696. ZoneJournalReader::SUCCESS,
  1697. ZoneJournalReaderPtr(new DatabaseJournalReader(jnl_accessor,
  1698. zone,
  1699. zoneinfo.second,
  1700. rrclass_,
  1701. begin_serial,
  1702. end_serial)));
  1703. LOG_DEBUG(logger, DBG_TRACE_BASIC,
  1704. DATASRC_DATABASE_JOURNALREADER_START).arg(zone).arg(rrclass_).
  1705. arg(jnl_accessor->getDBName()).arg(begin_serial).arg(end_serial);
  1706. return (ret);
  1707. } catch (const NoSuchSerial&) {
  1708. return (pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>(
  1709. ZoneJournalReader::NO_SUCH_VERSION,
  1710. ZoneJournalReaderPtr()));
  1711. }
  1712. }
  1713. }
  1714. }