zone_loader_unittest.cc 18 KB

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