database.cc 62 KB

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