database.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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<DatabaseConnection>
  28. connection) :
  29. connection_(connection)
  30. {
  31. if (connection_.get() == NULL) {
  32. isc_throw(isc::InvalidParameter,
  33. "No connection provided to DatabaseClient");
  34. }
  35. }
  36. DataSourceClient::FindResult
  37. DatabaseClient::findZone(const Name& name) const {
  38. std::pair<bool, int> zone(connection_->getZone(name));
  39. // Try exact first
  40. if (zone.first) {
  41. return (FindResult(result::SUCCESS,
  42. ZoneFinderPtr(new Finder(connection_,
  43. zone.second))));
  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. zone = connection_->getZone(name.split(i));
  49. if (zone.first) {
  50. return (FindResult(result::PARTIALMATCH,
  51. ZoneFinderPtr(new Finder(connection_,
  52. zone.second))));
  53. }
  54. }
  55. // No, really nothing
  56. return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
  57. }
  58. DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseConnection>
  59. connection, int zone_id) :
  60. connection_(connection),
  61. zone_id_(zone_id)
  62. { }
  63. namespace {
  64. // Adds the given Rdata to the given RRset
  65. // If the rrset is an empty pointer, a new one is
  66. // created with the given name, class, type and ttl
  67. // The type is checked if the rrset exists, but the
  68. // name is not.
  69. //
  70. // Then adds the given rdata to the set
  71. //
  72. // Raises a DataSourceError if the type does not
  73. // match, or if the given rdata string does not
  74. // parse correctly for the given type and class
  75. //
  76. // The DatabaseConnection is passed to print the
  77. // database name in the log message if the TTL is
  78. // modified
  79. void addOrCreate(isc::dns::RRsetPtr& rrset,
  80. const isc::dns::Name& name,
  81. const isc::dns::RRClass& cls,
  82. const isc::dns::RRType& type,
  83. const isc::dns::RRTTL& ttl,
  84. const std::string& rdata_str,
  85. const DatabaseConnection& conn
  86. )
  87. {
  88. if (!rrset) {
  89. rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
  90. } else {
  91. // This is a check to make sure find() is not messing things up
  92. assert(type == rrset->getType());
  93. if (ttl != rrset->getTTL()) {
  94. if (ttl < rrset->getTTL()) {
  95. rrset->setTTL(ttl);
  96. }
  97. logger.info(DATASRC_DATABASE_FIND_TTL_MISMATCH)
  98. .arg(conn.getDBName()).arg(name).arg(cls)
  99. .arg(type).arg(rrset->getTTL());
  100. }
  101. }
  102. try {
  103. rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
  104. } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
  105. // at this point, rrset may have been initialised for no reason,
  106. // and won't be used. But the caller would drop the shared_ptr
  107. // on such an error anyway, so we don't care.
  108. isc_throw(DataSourceError,
  109. "bad rdata in database for " << name << " "
  110. << type << ": " << ivrt.what());
  111. }
  112. }
  113. // This class keeps a short-lived store of RRSIG records encountered
  114. // during a call to find(). If the backend happens to return signatures
  115. // before the actual data, we might not know which signatures we will need
  116. // So if they may be relevant, we store the in this class.
  117. //
  118. // (If this class seems useful in other places, we might want to move
  119. // it to util. That would also provide an opportunity to add unit tests)
  120. class RRsigStore {
  121. public:
  122. // Adds the given signature Rdata to the store
  123. // The signature rdata MUST be of the RRSIG rdata type
  124. // (the caller must make sure of this).
  125. // NOTE: if we move this class to a public namespace,
  126. // we should add a type_covered argument, so as not
  127. // to have to do this cast here.
  128. void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
  129. const isc::dns::RRType& type_covered =
  130. static_cast<isc::dns::rdata::generic::RRSIG*>(
  131. sig_rdata.get())->typeCovered();
  132. sigs[type_covered].push_back(sig_rdata);
  133. }
  134. // If the store contains signatures for the type of the given
  135. // rrset, they are appended to it.
  136. void appendSignatures(isc::dns::RRsetPtr& rrset) const {
  137. std::map<isc::dns::RRType,
  138. std::vector<isc::dns::rdata::RdataPtr> >::const_iterator
  139. found = sigs.find(rrset->getType());
  140. if (found != sigs.end()) {
  141. BOOST_FOREACH(isc::dns::rdata::RdataPtr sig, found->second) {
  142. rrset->addRRsig(sig);
  143. }
  144. }
  145. }
  146. private:
  147. std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
  148. };
  149. }
  150. ZoneFinder::FindResult
  151. DatabaseClient::Finder::find(const isc::dns::Name& name,
  152. const isc::dns::RRType& type,
  153. isc::dns::RRsetList*,
  154. const FindOptions)
  155. {
  156. // This variable is used to determine the difference between
  157. // NXDOMAIN and NXRRSET
  158. bool records_found = false;
  159. isc::dns::RRsetPtr result_rrset;
  160. ZoneFinder::Result result_status = SUCCESS;
  161. RRsigStore sig_store;
  162. logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FIND_RECORDS)
  163. .arg(connection_->getDBName()).arg(name).arg(type);
  164. try {
  165. connection_->searchForRecords(zone_id_, name.toText());
  166. std::string columns[DatabaseConnection::COLUMN_COUNT];
  167. while (connection_->getNextRecord(columns,
  168. DatabaseConnection::COLUMN_COUNT)) {
  169. if (!records_found) {
  170. records_found = true;
  171. }
  172. try {
  173. const isc::dns::RRType cur_type(columns[DatabaseConnection::TYPE_COLUMN]);
  174. const isc::dns::RRTTL cur_ttl(columns[DatabaseConnection::TTL_COLUMN]);
  175. // Ths sigtype column was an optimization for finding the
  176. // relevant RRSIG RRs for a lookup. Currently this column is
  177. // not used in this revised datasource implementation. We
  178. // should either start using it again, or remove it from use
  179. // completely (i.e. also remove it from the schema and the
  180. // backend implementation).
  181. // Note that because we don't use it now, we also won't notice
  182. // it if the value is wrong (i.e. if the sigtype column
  183. // contains an rrtype that is different from the actual value
  184. // of the 'type covered' field in the RRSIG Rdata).
  185. //cur_sigtype(columns[SIGTYPE_COLUMN]);
  186. if (cur_type == type) {
  187. if (result_rrset &&
  188. result_rrset->getType() == isc::dns::RRType::CNAME()) {
  189. isc_throw(DataSourceError, "CNAME found but it is not "
  190. "the only record for " + name.toText());
  191. }
  192. addOrCreate(result_rrset, name, getClass(), cur_type,
  193. cur_ttl, columns[DatabaseConnection::RDATA_COLUMN],
  194. *connection_);
  195. } else if (cur_type == isc::dns::RRType::CNAME()) {
  196. // There should be no other data, so result_rrset should
  197. // be empty.
  198. if (result_rrset) {
  199. isc_throw(DataSourceError, "CNAME found but it is not "
  200. "the only record for " + name.toText());
  201. }
  202. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl,
  203. columns[DatabaseConnection::RDATA_COLUMN],
  204. *connection_);
  205. result_status = CNAME;
  206. } else if (cur_type == isc::dns::RRType::RRSIG()) {
  207. // If we get signatures before we get the actual data, we
  208. // can't know which ones to keep and which to drop...
  209. // So we keep a separate store of any signature that may be
  210. // relevant and add them to the final RRset when we are
  211. // done.
  212. // A possible optimization here is to not store them for
  213. // types we are certain we don't need
  214. sig_store.addSig(isc::dns::rdata::createRdata(cur_type,
  215. getClass(),
  216. columns[DatabaseConnection::RDATA_COLUMN]));
  217. }
  218. } catch (const isc::dns::InvalidRRType& irt) {
  219. isc_throw(DataSourceError, "Invalid RRType in database for " <<
  220. name << ": " << columns[DatabaseConnection::TYPE_COLUMN]);
  221. } catch (const isc::dns::InvalidRRTTL& irttl) {
  222. isc_throw(DataSourceError, "Invalid TTL in database for " <<
  223. name << ": " << columns[DatabaseConnection::TTL_COLUMN]);
  224. } catch (const isc::dns::rdata::InvalidRdataText& ird) {
  225. isc_throw(DataSourceError, "Invalid rdata in database for " <<
  226. name << ": " << columns[DatabaseConnection::RDATA_COLUMN]);
  227. }
  228. }
  229. } catch (const DataSourceError& dse) {
  230. logger.error(DATASRC_DATABASE_FIND_ERROR)
  231. .arg(connection_->getDBName()).arg(dse.what());
  232. // call cleanup and rethrow
  233. connection_->resetSearch();
  234. throw;
  235. } catch (const isc::Exception& isce) {
  236. logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ISC_ERROR)
  237. .arg(connection_->getDBName()).arg(isce.what());
  238. // cleanup, change it to a DataSourceError and rethrow
  239. connection_->resetSearch();
  240. isc_throw(DataSourceError, isce.what());
  241. } catch (const std::exception& ex) {
  242. logger.error(DATASRC_DATABASE_FIND_UNCAUGHT_ERROR)
  243. .arg(connection_->getDBName()).arg(ex.what());
  244. connection_->resetSearch();
  245. throw;
  246. }
  247. if (!result_rrset) {
  248. if (records_found) {
  249. logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXRRSET)
  250. .arg(name).arg(getClass()).arg(type);
  251. result_status = NXRRSET;
  252. } else {
  253. logger.debug(DBG_TRACE_DETAILED, DATASRC_DATABASE_FOUND_NXDOMAIN)
  254. .arg(name).arg(getClass()).arg(type);
  255. result_status = NXDOMAIN;
  256. }
  257. } else {
  258. sig_store.appendSignatures(result_rrset);
  259. logger.debug(DBG_TRACE_DETAILED,
  260. DATASRC_DATABASE_FOUND_RRSET).arg(*result_rrset);
  261. }
  262. return (FindResult(result_status, result_rrset));
  263. }
  264. Name
  265. DatabaseClient::Finder::getOrigin() const {
  266. // TODO Implement
  267. return (Name("."));
  268. }
  269. isc::dns::RRClass
  270. DatabaseClient::Finder::getClass() const {
  271. // TODO Implement
  272. return isc::dns::RRClass::IN();
  273. }
  274. }
  275. }