database.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 <vector>
  15. #include <datasrc/database.h>
  16. #include <exceptions/exceptions.h>
  17. #include <dns/name.h>
  18. #include <dns/rrttl.h>
  19. #include <dns/rdata.h>
  20. #include <dns/rdataclass.h>
  21. #include <datasrc/data_source.h>
  22. #include <datasrc/logger.h>
  23. #include <boost/foreach.hpp>
  24. using isc::dns::Name;
  25. namespace isc {
  26. namespace datasrc {
  27. DatabaseClient::DatabaseClient(boost::shared_ptr<DatabaseAccessor>
  28. database) :
  29. database_(database)
  30. {
  31. if (database_.get() == NULL) {
  32. isc_throw(isc::InvalidParameter,
  33. "No database provided to DatabaseClient");
  34. }
  35. }
  36. DataSourceClient::FindResult
  37. DatabaseClient::findZone(const Name& name) const {
  38. std::pair<bool, int> zone(database_->getZone(name));
  39. // Try exact first
  40. if (zone.first) {
  41. return (FindResult(result::SUCCESS,
  42. ZoneFinderPtr(new Finder(database_,
  43. zone.second, name))));
  44. }
  45. // Than super domains
  46. // Start from 1, as 0 is covered above
  47. for (size_t i(1); i < name.getLabelCount(); ++i) {
  48. isc::dns::Name superdomain(name.split(i));
  49. zone = database_->getZone(superdomain);
  50. if (zone.first) {
  51. return (FindResult(result::PARTIALMATCH,
  52. ZoneFinderPtr(new Finder(database_,
  53. zone.second,
  54. superdomain))));
  55. }
  56. }
  57. // No, really nothing
  58. return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
  59. }
  60. DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseAccessor>
  61. database, int zone_id,
  62. const isc::dns::Name& origin) :
  63. database_(database),
  64. zone_id_(zone_id),
  65. origin_(origin)
  66. { }
  67. namespace {
  68. // Adds the given Rdata to the given RRset
  69. // If the rrset is an empty pointer, a new one is
  70. // created with the given name, class, type and ttl
  71. // The type is checked if the rrset exists, but the
  72. // name is not.
  73. //
  74. // Then adds the given rdata to the set
  75. //
  76. // Raises a DataSourceError if the type does not
  77. // match, or if the given rdata string does not
  78. // parse correctly for the given type and class
  79. //
  80. // The DatabaseAccessor is passed to print the
  81. // database name in the log message if the TTL is
  82. // modified
  83. void addOrCreate(isc::dns::RRsetPtr& rrset,
  84. const isc::dns::Name& name,
  85. const isc::dns::RRClass& cls,
  86. const isc::dns::RRType& type,
  87. const isc::dns::RRTTL& ttl,
  88. const std::string& rdata_str,
  89. const DatabaseAccessor& db
  90. )
  91. {
  92. if (!rrset) {
  93. rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
  94. } else {
  95. // This is a check to make sure find() is not messing things up
  96. assert(type == rrset->getType());
  97. if (ttl != rrset->getTTL()) {
  98. if (ttl < rrset->getTTL()) {
  99. rrset->setTTL(ttl);
  100. }
  101. logger.info(DATASRC_DATABASE_FIND_TTL_MISMATCH)
  102. .arg(db.getDBName()).arg(name).arg(cls)
  103. .arg(type).arg(rrset->getTTL());
  104. }
  105. }
  106. try {
  107. rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
  108. } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
  109. // at this point, rrset may have been initialised for no reason,
  110. // and won't be used. But the caller would drop the shared_ptr
  111. // on such an error anyway, so we don't care.
  112. isc_throw(DataSourceError,
  113. "bad rdata in database for " << name << " "
  114. << type << ": " << ivrt.what());
  115. }
  116. }
  117. // This class keeps a short-lived store of RRSIG records encountered
  118. // during a call to find(). If the backend happens to return signatures
  119. // before the actual data, we might not know which signatures we will need
  120. // So if they may be relevant, we store the in this class.
  121. //
  122. // (If this class seems useful in other places, we might want to move
  123. // it to util. That would also provide an opportunity to add unit tests)
  124. class RRsigStore {
  125. public:
  126. // Adds the given signature Rdata to the store
  127. // The signature rdata MUST be of the RRSIG rdata type
  128. // (the caller must make sure of this).
  129. // NOTE: if we move this class to a public namespace,
  130. // we should add a type_covered argument, so as not
  131. // to have to do this cast here.
  132. void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
  133. const isc::dns::RRType& type_covered =
  134. static_cast<isc::dns::rdata::generic::RRSIG*>(
  135. sig_rdata.get())->typeCovered();
  136. sigs[type_covered].push_back(sig_rdata);
  137. }
  138. // If the store contains signatures for the type of the given
  139. // rrset, they are appended to it.
  140. void appendSignatures(isc::dns::RRsetPtr& rrset) const {
  141. std::map<isc::dns::RRType,
  142. std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
  143. found = sigs.find(rrset->getType());
  144. if (found != sigs.end()) {
  145. BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
  146. rrset->addRRsig(sig);
  147. }
  148. }
  149. }
  150. private:
  151. std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
  152. };
  153. }
  154. std::pair<bool, isc::dns::RRsetPtr>
  155. DatabaseClient::Finder::getRRset(const isc::dns::Name& name,
  156. const isc::dns::RRType* type,
  157. bool want_cname, bool want_dname,
  158. bool want_ns)
  159. {
  160. RRsigStore sig_store;
  161. database_->searchForRecords(zone_id_, name.toText());
  162. bool records_found = false;
  163. isc::dns::RRsetPtr result_rrset;
  164. std::string columns[DatabaseAccessor::COLUMN_COUNT];
  165. while (database_->getNextRecord(columns, DatabaseAccessor::COLUMN_COUNT)) {
  166. if (!records_found) {
  167. records_found = true;
  168. }
  169. try {
  170. const isc::dns::RRType cur_type(columns[DatabaseAccessor::
  171. TYPE_COLUMN]);
  172. const isc::dns::RRTTL cur_ttl(columns[DatabaseAccessor::
  173. TTL_COLUMN]);
  174. // Ths sigtype column was an optimization for finding the
  175. // relevant RRSIG RRs for a lookup. Currently this column is
  176. // not used in this revised datasource implementation. We
  177. // should either start using it again, or remove it from use
  178. // completely (i.e. also remove it from the schema and the
  179. // backend implementation).
  180. // Note that because we don't use it now, we also won't notice
  181. // it if the value is wrong (i.e. if the sigtype column
  182. // contains an rrtype that is different from the actual value
  183. // of the 'type covered' field in the RRSIG Rdata).
  184. //cur_sigtype(columns[SIGTYPE_COLUMN]);
  185. // Check for delegations before checking for the right type.
  186. // This is needed to properly delegate request for the NS
  187. // record itself.
  188. //
  189. // This happens with NS only, CNAME must be alone and DNAME
  190. // is not checked in the exact queried domain.
  191. if (want_ns && cur_type == isc::dns::RRType::NS()) {
  192. if (result_rrset &&
  193. result_rrset->getType() != isc::dns::RRType::NS()) {
  194. isc_throw(DataSourceError, "NS found together with data"
  195. " in non-apex domain " + name.toText());
  196. }
  197. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
  198. columns[DatabaseAccessor::RDATA_COLUMN],
  199. *database_);
  200. } else if (type != NULL && cur_type == *type) {
  201. if (result_rrset &&
  202. result_rrset->getType() == isc::dns::RRType::CNAME()) {
  203. isc_throw(DataSourceError, "CNAME found but it is not "
  204. "the only record for " + name.toText());
  205. } else if (result_rrset && want_ns &&
  206. result_rrset->getType() == isc::dns::RRType::NS()) {
  207. isc_throw(DataSourceError, "NS found together with data"
  208. " in non-apex domain " + name.toText());
  209. }
  210. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
  211. columns[DatabaseAccessor::RDATA_COLUMN],
  212. *database_);
  213. } else if (want_cname && cur_type == isc::dns::RRType::CNAME()) {
  214. // There should be no other data, so result_rrset should
  215. // be empty.
  216. if (result_rrset) {
  217. isc_throw(DataSourceError, "CNAME found but it is not "
  218. "the only record for " + name.toText());
  219. }
  220. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
  221. columns[DatabaseAccessor::RDATA_COLUMN],
  222. *database_);
  223. } else if (want_dname && cur_type == isc::dns::RRType::DNAME()) {
  224. // There should be max one RR of DNAME present
  225. if (result_rrset &&
  226. result_rrset->getType() == isc::dns::RRType::DNAME()) {
  227. isc_throw(DataSourceError, "DNAME with multiple RRs in " +
  228. name.toText());
  229. }
  230. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
  231. columns[DatabaseAccessor::RDATA_COLUMN],
  232. *database_);
  233. } else if (cur_type == isc::dns::RRType::RRSIG()) {
  234. // If we get signatures before we get the actual data, we
  235. // can't know which ones to keep and which to drop...
  236. // So we keep a separate store of any signature that may be
  237. // relevant and add them to the final RRset when we are
  238. // done.
  239. // A possible optimization here is to not store them for
  240. // types we are certain we don't need
  241. sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
  242. getClass(), columns[DatabaseAccessor::RDATA_COLUMN]));
  243. }
  244. } catch (const isc::dns::InvalidRRType& irt) {
  245. isc_throw(DataSourceError, "Invalid RRType in database for " <<
  246. name << ": " << columns[DatabaseAccessor::
  247. TYPE_COLUMN]);
  248. } catch (const isc::dns::InvalidRRTTL& irttl) {
  249. isc_throw(DataSourceError, "Invalid TTL in database for " <<
  250. name << ": " << columns[DatabaseAccessor::
  251. TTL_COLUMN]);
  252. } catch (const isc::dns::rdata::InvalidRdataText& ird) {
  253. isc_throw(DataSourceError, "Invalid rdata in database for " <<
  254. name << ": " << columns[DatabaseAccessor::
  255. RDATA_COLUMN]);
  256. }
  257. }
  258. if (result_rrset) {
  259. sig_store.appendSignatures(result_rrset);
  260. }
  261. return std::pair<bool, isc::dns::RRsetPtr>(records_found, result_rrset);
  262. }
  263. ZoneFinder::FindResult
  264. DatabaseClient::Finder::find(const isc::dns::Name& name,
  265. const isc::dns::RRType& type,
  266. isc::dns::RRsetList*,
  267. const FindOptions)
  268. {
  269. // This variable is used to determine the difference between
  270. // NXDOMAIN and NXRRSET
  271. bool records_found = false;
  272. isc::dns::RRsetPtr result_rrset;
  273. ZoneFinder::Result result_status = SUCCESS;
  274. std::pair<bool, isc::dns::RRsetPtr> found;
  275. logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
  276. .arg(database_->getDBName()).arg(name).arg(type);
  277. try {
  278. // First, do we have any kind of delegation (NS/DNAME) here?
  279. Name origin(getOrigin());
  280. size_t originLabelCount(origin.getLabelCount());
  281. size_t currentLabelCount(name.getLabelCount());
  282. // This is how many labels we remove to get origin
  283. size_t removeLabels(currentLabelCount - originLabelCount);
  284. // Now go trough all superdomains from origin down
  285. for (int i(removeLabels); i > 0; -- i) {
  286. Name superdomain(name.split(i));
  287. // Look if there's NS or DNAME (but ignore the NS in origin)
  288. found = getRRset(superdomain, NULL, false, true,
  289. i != removeLabels);
  290. if (found.second) {
  291. // We found something redirecting somewhere else
  292. // (it can be only NS or DNAME here)
  293. result_rrset = found.second;
  294. if (result_rrset->getType() == isc::dns::RRType::NS()) {
  295. result_status = DELEGATION;
  296. } else {
  297. result_status = DNAME;
  298. }
  299. // Don't search more
  300. break;
  301. }
  302. }
  303. if (!result_rrset) { // Only if we didn't find a redirect already
  304. // Try getting the final result and extract it
  305. // It is special if there's a CNAME or NS, DNAME is ignored here
  306. // And we don't consider the NS in origin
  307. found = getRRset(name, &type, true, false, name != origin);
  308. records_found = found.first;
  309. result_rrset = found.second;
  310. if (result_rrset && name != origin &&
  311. result_rrset->getType() == isc::dns::RRType::NS()) {
  312. result_status = DELEGATION;
  313. } else if (result_rrset && type != isc::dns::RRType::CNAME() &&
  314. result_rrset->getType() == isc::dns::RRType::CNAME()) {
  315. result_status = CNAME;
  316. }
  317. }
  318. } catch (const DataSourceError& dse) {
  319. logger.error(DATASRC_DATABASE_FIND_ERROR)
  320. .arg(database_->getDBName()).arg(dse.what());
  321. // call cleanup and rethrow
  322. database_->resetSearch();
  323. throw;
  324. } catch (const isc::Exception& isce) {
  325. logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR)
  326. .arg(database_->getDBName()).arg(isce.what());
  327. // cleanup, change it to a DataSourceError and rethrow
  328. database_->resetSearch();
  329. isc_throw(DataSourceError, isce.what());
  330. } catch (const std::exception& ex) {
  331. logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR)
  332. .arg(database_->getDBName()).arg(ex.what());
  333. database_->resetSearch();
  334. throw;
  335. }
  336. if (!result_rrset) {
  337. if (records_found) {
  338. logger.debug(DBG_TRACE_DETAILED,
  339. DATASRC_DATABASE_FOUND_NXRRSET)
  340. .arg(database_->getDBName()).arg(name)
  341. .arg(getClass()).arg(type);
  342. result_status = NXRRSET;
  343. } else {
  344. logger.debug(DBG_TRACE_DETAILED,
  345. DATASRC_DATABASE_FOUND_NXDOMAIN)
  346. .arg(database_->getDBName()).arg(name)
  347. .arg(getClass()).arg(type);
  348. result_status = NXDOMAIN;
  349. }
  350. } else {
  351. logger.debug(DBG_TRACE_DETAILED,
  352. DATASRC_DATABASE_FOUND_RRSET)
  353. .arg(database_->getDBName()).arg(*result_rrset);
  354. }
  355. return (FindResult(result_status, result_rrset));
  356. }
  357. Name
  358. DatabaseClient::Finder::getOrigin() const {
  359. return (origin_);
  360. }
  361. isc::dns::RRClass
  362. DatabaseClient::Finder::getClass() const {
  363. // TODO Implement
  364. return isc::dns::RRClass::IN();
  365. }
  366. }
  367. }