database.cc 75 KB

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