database.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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 <datasrc/database.h>
  15. #include <exceptions/exceptions.h>
  16. #include <dns/name.h>
  17. #include <dns/rrttl.h>
  18. #include <dns/rdata.h>
  19. #include <dns/rdataclass.h>
  20. #include <datasrc/data_source.h>
  21. #include <boost/foreach.hpp>
  22. using isc::dns::Name;
  23. namespace isc {
  24. namespace datasrc {
  25. DatabaseClient::DatabaseClient(boost::shared_ptr<DatabaseConnection>
  26. connection) :
  27. connection_(connection)
  28. {
  29. if (connection_.get() == NULL) {
  30. isc_throw(isc::InvalidParameter,
  31. "No connection provided to DatabaseClient");
  32. }
  33. }
  34. DataSourceClient::FindResult
  35. DatabaseClient::findZone(const Name& name) const {
  36. std::pair<bool, int> zone(connection_->getZone(name));
  37. // Try exact first
  38. if (zone.first) {
  39. return (FindResult(result::SUCCESS,
  40. ZoneFinderPtr(new Finder(connection_,
  41. zone.second))));
  42. }
  43. // Than super domains
  44. // Start from 1, as 0 is covered above
  45. for (size_t i(1); i < name.getLabelCount(); ++i) {
  46. zone = connection_->getZone(name.split(i));
  47. if (zone.first) {
  48. return (FindResult(result::PARTIALMATCH,
  49. ZoneFinderPtr(new Finder(connection_,
  50. zone.second))));
  51. }
  52. }
  53. // No, really nothing
  54. return (FindResult(result::NOTFOUND, ZoneFinderPtr()));
  55. }
  56. DatabaseClient::Finder::Finder(boost::shared_ptr<DatabaseConnection>
  57. connection, int zone_id) :
  58. connection_(connection),
  59. zone_id_(zone_id)
  60. { }
  61. namespace {
  62. // Adds the given Rdata to the given RRset
  63. // If the rrset does not exist, one is created
  64. // adds the given rdata to the set
  65. void addOrCreate(isc::dns::RRsetPtr& rrset,
  66. const isc::dns::Name& name,
  67. const isc::dns::RRClass& cls,
  68. const isc::dns::RRType& type,
  69. const isc::dns::RRTTL& ttl,
  70. const std::string& rdata_str)
  71. {
  72. if (!rrset) {
  73. rrset.reset(new isc::dns::RRset(name, cls, type, ttl));
  74. } else {
  75. if (ttl < rrset->getTTL()) {
  76. rrset->setTTL(ttl);
  77. }
  78. // make sure the type is correct
  79. if (type != rrset->getType()) {
  80. isc_throw(DataSourceError,
  81. "attempt to add multiple types to RRset in find()");
  82. }
  83. }
  84. if (rdata_str != "") {
  85. try {
  86. rrset->addRdata(isc::dns::rdata::createRdata(type, cls, rdata_str));
  87. } catch (const isc::dns::rdata::InvalidRdataText& ivrt) {
  88. // at this point, rrset may have been initialised for no reason,
  89. // and won't be used. But the caller would drop the shared_ptr
  90. // on such an error anyway, so we don't care.
  91. isc_throw(DataSourceError,
  92. "bad rdata in database for " << name.toText() << " "
  93. << type.toText() << " " << ivrt.what());
  94. }
  95. }
  96. }
  97. // This class keeps a short-lived store of RRSIG records encountered
  98. // during a call to find(). If the backend happens to return signatures
  99. // before the actual data, we might not know which signatures we will need
  100. // So if they may be relevant, we store the in this class.
  101. //
  102. // (If this class seems useful in other places, we might want to move
  103. // it to util. That would also provide an opportunity to add unit tests)
  104. class RRsigStore {
  105. public:
  106. // add the given signature Rdata to the store
  107. // The signature MUST be of the RRSIG type (the caller
  108. // must make sure of this)
  109. void addSig(isc::dns::rdata::RdataPtr sig_rdata) {
  110. const isc::dns::RRType& type_covered =
  111. static_cast<isc::dns::rdata::generic::RRSIG*>(
  112. sig_rdata.get())->typeCovered();
  113. if (!haveSigsFor(type_covered)) {
  114. sigs[type_covered] = std::vector<isc::dns::rdata::RdataPtr>();
  115. }
  116. sigs.find(type_covered)->second.push_back(sig_rdata);
  117. }
  118. // Returns true if this store contains signatures covering the
  119. // given type
  120. bool haveSigsFor(isc::dns::RRType type) {
  121. return (sigs.count(type) > 0);
  122. }
  123. // If the store contains signatures for the type of the given
  124. // rrset, they are appended to it.
  125. void appendSignatures(isc::dns::RRsetPtr& rrset) {
  126. if (haveSigsFor(rrset->getType())) {
  127. BOOST_FOREACH(isc::dns::rdata::RdataPtr sig,
  128. sigs.find(rrset->getType())->second) {
  129. rrset->addRRsig(sig);
  130. }
  131. }
  132. }
  133. private:
  134. std::map<isc::dns::RRType, std::vector<isc::dns::rdata::RdataPtr> > sigs;
  135. };
  136. }
  137. ZoneFinder::FindResult
  138. DatabaseClient::Finder::find(const isc::dns::Name& name,
  139. const isc::dns::RRType& type,
  140. isc::dns::RRsetList*,
  141. const FindOptions) const
  142. {
  143. // This variable is used to determine the difference between
  144. // NXDOMAIN and NXRRSET
  145. bool records_found = false;
  146. isc::dns::RRsetPtr result_rrset;
  147. ZoneFinder::Result result_status = SUCCESS;
  148. RRsigStore sig_store;
  149. connection_->searchForRecords(zone_id_, name.toText());
  150. std::vector<std::string> columns;
  151. while (connection_->getNextRecord(columns)) {
  152. if (!records_found) {
  153. records_found = true;
  154. }
  155. if (columns.size() != 4) {
  156. isc_throw(DataSourceError,
  157. "Datasource backend did not return 4 columns in getNextRecord()");
  158. }
  159. try {
  160. const isc::dns::RRType cur_type(columns[0]);
  161. const isc::dns::RRTTL cur_ttl(columns[1]);
  162. //cur_sigtype(columns[2]);
  163. if (cur_type == type) {
  164. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]);
  165. //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3]));
  166. } else if (cur_type == isc::dns::RRType::CNAME()) {
  167. // There should be no other data, so cur_rrset should be empty,
  168. // except for signatures, of course
  169. if (result_rrset) {
  170. if (result_rrset->getRdataCount() > 0) {
  171. isc_throw(DataSourceError, "CNAME found but it is not the only record for " + name.toText());
  172. }
  173. }
  174. addOrCreate(result_rrset, name, getClass(), cur_type, cur_ttl, columns[3]);
  175. //isc::dns::rdata::createRdata(cur_type, getClass(), columns[3]));
  176. result_status = CNAME;
  177. } else if (cur_type == isc::dns::RRType::RRSIG()) {
  178. // If we get signatures before we get the actual data, we can't know
  179. // which ones to keep and which to drop...
  180. // So we keep a separate store of any signature that may be relevant
  181. // and add them to the final RRset when we are done.
  182. isc::dns::rdata::RdataPtr cur_rrsig(
  183. isc::dns::rdata::createRdata(cur_type, getClass(), columns[3]));
  184. sig_store.addSig(cur_rrsig);
  185. }
  186. } catch (const isc::dns::InvalidRRType& irt) {
  187. isc_throw(DataSourceError,
  188. "Invalid RRType in database for " << name << ": " << columns[0]);
  189. } catch (const isc::dns::InvalidRRTTL& irttl) {
  190. isc_throw(DataSourceError,
  191. "Invalid TTL in database for " << name << ": " << columns[1]);
  192. }
  193. }
  194. if (!result_rrset) {
  195. if (records_found) {
  196. result_status = NXRRSET;
  197. } else {
  198. result_status = NXDOMAIN;
  199. }
  200. } else {
  201. sig_store.appendSignatures(result_rrset);
  202. }
  203. return (FindResult(result_status, result_rrset));
  204. }
  205. Name
  206. DatabaseClient::Finder::getOrigin() const {
  207. // TODO Implement
  208. return (Name("."));
  209. }
  210. isc::dns::RRClass
  211. DatabaseClient::Finder::getClass() const {
  212. // TODO Implement
  213. return isc::dns::RRClass::IN();
  214. }
  215. }
  216. }