zone_loader_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. // Copyright (C) 2012 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/zone_loader.h>
  15. #include <datasrc/data_source.h>
  16. #include <datasrc/memory/zone_table_segment.h>
  17. #include <datasrc/memory/memory_client.h>
  18. #include <dns/rrclass.h>
  19. #include <dns/name.h>
  20. #include <dns/rrset.h>
  21. #include <util/memory_segment_local.h>
  22. #include <exceptions/exceptions.h>
  23. #include <gtest/gtest.h>
  24. #include <boost/shared_ptr.hpp>
  25. #include <string>
  26. #include <vector>
  27. using isc::dns::RRClass;
  28. using isc::dns::Name;
  29. using isc::dns::RRType;
  30. using isc::dns::ConstRRsetPtr;
  31. using std::string;
  32. using std::vector;
  33. using boost::shared_ptr;
  34. using namespace isc::datasrc;
  35. namespace {
  36. class MockClient : public DataSourceClient {
  37. public:
  38. MockClient() :
  39. commit_called_(false),
  40. missing_zone_(false),
  41. rrclass_(RRClass::IN())
  42. {}
  43. virtual FindResult findZone(const Name&) const {
  44. isc_throw(isc::NotImplemented, "Method not used in tests");
  45. };
  46. virtual std::pair<ZoneJournalReader::Result, ZoneJournalReaderPtr>
  47. getJournalReader(const Name&, uint32_t, uint32_t) const
  48. {
  49. isc_throw(isc::NotImplemented, "Method not used in tests");
  50. }
  51. virtual ZoneUpdaterPtr getUpdater(const Name& name, bool replace,
  52. bool journaling) const;
  53. // We store some information about what was happening here.
  54. // It is publicly accessible, since this is private testing class
  55. // anyway, so no need to dress it fancy into getters. Some are mutable,
  56. // since many client methods are const, but we still want to know they
  57. // were called.
  58. mutable vector<Name> provided_updaters_;
  59. // We store string representations of the RRsets. This is simpler than
  60. // copying them and we can't really put them into shared pointers, because
  61. // we get them as references.
  62. vector<string> rrsets_;
  63. bool commit_called_;
  64. // If set to true, getUpdater returns NULL
  65. bool missing_zone_;
  66. // The pretended class of the client. Usualy IN, but can be overriden.
  67. RRClass rrclass_;
  68. };
  69. // The updater isn't really correct according to the API. For example,
  70. // the whole client can be committed only once in its lifetime. The
  71. // updaters would influence each other if there were more. But we
  72. // don't need more updaters in the same test, so it doesn't matter
  73. // and this way, it is much simpler.
  74. class Updater : public ZoneUpdater {
  75. public:
  76. Updater(MockClient* client) :
  77. client_(client),
  78. finder_(client_->rrclass_)
  79. {}
  80. virtual ZoneFinder& getFinder() {
  81. return (finder_);
  82. }
  83. virtual isc::dns::RRsetCollectionBase& getRRsetCollection() {
  84. isc_throw(isc::NotImplemented, "Method not used in tests");
  85. }
  86. virtual void addRRset(const isc::dns::AbstractRRset& rrset) {
  87. if (client_->commit_called_) {
  88. isc_throw(DataSourceError, "Add after commit");
  89. }
  90. client_->rrsets_.push_back(rrset.toText());
  91. }
  92. virtual void deleteRRset(const isc::dns::AbstractRRset&) {
  93. isc_throw(isc::NotImplemented, "Method not used in tests");
  94. }
  95. virtual void commit() {
  96. client_->commit_called_ = true;
  97. }
  98. private:
  99. MockClient* client_;
  100. class Finder : public ZoneFinder {
  101. public:
  102. Finder(const RRClass& rrclass) :
  103. class_(rrclass)
  104. {}
  105. virtual RRClass getClass() const {
  106. return (class_);
  107. }
  108. virtual Name getOrigin() const {
  109. isc_throw(isc::NotImplemented, "Method not used in tests");
  110. }
  111. virtual shared_ptr<Context> find(const Name&, const RRType&,
  112. const FindOptions)
  113. {
  114. isc_throw(isc::NotImplemented, "Method not used in tests");
  115. }
  116. virtual shared_ptr<Context> findAll(const Name&,
  117. vector<ConstRRsetPtr>&,
  118. const FindOptions)
  119. {
  120. isc_throw(isc::NotImplemented, "Method not used in tests");
  121. }
  122. virtual FindNSEC3Result findNSEC3(const Name&, bool) {
  123. isc_throw(isc::NotImplemented, "Method not used in tests");
  124. }
  125. private:
  126. const RRClass class_;
  127. } finder_;
  128. };
  129. ZoneUpdaterPtr
  130. MockClient::getUpdater(const Name& name, bool replace, bool journaling) const {
  131. if (missing_zone_) {
  132. return (ZoneUpdaterPtr());
  133. }
  134. EXPECT_TRUE(replace);
  135. EXPECT_FALSE(journaling);
  136. provided_updaters_.push_back(name);
  137. // const_cast is bad. But the const on getUpdater seems wrong in the first
  138. // place, since updater will be modifying the data there. And the updater
  139. // wants to store data into the client so we can examine it later.
  140. return (ZoneUpdaterPtr(new Updater(const_cast<MockClient*>(this))));
  141. }
  142. class ZoneLoaderTest : public ::testing::Test {
  143. protected:
  144. ZoneLoaderTest() :
  145. rrclass_(RRClass::IN()),
  146. ztable_segment_(memory::ZoneTableSegment::
  147. create(isc::data::NullElement(), rrclass_)),
  148. source_client_(ztable_segment_, rrclass_)
  149. {}
  150. void prepareSource(const Name& zone, const char* filename) {
  151. // TODO:
  152. // Currently, load uses an urelated implementation. In the long term,
  153. // the method will probably be deprecated. At that time, we should
  154. // probably prepare the data in some other way (using sqlite3 or
  155. // something). This is simpler for now.
  156. source_client_.load(zone, string(TEST_DATA_DIR) + "/" + filename);
  157. }
  158. private:
  159. const RRClass rrclass_;
  160. // This is because of the in-memory client. We use it to read data
  161. // from. It is still easier than setting up sqlite3 client, since
  162. // we have this one in the linked library.
  163. // FIXME: We should be destroying it by ZoneTableSegment::destroy.
  164. // But the shared pointer won't let us, will it?
  165. shared_ptr<memory::ZoneTableSegment> ztable_segment_;
  166. protected:
  167. memory::InMemoryClient source_client_;
  168. // This one is mocked. It will help us see what is happening inside.
  169. // Also, mocking it is simpler than setting up an sqlite3 client.
  170. MockClient destination_client_;
  171. };
  172. // Use the loader to load an unsigned zone.
  173. TEST_F(ZoneLoaderTest, copyUnsigned) {
  174. prepareSource(Name::ROOT_NAME(), "root.zone");
  175. ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
  176. // It gets the updater directly in the constructor
  177. ASSERT_EQ(1, destination_client_.provided_updaters_.size());
  178. EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
  179. // Now load the whole zone
  180. loader.load();
  181. EXPECT_TRUE(destination_client_.commit_called_);
  182. // We don't check the whole zone. We check the first and last and the
  183. // count, which should be enough.
  184. // The count is 34 because we expect the RRs to be separated.
  185. EXPECT_EQ(34, destination_client_.rrsets_.size());
  186. // Ensure known order.
  187. std::sort(destination_client_.rrsets_.begin(),
  188. destination_client_.rrsets_.end());
  189. EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
  190. destination_client_.rrsets_.front());
  191. EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
  192. destination_client_.rrsets_.back());
  193. // It isn't possible to try again now
  194. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  195. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  196. // Even 0, which should load nothing, returns the error
  197. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  198. }
  199. // Try loading incrementally.
  200. TEST_F(ZoneLoaderTest, copyUnsignedIncremental) {
  201. prepareSource(Name::ROOT_NAME(), "root.zone");
  202. ZoneLoader loader(destination_client_, Name::ROOT_NAME(), source_client_);
  203. // Try loading few RRs first.
  204. loader.loadIncremental(10);
  205. // We should get the 10 we asked for
  206. EXPECT_EQ(10, destination_client_.rrsets_.size());
  207. // Not committed yet, we didn't complete the loading
  208. EXPECT_FALSE(destination_client_.commit_called_);
  209. // This is unusual, but allowed. Check it doesn't do anything
  210. loader.loadIncremental(0);
  211. EXPECT_EQ(10, destination_client_.rrsets_.size());
  212. EXPECT_FALSE(destination_client_.commit_called_);
  213. // We can finish the rest
  214. loader.loadIncremental(30);
  215. EXPECT_EQ(34, destination_client_.rrsets_.size());
  216. EXPECT_TRUE(destination_client_.commit_called_);
  217. // No more loading now
  218. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  219. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  220. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  221. }
  222. // Check we can load RRSIGs and NSEC3 (which could break due to them being
  223. // in separate namespace)
  224. TEST_F(ZoneLoaderTest, copySigned) {
  225. prepareSource(Name("example.org"), "example.org.nsec3-signed");
  226. ZoneLoader loader(destination_client_, Name("example.org"),
  227. source_client_);
  228. loader.load();
  229. // All the RRs are there, including the ones in NSEC3 namespace
  230. EXPECT_EQ(14, destination_client_.rrsets_.size());
  231. EXPECT_TRUE(destination_client_.commit_called_);
  232. // Same trick with sorting to know where they are
  233. std::sort(destination_client_.rrsets_.begin(),
  234. destination_client_.rrsets_.end());
  235. // Due to the R at the beginning, this one should be last
  236. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
  237. "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
  238. destination_client_.rrsets_[0]);
  239. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
  240. "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
  241. " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
  242. "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
  243. "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
  244. destination_client_.rrsets_[1]);
  245. }
  246. // If the destination zone does not exist, it throws
  247. TEST_F(ZoneLoaderTest, copyMissingDestination) {
  248. destination_client_.missing_zone_ = true;
  249. prepareSource(Name::ROOT_NAME(), "root.zone");
  250. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  251. source_client_), DataSourceError);
  252. }
  253. // If the source zone does not exist, it throws
  254. TEST_F(ZoneLoaderTest, copyMissingSource) {
  255. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  256. source_client_), DataSourceError);
  257. }
  258. // The class of the source and destination are different
  259. TEST_F(ZoneLoaderTest, classMismatch) {
  260. destination_client_.rrclass_ = RRClass::CH();
  261. prepareSource(Name::ROOT_NAME(), "root.zone");
  262. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  263. source_client_), isc::InvalidParameter);
  264. }
  265. // Load an unsigned zone, all at once
  266. TEST_F(ZoneLoaderTest, loadUnsigned) {
  267. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  268. TEST_DATA_DIR "/root.zone");
  269. // It gets the updater directly in the constructor
  270. ASSERT_EQ(1, destination_client_.provided_updaters_.size());
  271. EXPECT_EQ(Name::ROOT_NAME(), destination_client_.provided_updaters_[0]);
  272. // Now load the whole zone
  273. loader.load();
  274. EXPECT_TRUE(destination_client_.commit_called_);
  275. // We don't check the whole zone. We check the first and last and the
  276. // count, which should be enough.
  277. // The count is 34 because we expect the RRs to be separated.
  278. EXPECT_EQ(34, destination_client_.rrsets_.size());
  279. // Ensure known order.
  280. std::sort(destination_client_.rrsets_.begin(),
  281. destination_client_.rrsets_.end());
  282. EXPECT_EQ(". 518400 IN NS a.root-servers.net.\n",
  283. destination_client_.rrsets_.front());
  284. EXPECT_EQ("m.root-servers.net. 3600000 IN AAAA 2001:dc3::35\n",
  285. destination_client_.rrsets_.back());
  286. // It isn't possible to try again now
  287. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  288. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  289. // Even 0, which should load nothing, returns the error
  290. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  291. }
  292. // Try loading from master file incrementally.
  293. TEST_F(ZoneLoaderTest, loadUnsignedIncremental) {
  294. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  295. TEST_DATA_DIR "/root.zone");
  296. // Try loading few RRs first.
  297. loader.loadIncremental(10);
  298. // We should get the 10 we asked for
  299. EXPECT_EQ(10, destination_client_.rrsets_.size());
  300. // Not committed yet, we didn't complete the loading
  301. EXPECT_FALSE(destination_client_.commit_called_);
  302. EXPECT_EQ(10, destination_client_.rrsets_.size());
  303. EXPECT_FALSE(destination_client_.commit_called_);
  304. // We can finish the rest
  305. loader.loadIncremental(30);
  306. EXPECT_EQ(34, destination_client_.rrsets_.size());
  307. EXPECT_TRUE(destination_client_.commit_called_);
  308. // No more loading now
  309. EXPECT_THROW(loader.load(), isc::InvalidOperation);
  310. EXPECT_THROW(loader.loadIncremental(1), isc::InvalidOperation);
  311. EXPECT_THROW(loader.loadIncremental(0), isc::InvalidOperation);
  312. }
  313. // If the destination zone does not exist, it throws
  314. TEST_F(ZoneLoaderTest, loadMissingDestination) {
  315. destination_client_.missing_zone_ = true;
  316. EXPECT_THROW(ZoneLoader(destination_client_, Name::ROOT_NAME(),
  317. TEST_DATA_DIR "/root.zone"), DataSourceError);
  318. }
  319. // Check we can load RRSIGs and NSEC3 (which could break due to them being
  320. // in separate namespace)
  321. TEST_F(ZoneLoaderTest, loadSigned) {
  322. ZoneLoader loader(destination_client_, Name("example.org"),
  323. TEST_DATA_DIR "/example.org.nsec3-signed");
  324. loader.load();
  325. // All the RRs are there, including the ones in NSEC3 namespace
  326. EXPECT_EQ(14, destination_client_.rrsets_.size());
  327. EXPECT_TRUE(destination_client_.commit_called_);
  328. // Same trick with sorting to know where they are
  329. std::sort(destination_client_.rrsets_.begin(),
  330. destination_client_.rrsets_.end());
  331. // Due to the R at the beginning, this one should be last
  332. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN NSEC3 "
  333. "1 0 10 AABBCCDD RKOF8QMFRB5F2V9EJHFBVB2JPVSA0DJD A RRSIG\n",
  334. destination_client_.rrsets_[0]);
  335. EXPECT_EQ("09GM5T42SMIMT7R8DF6RTG80SFMS1NLU.example.org. 1200 IN RRSIG "
  336. "NSEC3 7 3 1200 20120301040838 20120131040838 19562 example.org."
  337. " EdwMeepLf//lV+KpCAN+213Scv1rrZyj4i2OwoCP4XxxS3CWGSuvYuKOyfZc8w"
  338. "KRcrD/4YG6nZVXE0s5O8NahjBJmDIyVt4WkfZ6QthxGg8ggLVvcD3dFksPyiKHf"
  339. "/WrTOZPSsxvN5m/i1Ey6+YWS01Gf3WDCMWDauC7Nmh3CTM=\n",
  340. destination_client_.rrsets_[1]);
  341. }
  342. // Test it throws when there's no such file
  343. TEST_F(ZoneLoaderTest, loadNoSuchFile) {
  344. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  345. "This file does not exist");
  346. EXPECT_THROW(loader.load(), MasterFileError);
  347. EXPECT_FALSE(destination_client_.commit_called_);
  348. }
  349. // And it also throws when there's a syntax error in the master file
  350. TEST_F(ZoneLoaderTest, loadSyntaxError) {
  351. ZoneLoader loader(destination_client_, Name::ROOT_NAME(),
  352. // This is not a master file for sure
  353. // (misusing a file that happens to be there
  354. // already).
  355. TEST_DATA_DIR "/example.org.sqlite3");
  356. EXPECT_THROW(loader.load(), MasterFileError);
  357. EXPECT_FALSE(destination_client_.commit_called_);
  358. }
  359. }