query.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Copyright (C) 2010 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 <algorithm> // for std::max
  15. #include <vector>
  16. #include <boost/foreach.hpp>
  17. #include <dns/message.h>
  18. #include <dns/rcode.h>
  19. #include <dns/rdataclass.h>
  20. #include <datasrc/client.h>
  21. #include <auth/query.h>
  22. using namespace isc::dns;
  23. using namespace isc::datasrc;
  24. using namespace isc::dns::rdata;
  25. namespace isc {
  26. namespace auth {
  27. void
  28. Query::addAdditional(ZoneFinder& zone, const RRset& rrset) {
  29. RdataIteratorPtr rdata_iterator(rrset.getRdataIterator());
  30. for (; !rdata_iterator->isLast(); rdata_iterator->next()) {
  31. const Rdata& rdata(rdata_iterator->getCurrent());
  32. if (rrset.getType() == RRType::NS()) {
  33. // Need to perform the search in the "GLUE OK" mode.
  34. const generic::NS& ns = dynamic_cast<const generic::NS&>(rdata);
  35. addAdditionalAddrs(zone, ns.getNSName(), ZoneFinder::FIND_GLUE_OK);
  36. } else if (rrset.getType() == RRType::MX()) {
  37. const generic::MX& mx(dynamic_cast<const generic::MX&>(rdata));
  38. addAdditionalAddrs(zone, mx.getMXName());
  39. }
  40. }
  41. }
  42. void
  43. Query::addAdditionalAddrs(ZoneFinder& zone, const Name& qname,
  44. const ZoneFinder::FindOptions options)
  45. {
  46. // Out of zone name
  47. NameComparisonResult result = zone.getOrigin().compare(qname);
  48. if ((result.getRelation() != NameComparisonResult::SUPERDOMAIN) &&
  49. (result.getRelation() != NameComparisonResult::EQUAL))
  50. return;
  51. // Omit additional data which has already been provided in the answer
  52. // section from the additional.
  53. //
  54. // All the address rrset with the owner name of qname have been inserted
  55. // into ANSWER section.
  56. if (qname_ == qname && qtype_ == RRType::ANY())
  57. return;
  58. // Find A rrset
  59. if (qname_ != qname || qtype_ != RRType::A()) {
  60. ZoneFinder::FindResult a_result = zone.find(qname, RRType::A(), NULL,
  61. options | dnssec_opt_);
  62. if (a_result.code == ZoneFinder::SUCCESS) {
  63. response_.addRRset(Message::SECTION_ADDITIONAL,
  64. boost::const_pointer_cast<RRset>(a_result.rrset), dnssec_);
  65. }
  66. }
  67. // Find AAAA rrset
  68. if (qname_ != qname || qtype_ != RRType::AAAA()) {
  69. ZoneFinder::FindResult aaaa_result =
  70. zone.find(qname, RRType::AAAA(), NULL, options | dnssec_opt_);
  71. if (aaaa_result.code == ZoneFinder::SUCCESS) {
  72. response_.addRRset(Message::SECTION_ADDITIONAL,
  73. boost::const_pointer_cast<RRset>(aaaa_result.rrset),
  74. dnssec_);
  75. }
  76. }
  77. }
  78. void
  79. Query::addSOA(ZoneFinder& finder) {
  80. ZoneFinder::FindResult soa_result(finder.find(finder.getOrigin(),
  81. RRType::SOA(), NULL, dnssec_opt_));
  82. if (soa_result.code != ZoneFinder::SUCCESS) {
  83. isc_throw(NoSOA, "There's no SOA record in zone " <<
  84. finder.getOrigin().toText());
  85. } else {
  86. /*
  87. * FIXME:
  88. * The const-cast is wrong, but the Message interface seems
  89. * to insist.
  90. */
  91. response_.addRRset(Message::SECTION_AUTHORITY,
  92. boost::const_pointer_cast<RRset>(soa_result.rrset), dnssec_);
  93. }
  94. }
  95. // Note: unless the data source client implementation or the zone content
  96. // is broken, 'nsec' should be a valid NSEC RR. Likewise, the call to
  97. // find() in this method should result in NXDOMAIN and an NSEC RR that proves
  98. // the non existent of matching wildcard. If these assumptions aren't met
  99. // due to a buggy data source implementation or a broken zone, we'll let
  100. // underlying libdns++ modules throw an exception, which would result in
  101. // either an SERVFAIL response or just ignoring the query. We at least prevent
  102. // a complete crash due to such broken behavior.
  103. void
  104. Query::addNXDOMAINProof(ZoneFinder& finder, ConstRRsetPtr nsec) {
  105. if (nsec->getRdataCount() == 0) {
  106. isc_throw(BadNSEC, "NSEC for NXDOMAIN is empty");
  107. return;
  108. }
  109. // Add the NSEC proving NXDOMAIN to the authority section.
  110. response_.addRRset(Message::SECTION_AUTHORITY,
  111. boost::const_pointer_cast<RRset>(nsec), dnssec_);
  112. // Next, identify the best possible wildcard name that would match
  113. // the query name. It's the longer common suffix with the qname
  114. // between the owner or the next domain of the NSEC that proves NXDOMAIN,
  115. // prefixed by the wildcard label, "*". For example, for query name
  116. // a.b.example.com, if the NXDOMAIN NSEC is
  117. // b.example.com. NSEC c.example.com., the longer suffix is b.example.com.,
  118. // and the best possible wildcard is *.b.example.com. If the NXDOMAIN
  119. // NSEC is a.example.com. NSEC c.b.example.com., the longer suffix
  120. // is the next domain of the NSEC, and we get the same wildcard name.
  121. const int qlabels = qname_.getLabelCount();
  122. const int olabels = qname_.compare(nsec->getName()).getCommonLabels();
  123. const int nlabels = qname_.compare(
  124. dynamic_cast<const generic::NSEC&>(nsec->getRdataIterator()->
  125. getCurrent()).
  126. getNextName()).getCommonLabels();
  127. const int common_labels = std::max(olabels, nlabels);
  128. const Name wildname(Name("*").concatenate(qname_.split(qlabels -
  129. common_labels)));
  130. // Confirm the wildcard doesn't exist (this should result in NXDOMAIN;
  131. // otherwise we shouldn't have got NXDOMAIN for the original query in
  132. // the first place).
  133. const ZoneFinder::FindResult fresult = finder.find(wildname,
  134. RRType::NSEC(), NULL,
  135. dnssec_opt_);
  136. if (fresult.code != ZoneFinder::NXDOMAIN || !fresult.rrset ||
  137. fresult.rrset->getRdataCount() == 0) {
  138. isc_throw(BadNSEC, "Unexpected result for wildcard NXDOMAIN proof");
  139. return;
  140. }
  141. // Add the (no-) wildcard proof only when it's different from the NSEC
  142. // that proves NXDOMAIN; sometimes they can be the same.
  143. // Note: name comparison is relatively expensive. When we are at the
  144. // stage of performance optimization, we should consider optimizing this
  145. // for some optimized data source implementations.
  146. if (nsec->getName() != fresult.rrset->getName()) {
  147. response_.addRRset(Message::SECTION_AUTHORITY,
  148. boost::const_pointer_cast<RRset>(fresult.rrset),
  149. dnssec_);
  150. }
  151. }
  152. void
  153. Query::addWildcardProof(ZoneFinder& finder) {
  154. const ZoneFinder::FindResult fresult =
  155. finder.find(qname_, RRType::NSEC(), NULL,
  156. dnssec_opt_ | ZoneFinder::NO_WILDCARD);
  157. response_.addRRset(Message::SECTION_AUTHORITY,
  158. boost::const_pointer_cast<RRset>(fresult.rrset),
  159. dnssec_);
  160. }
  161. void
  162. Query::addAuthAdditional(ZoneFinder& finder) {
  163. // Fill in authority and addtional sections.
  164. ZoneFinder::FindResult ns_result = finder.find(finder.getOrigin(),
  165. RRType::NS(), NULL,
  166. dnssec_opt_);
  167. // zone origin name should have NS records
  168. if (ns_result.code != ZoneFinder::SUCCESS) {
  169. isc_throw(NoApexNS, "There's no apex NS records in zone " <<
  170. finder.getOrigin().toText());
  171. } else {
  172. response_.addRRset(Message::SECTION_AUTHORITY,
  173. boost::const_pointer_cast<RRset>(ns_result.rrset), dnssec_);
  174. // Handle additional for authority section
  175. addAdditional(finder, *ns_result.rrset);
  176. }
  177. }
  178. void
  179. Query::process() {
  180. bool keep_doing = true;
  181. const bool qtype_is_any = (qtype_ == RRType::ANY());
  182. response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
  183. const DataSourceClient::FindResult result =
  184. datasrc_client_.findZone(qname_);
  185. // If we have no matching authoritative zone for the query name, return
  186. // REFUSED. In short, this is to be compatible with BIND 9, but the
  187. // background discussion is not that simple. See the relevant topic
  188. // at the BIND 10 developers's ML:
  189. // https://lists.isc.org/mailman/htdig/bind10-dev/2010-December/001633.html
  190. if (result.code != result::SUCCESS &&
  191. result.code != result::PARTIALMATCH) {
  192. response_.setRcode(Rcode::REFUSED());
  193. return;
  194. }
  195. ZoneFinder& zfinder = *result.zone_finder;
  196. // Found a zone which is the nearest ancestor to QNAME, set the AA bit
  197. response_.setHeaderFlag(Message::HEADERFLAG_AA);
  198. response_.setRcode(Rcode::NOERROR());
  199. while (keep_doing) {
  200. keep_doing = false;
  201. std::auto_ptr<RRsetList> target(qtype_is_any ? new RRsetList : NULL);
  202. const ZoneFinder::FindResult db_result(
  203. zfinder.find(qname_, qtype_, target.get(), dnssec_opt_));
  204. switch (db_result.code) {
  205. case ZoneFinder::DNAME: {
  206. // First, put the dname into the answer
  207. response_.addRRset(Message::SECTION_ANSWER,
  208. boost::const_pointer_cast<RRset>(db_result.rrset),
  209. dnssec_);
  210. /*
  211. * Empty DNAME should never get in, as it is impossible to
  212. * create one in master file.
  213. *
  214. * FIXME: Other way to prevent this should be done
  215. */
  216. assert(db_result.rrset->getRdataCount() > 0);
  217. // Get the data of DNAME
  218. const rdata::generic::DNAME& dname(
  219. dynamic_cast<const rdata::generic::DNAME&>(
  220. db_result.rrset->getRdataIterator()->getCurrent()));
  221. // The yet unmatched prefix dname
  222. const Name prefix(qname_.split(0, qname_.getLabelCount() -
  223. db_result.rrset->getName().getLabelCount()));
  224. // If we put it together, will it be too long?
  225. // (The prefix contains trailing ., which will be removed
  226. if (prefix.getLength() - Name::ROOT_NAME().getLength() +
  227. dname.getDname().getLength() > Name::MAX_WIRE) {
  228. /*
  229. * In case the synthesized name is too long, section 4.1
  230. * of RFC 2672 mandates we return YXDOMAIN.
  231. */
  232. response_.setRcode(Rcode::YXDOMAIN());
  233. return;
  234. }
  235. // The new CNAME we are creating (it will be unsigned even
  236. // with DNSSEC, the DNAME is signed and it can be validated
  237. // by that)
  238. RRsetPtr cname(new RRset(qname_, db_result.rrset->getClass(),
  239. RRType::CNAME(), db_result.rrset->getTTL()));
  240. // Construct the new target by replacing the end
  241. cname->addRdata(rdata::generic::CNAME(qname_.split(0,
  242. qname_.getLabelCount() -
  243. db_result.rrset->getName().getLabelCount()).
  244. concatenate(dname.getDname())));
  245. response_.addRRset(Message::SECTION_ANSWER, cname, dnssec_);
  246. break;
  247. }
  248. case ZoneFinder::CNAME:
  249. /*
  250. * We don't do chaining yet. Therefore handling a CNAME is
  251. * mostly the same as handling SUCCESS, but we didn't get
  252. * what we expected. It means no exceptions in ANY or NS
  253. * on the origin (though CNAME in origin is probably
  254. * forbidden anyway).
  255. *
  256. * So, just put it there.
  257. */
  258. response_.addRRset(Message::SECTION_ANSWER,
  259. boost::const_pointer_cast<RRset>(db_result.rrset),
  260. dnssec_);
  261. break;
  262. case ZoneFinder::SUCCESS:
  263. case ZoneFinder::WILDCARD:
  264. if (qtype_is_any) {
  265. // If quety type is ANY, insert all RRs under the domain
  266. // into answer section.
  267. BOOST_FOREACH(RRsetPtr rrset, *target) {
  268. response_.addRRset(Message::SECTION_ANSWER, rrset,
  269. dnssec_);
  270. // Handle additional for answer section
  271. addAdditional(*result.zone_finder, *rrset.get());
  272. }
  273. } else {
  274. response_.addRRset(Message::SECTION_ANSWER,
  275. boost::const_pointer_cast<RRset>(db_result.rrset),
  276. dnssec_);
  277. // Handle additional for answer section
  278. addAdditional(*result.zone_finder, *db_result.rrset);
  279. }
  280. // If apex NS records haven't been provided in the answer
  281. // section, insert apex NS records into the authority section
  282. // and AAAA/A RRS of each of the NS RDATA into the additional
  283. // section.
  284. if (qname_ != result.zone_finder->getOrigin() ||
  285. db_result.code != ZoneFinder::SUCCESS ||
  286. (qtype_ != RRType::NS() && !qtype_is_any))
  287. {
  288. addAuthAdditional(*result.zone_finder);
  289. }
  290. // If the answer is a result of wildcard substitution,
  291. // add a proof that there's no closer name.
  292. if (dnssec_ && db_result.code == ZoneFinder::WILDCARD) {
  293. addWildcardProof(*result.zone_finder);
  294. }
  295. break;
  296. case ZoneFinder::DELEGATION:
  297. response_.setHeaderFlag(Message::HEADERFLAG_AA, false);
  298. response_.addRRset(Message::SECTION_AUTHORITY,
  299. boost::const_pointer_cast<RRset>(db_result.rrset),
  300. dnssec_);
  301. addAdditional(*result.zone_finder, *db_result.rrset);
  302. break;
  303. case ZoneFinder::NXDOMAIN:
  304. response_.setRcode(Rcode::NXDOMAIN());
  305. addSOA(*result.zone_finder);
  306. if (dnssec_ && db_result.rrset) {
  307. addNXDOMAINProof(zfinder, db_result.rrset);
  308. }
  309. break;
  310. case ZoneFinder::NXRRSET:
  311. addSOA(*result.zone_finder);
  312. if (dnssec_ && db_result.rrset) {
  313. response_.addRRset(Message::SECTION_AUTHORITY,
  314. boost::const_pointer_cast<RRset>(
  315. db_result.rrset),
  316. dnssec_);
  317. }
  318. break;
  319. default:
  320. // These are new result codes (WILDCARD and WILDCARD_NXRRSET)
  321. // They should not happen from the in-memory and the database
  322. // backend isn't used yet.
  323. // TODO: Implement before letting the database backends in
  324. isc_throw(isc::NotImplemented, "Unknown result code");
  325. break;
  326. }
  327. }
  328. }
  329. }
  330. }