query_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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 <dns/message.h>
  15. #include <dns/name.h>
  16. #include <dns/rcode.h>
  17. #include <dns/rrttl.h>
  18. #include <dns/rrtype.h>
  19. #include <dns/rdataclass.h>
  20. #include <datasrc/memory_datasrc.h>
  21. #include <auth/query.h>
  22. #include <gtest/gtest.h>
  23. using namespace isc::dns;
  24. using namespace isc::datasrc;
  25. using namespace isc::auth;
  26. namespace {
  27. RRsetPtr a_rrset = RRsetPtr(new RRset(Name("www.example.com"),
  28. RRClass::IN(), RRType::A(),
  29. RRTTL(3600)));
  30. RRsetPtr soa_rrset = RRsetPtr(new RRset(Name("example.com"),
  31. RRClass::IN(), RRType::SOA(),
  32. RRTTL(3600)));
  33. RRsetPtr ns_rrset(RRsetPtr(new RRset(Name("ns.example.com"),
  34. RRClass::IN(), RRType::NS(),
  35. RRTTL(3600))));
  36. RRsetPtr glue_a_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
  37. RRClass::IN(), RRType::A(),
  38. RRTTL(3600))));
  39. RRsetPtr glue_aaaa_rrset(RRsetPtr(new RRset(Name("glue.ns.example.com"),
  40. RRClass::IN(), RRType::AAAA(),
  41. RRTTL(3600))));
  42. RRsetPtr noglue_a_rrset(RRsetPtr(new RRset(Name("noglue.example.com"),
  43. RRClass::IN(), RRType::A(),
  44. RRTTL(3600))));
  45. RRsetPtr delegated_mx_a_rrset(RRsetPtr(new RRset(
  46. Name("mx.delegation.example.com"), RRClass::IN(), RRType::A(),
  47. RRTTL(3600))));
  48. // This is a mock Zone class for testing.
  49. // It is a derived class of Zone, and simply hardcodes the results of find()
  50. // See the find() implementation if you want to know its content.
  51. class MockZone : public Zone {
  52. public:
  53. MockZone(bool has_SOA = true) :
  54. origin_(Name("example.com")),
  55. has_SOA_(has_SOA),
  56. delegation_rrset(RRsetPtr(new RRset(Name("delegation.example.com"),
  57. RRClass::IN(), RRType::NS(),
  58. RRTTL(3600)))),
  59. cname_rrset(RRsetPtr(new RRset(Name("cname.example.com"),
  60. RRClass::IN(), RRType::CNAME(),
  61. RRTTL(3600)))),
  62. mx_rrset_(new RRset(Name("mx.example.com"), RRClass::IN(),
  63. RRType::MX(), RRTTL(3600)))
  64. {
  65. delegation_rrset->addRdata(rdata::generic::NS(
  66. Name("glue.ns.example.com")));
  67. delegation_rrset->addRdata(rdata::generic::NS(
  68. Name("noglue.example.com")));
  69. delegation_rrset->addRdata(rdata::generic::NS(
  70. Name("cname.example.com")));
  71. delegation_rrset->addRdata(rdata::generic::NS(
  72. Name("example.org")));
  73. cname_rrset->addRdata(rdata::generic::CNAME(
  74. Name("www.example.com")));
  75. mx_rrset_->addRdata(isc::dns::rdata::generic::MX(10,
  76. Name("www.example.com")));
  77. mx_rrset_->addRdata(isc::dns::rdata::generic::MX(20,
  78. Name("mailer.example.org")));
  79. mx_rrset_->addRdata(isc::dns::rdata::generic::MX(30,
  80. Name("mx.delegation.example.com")));
  81. }
  82. virtual const isc::dns::Name& getOrigin() const;
  83. virtual const isc::dns::RRClass& getClass() const;
  84. FindResult find(const isc::dns::Name& name,
  85. const isc::dns::RRType& type,
  86. const FindOptions options = FIND_DEFAULT) const;
  87. private:
  88. Name origin_;
  89. bool has_SOA_;
  90. RRsetPtr delegation_rrset;
  91. RRsetPtr cname_rrset;
  92. RRsetPtr mx_rrset_;
  93. };
  94. const Name&
  95. MockZone::getOrigin() const {
  96. return (origin_);
  97. }
  98. const RRClass&
  99. MockZone::getClass() const {
  100. return (RRClass::IN());
  101. }
  102. Zone::FindResult
  103. MockZone::find(const Name& name, const RRType& type,
  104. const FindOptions options) const
  105. {
  106. // hardcode the find results
  107. if (name == Name("www.example.com") && type == RRType::A()) {
  108. return (FindResult(SUCCESS, a_rrset));
  109. } else if (name == Name("www.example.com")) {
  110. return (FindResult(NXRRSET, RRsetPtr()));
  111. } else if (name == Name("glue.ns.example.com") && type == RRType::A() &&
  112. (options & FIND_GLUE_OK)) {
  113. return (FindResult(SUCCESS, glue_a_rrset));
  114. } else if (name == Name("noglue.example.com") && type == RRType::A()) {
  115. return (FindResult(SUCCESS, noglue_a_rrset));
  116. } else if (name == Name("glue.ns.example.com") && type == RRType::AAAA() &&
  117. (options & FIND_GLUE_OK)) {
  118. return (FindResult(SUCCESS, glue_aaaa_rrset));
  119. } else if (name == Name("example.com") && type == RRType::SOA() &&
  120. has_SOA_)
  121. {
  122. return (FindResult(SUCCESS, soa_rrset));
  123. } else if (name == Name("mx.delegation.example.com") &&
  124. type == RRType::A() && (options & FIND_GLUE_OK))
  125. {
  126. return (FindResult(SUCCESS, delegated_mx_a_rrset));
  127. } else if (name == Name("delegation.example.com") ||
  128. name.compare(Name("delegation.example.com")).getRelation() ==
  129. NameComparisonResult::SUBDOMAIN)
  130. {
  131. return (FindResult(DELEGATION, delegation_rrset));
  132. } else if (name == Name("ns.example.com")) {
  133. return (FindResult(DELEGATION, ns_rrset));
  134. } else if (name == Name("nxdomain.example.com")) {
  135. return (FindResult(NXDOMAIN, RRsetPtr()));
  136. } else if (name == Name("nxrrset.example.com")) {
  137. return (FindResult(NXRRSET, RRsetPtr()));
  138. } else if ((name == Name("cname.example.com"))) {
  139. return (FindResult(CNAME, cname_rrset));
  140. } else if (name == Name("mx.example.com")) {
  141. return (FindResult(SUCCESS, mx_rrset_));
  142. } else {
  143. return (FindResult(DNAME, RRsetPtr()));
  144. }
  145. }
  146. class QueryTest : public ::testing::Test {
  147. protected:
  148. QueryTest() :
  149. qname(Name("www.example.com")), qclass(RRClass::IN()),
  150. qtype(RRType::A()), response(Message::RENDER),
  151. query(memory_datasrc, qname, qtype, response)
  152. {
  153. response.setRcode(Rcode::NOERROR());
  154. }
  155. MemoryDataSrc memory_datasrc;
  156. const Name qname;
  157. const RRClass qclass;
  158. const RRType qtype;
  159. Message response;
  160. Query query;
  161. };
  162. TEST_F(QueryTest, noZone) {
  163. // There's no zone in the memory datasource. So the response should have
  164. // REFUSED.
  165. query.process();
  166. EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
  167. }
  168. TEST_F(QueryTest, matchZone) {
  169. // add a matching zone.
  170. memory_datasrc.addZone(ZonePtr(new MockZone()));
  171. query.process();
  172. EXPECT_TRUE(response.getHeaderFlag(Message::HEADERFLAG_AA));
  173. EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
  174. EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
  175. Name("www.example.com"), RRClass::IN(),
  176. RRType::A()));
  177. // Delegation
  178. const Name delegation_name(Name("delegation.example.com"));
  179. Query delegation_query(memory_datasrc, delegation_name, qtype, response);
  180. delegation_query.process();
  181. EXPECT_FALSE(response.getHeaderFlag(Message::HEADERFLAG_AA));
  182. EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
  183. EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
  184. Name("delegation.example.com"),
  185. RRClass::IN(), RRType::NS()));
  186. // glue address records
  187. EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
  188. Name("glue.ns.example.com"),
  189. RRClass::IN(), RRType::A()));
  190. EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
  191. Name("glue.ns.example.com"),
  192. RRClass::IN(), RRType::AAAA()));
  193. // noglue address records
  194. EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
  195. Name("noglue.example.com"),
  196. RRClass::IN(), RRType::A()));
  197. // NS name has a CNAME
  198. EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
  199. Name("www.example.com"),
  200. RRClass::IN(), RRType::A()));
  201. // NS name is out of zone
  202. EXPECT_FALSE(response.hasRRset(Message::SECTION_ADDITIONAL,
  203. Name("example.org"),
  204. RRClass::IN(), RRType::A()));
  205. // NXDOMAIN
  206. const Name nxdomain_name(Name("nxdomain.example.com"));
  207. Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
  208. nxdomain_query.process();
  209. EXPECT_EQ(Rcode::NXDOMAIN(), response.getRcode());
  210. EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
  211. EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
  212. EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
  213. Name("example.com"), RRClass::IN(), RRType::SOA()));
  214. // NXRRSET
  215. const Name nxrrset_name(Name("nxrrset.example.com"));
  216. Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
  217. nxrrset_query.process();
  218. EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
  219. EXPECT_EQ(0, response.getRRCount(Message::SECTION_ANSWER));
  220. EXPECT_EQ(0, response.getRRCount(Message::SECTION_ADDITIONAL));
  221. EXPECT_TRUE(response.hasRRset(Message::SECTION_AUTHORITY,
  222. Name("example.com"), RRClass::IN(), RRType::SOA()));
  223. }
  224. /*
  225. * This tests that when there's no SOA and we need a negative answer. It should
  226. * throw in that case.
  227. */
  228. TEST_F(QueryTest, noSOA) {
  229. memory_datasrc.addZone(ZonePtr(new MockZone(false)));
  230. // The NX Domain
  231. const Name nxdomain_name(Name("nxdomain.example.com"));
  232. Query nxdomain_query(memory_datasrc, nxdomain_name, qtype, response);
  233. EXPECT_THROW(nxdomain_query.process(), Query::NoSOA);
  234. // Of course, we don't look into the response, as it throwed
  235. // NXRRSET
  236. const Name nxrrset_name(Name("nxrrset.example.com"));
  237. Query nxrrset_query(memory_datasrc, nxrrset_name, qtype, response);
  238. EXPECT_THROW(nxrrset_query.process(), Query::NoSOA);
  239. }
  240. TEST_F(QueryTest, noMatchZone) {
  241. // there's a zone in the memory datasource but it doesn't match the qname.
  242. // should result in REFUSED.
  243. memory_datasrc.addZone(ZonePtr(new MockZone()));
  244. const Name nomatch_name(Name("example.org"));
  245. Query nomatch_query(memory_datasrc, nomatch_name, qtype, response);
  246. nomatch_query.process();
  247. EXPECT_EQ(Rcode::REFUSED(), response.getRcode());
  248. }
  249. /*
  250. * Test MX additional processing.
  251. *
  252. * The MX RRset has two RRs, one pointing to a known domain with
  253. * A record, other to unknown out of zone one.
  254. */
  255. TEST_F(QueryTest, MX) {
  256. memory_datasrc.addZone(ZonePtr(new MockZone()));
  257. Name qname("mx.example.com");
  258. Query mx_query(memory_datasrc, qname, RRType::MX(), response);
  259. EXPECT_NO_THROW(mx_query.process());
  260. EXPECT_EQ(Rcode::NOERROR(), response.getRcode());
  261. EXPECT_TRUE(response.hasRRset(Message::SECTION_ANSWER,
  262. Name("mx.example.com"), RRClass::IN(), RRType::MX()));
  263. EXPECT_TRUE(response.hasRRset(Message::SECTION_ADDITIONAL,
  264. Name("www.example.com"), RRClass::IN(), RRType::A()));
  265. /*
  266. * In fact, the MX RRset mentions three names, but we don't know anything
  267. * about one of them and one is under a zone cut, so we should have just
  268. * one RRset (A for www.example.com)
  269. */
  270. // We can't use getRRCount, as it counts RRs, not RRsets
  271. unsigned additional_count(0);
  272. for (SectionIterator<RRsetPtr> ai(response.beginSection(
  273. Message::SECTION_ADDITIONAL)); ai != response.endSection(
  274. Message::SECTION_ADDITIONAL); ++ai)
  275. {
  276. additional_count++;
  277. EXPECT_EQ(Name("www.example.com"), (*ai)->getName());
  278. EXPECT_EQ(RRType::A(), (*ai)->getType());
  279. }
  280. EXPECT_EQ(1, additional_count);
  281. }
  282. }