nameserver_address_store_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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. // $Id$
  15. #include <config.h>
  16. /// \brief Test Deleter Objects
  17. ///
  18. /// This file contains tests for the "deleter" classes within the nameserver
  19. /// address store. These act to delete zones from the zone hash table when
  20. /// the element reaches the top of the LRU list.
  21. #include <dns/rrttl.h>
  22. #include <dns/rdataclass.h>
  23. #include <dns/rrclass.h>
  24. #include <gtest/gtest.h>
  25. #include <boost/shared_ptr.hpp>
  26. #include <boost/lexical_cast.hpp>
  27. #include <boost/foreach.hpp>
  28. #include <string.h>
  29. #include <cassert>
  30. #include "../nameserver_address_store.h"
  31. #include "../nsas_entry_compare.h"
  32. #include "../nameserver_entry.h"
  33. #include "../zone_entry.h"
  34. #include "../address_request_callback.h"
  35. #include "nsas_test.h"
  36. using namespace isc::dns;
  37. using namespace std;
  38. namespace isc {
  39. namespace nsas {
  40. /// \brief NSAS Store
  41. ///
  42. /// This is a subclass of the NameserverAddressStore class, with methods to
  43. /// access the internal members to check that the deleter objects are working.
  44. class DerivedNsas : public NameserverAddressStore {
  45. public:
  46. /// \brief Constructor
  47. ///
  48. /// \param hashsize Size of the zone hash table
  49. /// \param lrusize Size of the zone hash table
  50. DerivedNsas(boost::shared_ptr<TestResolver> resolver, uint32_t hashsize,
  51. uint32_t lrusize) :
  52. NameserverAddressStore(resolver, hashsize, lrusize),
  53. resolver_(resolver)
  54. {}
  55. /// \brief Virtual Destructor
  56. virtual ~DerivedNsas()
  57. {}
  58. /// \brief Add Nameserver Entry to Hash and LRU Tables
  59. void AddNameserverEntry(boost::shared_ptr<NameserverEntry>& entry) {
  60. HashKey h = entry->hashKey();
  61. nameserver_hash_->add(entry, h);
  62. nameserver_lru_->add(entry);
  63. }
  64. /// \brief Add Zone Entry to Hash and LRU Tables
  65. void AddZoneEntry(boost::shared_ptr<ZoneEntry>& entry) {
  66. HashKey h = entry->hashKey();
  67. zone_hash_->add(entry, h);
  68. zone_lru_->add(entry);
  69. }
  70. /**
  71. * \short Just wraps the common lookup
  72. *
  73. * It calls the lookup and provides the authority section
  74. * if it is asked for by the resolver.
  75. */
  76. void lookupAndAnswer(const string& name, const RRClass& class_code,
  77. boost::shared_ptr<AbstractRRset> authority,
  78. boost::shared_ptr<AddressRequestCallback> callback)
  79. {
  80. size_t size(resolver_->requests.size());
  81. NameserverAddressStore::lookup(name, class_code, callback, ANY_OK);
  82. // It asked something, the only thing it can ask is the NS list
  83. if (size < resolver_->requests.size()) {
  84. resolver_->provideNS(size, authority);
  85. // Once answered, drop the request so noone else sees it
  86. resolver_->requests.erase(resolver_->requests.begin() + size);
  87. } else {
  88. ADD_FAILURE() << "Not asked for NS";
  89. }
  90. }
  91. private:
  92. boost::shared_ptr<TestResolver> resolver_;
  93. };
  94. /// \brief Text Fixture Class
  95. class NameserverAddressStoreTest : public TestWithRdata {
  96. protected:
  97. NameserverAddressStoreTest() :
  98. authority_(new RRset(Name("example.net."), RRClass::IN(), RRType::NS(),
  99. RRTTL(128))),
  100. empty_authority_(new RRset(Name("example.net."), RRClass::IN(),
  101. RRType::NS(), RRTTL(128))),
  102. resolver_(new TestResolver)
  103. {
  104. // Constructor - initialize a set of nameserver and zone objects. For
  105. // convenience, these are stored in vectors.
  106. for (int i = 1; i <= 9; ++i) {
  107. std::string name = "nameserver" + boost::lexical_cast<std::string>(
  108. i);
  109. nameservers_.push_back(boost::shared_ptr<NameserverEntry>(
  110. new NameserverEntry(name, RRClass(40 + i))));
  111. }
  112. // Some zones. They will not use the tables in this test, so it can be
  113. // empty
  114. for (int i = 1; i <= 9; ++i) {
  115. std::string name = "zone" + boost::lexical_cast<std::string>(i);
  116. zones_.push_back(boost::shared_ptr<ZoneEntry>(new ZoneEntry(
  117. resolver_, name, RRClass(40 + i),
  118. boost::shared_ptr<HashTable<NameserverEntry> >(),
  119. boost::shared_ptr<LruList<NameserverEntry> >())));
  120. }
  121. // A nameserver serving data
  122. authority_->addRdata(ConstRdataPtr(new rdata::generic::NS(Name(
  123. "ns.example.com."))));
  124. // This is reused because of convenience, clear it just in case
  125. NSASCallback::results.clear();
  126. }
  127. // Vector of pointers to nameserver and zone entries.
  128. std::vector<boost::shared_ptr<NameserverEntry> > nameservers_;
  129. std::vector<boost::shared_ptr<ZoneEntry> > zones_;
  130. RRsetPtr authority_, empty_authority_;
  131. boost::shared_ptr<TestResolver> resolver_;
  132. class NSASCallback : public AddressRequestCallback {
  133. public:
  134. typedef pair<bool, NameserverAddress> Result;
  135. static vector<Result> results;
  136. virtual void success(const NameserverAddress& address) {
  137. results.push_back(Result(true, address));
  138. }
  139. virtual void unreachable() {
  140. results.push_back(Result(false, NameserverAddress()));
  141. }
  142. };
  143. boost::shared_ptr<AddressRequestCallback> getCallback() {
  144. return (boost::shared_ptr<AddressRequestCallback>(new NSASCallback));
  145. }
  146. };
  147. vector<NameserverAddressStoreTest::NSASCallback::Result>
  148. NameserverAddressStoreTest::NSASCallback::results;
  149. /// \brief Remove Zone Entry from Hash Table
  150. ///
  151. /// Check that when an entry reaches the top of the zone LRU list, it is removed from the
  152. /// hash table as well.
  153. TEST_F(NameserverAddressStoreTest, ZoneDeletionCheck) {
  154. // Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
  155. // nameserver tables).
  156. DerivedNsas nsas(resolver_, 2, 2);
  157. // Add six entries to the tables. After addition the reference count of each element
  158. // should be 3 - one for the entry in the zones_ vector, and one each for the entries
  159. // in the LRU list and hash table.
  160. for (int i = 1; i <= 6; ++i) {
  161. EXPECT_EQ(1, zones_[i].use_count());
  162. nsas.AddZoneEntry(zones_[i]);
  163. EXPECT_EQ(3, zones_[i].use_count());
  164. }
  165. // Adding another entry should cause the first one to drop off the LRU list, which
  166. // should also trigger the deletion of the entry from the hash table. This should
  167. // reduce its use count to 1.
  168. EXPECT_EQ(1, zones_[7].use_count());
  169. nsas.AddZoneEntry(zones_[7]);
  170. EXPECT_EQ(3, zones_[7].use_count());
  171. EXPECT_EQ(1, zones_[1].use_count());
  172. }
  173. /// \brief Remove Entry from Hash Table
  174. ///
  175. /// Check that when an entry reaches the top of the LRU list, it is removed from the
  176. /// hash table as well.
  177. TEST_F(NameserverAddressStoreTest, NameserverDeletionCheck) {
  178. // Create a NSAS with a hash size of three and a LRU size of 9 (both zone and
  179. // nameserver tables).
  180. DerivedNsas nsas(resolver_, 2, 2);
  181. // Add six entries to the tables. After addition the reference count of each element
  182. // should be 3 - one for the entry in the nameservers_ vector, and one each for the entries
  183. // in the LRU list and hash table.
  184. for (int i = 1; i <= 6; ++i) {
  185. EXPECT_EQ(1, nameservers_[i].use_count());
  186. nsas.AddNameserverEntry(nameservers_[i]);
  187. EXPECT_EQ(3, nameservers_[i].use_count());
  188. }
  189. // Adding another entry should cause the first one to drop off the LRU list, which
  190. // should also trigger the deletion of the entry from the hash table. This should
  191. // reduce its use count to 1.
  192. EXPECT_EQ(1, nameservers_[7].use_count());
  193. nsas.AddNameserverEntry(nameservers_[7]);
  194. EXPECT_EQ(3, nameservers_[7].use_count());
  195. EXPECT_EQ(1, nameservers_[1].use_count());
  196. }
  197. /**
  198. * \short Try lookup on empty store.
  199. *
  200. * Check if it asks correct questions and it keeps correct internal state.
  201. */
  202. TEST_F(NameserverAddressStoreTest, emptyLookup) {
  203. DerivedNsas nsas(resolver_, 10, 10);
  204. // Ask it a question
  205. nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
  206. getCallback());
  207. // It should ask for IP addresses for ns.example.com.
  208. EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
  209. // Ask another question for the same zone
  210. nsas.lookup("example.net.", RRClass::IN(), getCallback());
  211. // It should ask no more questions now
  212. EXPECT_EQ(2, resolver_->requests.size());
  213. // Ask another question with different zone but the same nameserver
  214. authority_->setName(Name("example.com."));
  215. nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
  216. getCallback());
  217. // It still should ask nothing
  218. EXPECT_EQ(2, resolver_->requests.size());
  219. // We provide IP address of one nameserver, it should generate all the
  220. // results
  221. EXPECT_NO_THROW(resolver_->answer(0, Name("ns.example.com."), RRType::A(),
  222. rdata::in::A("192.0.2.1")));
  223. EXPECT_EQ(3, NSASCallback::results.size());
  224. BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
  225. EXPECT_TRUE(result.first);
  226. EXPECT_EQ("192.0.2.1", result.second.getAddress().toText());
  227. }
  228. }
  229. /**
  230. * \short Try looking up a zone that does not have any nameservers.
  231. *
  232. * It should not ask anything and say it is unreachable right away.
  233. */
  234. TEST_F(NameserverAddressStoreTest, zoneWithoutNameservers) {
  235. DerivedNsas nsas(resolver_, 10, 10);
  236. // Ask it a question
  237. nsas.lookupAndAnswer("example.net.", RRClass::IN(), empty_authority_,
  238. getCallback());
  239. // There should be no questions, because there's nothing to ask
  240. EXPECT_EQ(0, resolver_->requests.size());
  241. // And there should be one "unreachable" answer for the query
  242. ASSERT_EQ(1, NSASCallback::results.size());
  243. EXPECT_FALSE(NSASCallback::results[0].first);
  244. }
  245. /**
  246. * \short Try looking up a zone that has only an unreachable nameserver.
  247. *
  248. * It should be unreachable. Furthermore, subsequent questions for that zone
  249. * or other zone with the same nameserver should be unreachable right away,
  250. * without further asking.
  251. */
  252. TEST_F(NameserverAddressStoreTest, unreachableNS) {
  253. DerivedNsas nsas(resolver_, 10, 10);
  254. // Ask it a question
  255. nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
  256. getCallback());
  257. // It should ask for IP addresses for example.com.
  258. EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
  259. // Ask another question with different zone but the same nameserver
  260. authority_->setName(Name("example.com."));
  261. nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
  262. getCallback());
  263. // It should ask nothing more now
  264. EXPECT_EQ(2, resolver_->requests.size());
  265. // We say there are no addresses
  266. resolver_->requests[0].second->failure();
  267. resolver_->requests[1].second->failure();
  268. // We should have 2 answers now
  269. EXPECT_EQ(2, NSASCallback::results.size());
  270. // When we ask one same and one other zone with the same nameserver,
  271. // it should generate no questions and answer right away
  272. nsas.lookup("example.net.", RRClass::IN(), getCallback());
  273. authority_->setName(Name("example.org."));
  274. nsas.lookupAndAnswer("example.org.", RRClass::IN(), authority_,
  275. getCallback());
  276. // There should be 4 negative answers now
  277. EXPECT_EQ(4, NSASCallback::results.size());
  278. BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
  279. EXPECT_FALSE(result.first);
  280. }
  281. }
  282. /**
  283. * \short Try to stress it little bit by having multiple zones and nameservers.
  284. *
  285. * Does some asking, on a set of zones that share some nameservers, with
  286. * slower answering, evicting data, etc.
  287. */
  288. TEST_F(NameserverAddressStoreTest, CombinedTest) {
  289. // Create small caches, so we get some evictions
  290. DerivedNsas nsas(resolver_, 1, 1);
  291. // Ask for example.net. It has single nameserver out of the zone
  292. nsas.lookupAndAnswer("example.net.", RRClass::IN(), authority_,
  293. getCallback());
  294. // It should ask for the nameserver IP addresses
  295. EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), 0, 1));
  296. EXPECT_EQ(0, NSASCallback::results.size());
  297. // But we do not answer it right away. We create a new zone and
  298. // let this nameserver entry get out.
  299. rrns_->addRdata(rdata::generic::NS("example.cz"));
  300. nsas.lookupAndAnswer(EXAMPLE_CO_UK, RRClass::IN(), rrns_, getCallback());
  301. // It really should ask something, one of the nameservers
  302. // (or both)
  303. ASSERT_GT(resolver_->requests.size(), 2);
  304. Name name(resolver_->requests[2].first->getName());
  305. EXPECT_TRUE(name == Name("example.fr") || name == Name("example.de") ||
  306. name == Name("example.cz"));
  307. EXPECT_NO_THROW(resolver_->asksIPs(name, 2, 3));
  308. EXPECT_EQ(0, NSASCallback::results.size());
  309. size_t request_count(resolver_->requests.size());
  310. // This should still be in the hash table, so try it asks no more questions
  311. nsas.lookup("example.net.", RRClass::IN(), getCallback());
  312. EXPECT_EQ(request_count, resolver_->requests.size());
  313. EXPECT_EQ(0, NSASCallback::results.size());
  314. // We respond to one of the 3 nameservers
  315. EXPECT_NO_THROW(resolver_->answer(2, name, RRType::A(),
  316. rdata::in::A("192.0.2.1")));
  317. // That should trigger one answer
  318. EXPECT_EQ(1, NSASCallback::results.size());
  319. EXPECT_TRUE(NSASCallback::results[0].first);
  320. EXPECT_EQ("192.0.2.1",
  321. NSASCallback::results[0].second.getAddress().toText());
  322. EXPECT_NO_THROW(resolver_->answer(3, name, RRType::AAAA(),
  323. rdata::in::AAAA("2001:bd8::1")));
  324. // And there should be yet another query
  325. ASSERT_GT(resolver_->requests.size(), 4);
  326. EXPECT_NE(name, resolver_->requests[4].first->getName());
  327. Name another_name = resolver_->requests[4].first->getName();
  328. EXPECT_TRUE(another_name == Name("example.fr") ||
  329. another_name == Name("example.de") ||
  330. another_name == Name("example.cz"));
  331. request_count = resolver_->requests.size();
  332. // But when ask for a different zone with the first nameserver, it should
  333. // ask again, as it is evicted already
  334. authority_->setName(Name("example.com."));
  335. nsas.lookupAndAnswer("example.com.", RRClass::IN(), authority_,
  336. getCallback());
  337. EXPECT_EQ(request_count + 2, resolver_->requests.size());
  338. EXPECT_NO_THROW(resolver_->asksIPs(Name("ns.example.com."), request_count,
  339. request_count + 1));
  340. // Now, we answer both queries for the same address
  341. // and three (one for the original, one for this one) more answers should
  342. // arrive
  343. NSASCallback::results.clear();
  344. EXPECT_NO_THROW(resolver_->answer(0, Name("ns.example.com."), RRType::A(),
  345. rdata::in::A("192.0.2.2")));
  346. EXPECT_NO_THROW(resolver_->answer(request_count, Name("ns.example.com."),
  347. RRType::A(), rdata::in::A("192.0.2.2")));
  348. EXPECT_EQ(3, NSASCallback::results.size());
  349. BOOST_FOREACH(const NSASCallback::Result& result, NSASCallback::results) {
  350. EXPECT_TRUE(result.first);
  351. EXPECT_EQ("192.0.2.2", result.second.getAddress().toText());
  352. }
  353. }
  354. } // namespace nsas
  355. } // namespace isc