config_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  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 <gtest/gtest.h>
  15. #include <exceptions/exceptions.h>
  16. #include <dns/rrclass.h>
  17. #include <dns/masterload.h>
  18. #include <cc/data.h>
  19. #include <datasrc/memory_datasrc.h>
  20. #include <xfr/xfrout_client.h>
  21. #include <auth/auth_srv.h>
  22. #include <auth/auth_config.h>
  23. #include <auth/common.h>
  24. #include <testutils/mockups.h>
  25. #include <testutils/portconfig.h>
  26. using namespace isc::dns;
  27. using namespace isc::data;
  28. using namespace isc::datasrc;
  29. using namespace asiolink;
  30. namespace {
  31. class AuthConfigTest : public ::testing::Test {
  32. protected:
  33. AuthConfigTest() :
  34. dnss_(ios_, NULL, NULL, NULL),
  35. rrclass(RRClass::IN()),
  36. server(true, xfrout)
  37. {
  38. server.setDNSService(dnss_);
  39. }
  40. IOService ios_;
  41. DNSService dnss_;
  42. const RRClass rrclass;
  43. MockXfroutClient xfrout;
  44. AuthSrv server;
  45. };
  46. TEST_F(AuthConfigTest, datasourceConfig) {
  47. // By default, we don't have any in-memory data source.
  48. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  49. configureAuthServer(server, Element::fromJSON(
  50. "{\"datasources\": [{\"type\": \"memory\"}]}"));
  51. // after successful configuration, we should have one (with empty zoneset).
  52. ASSERT_NE(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  53. EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount());
  54. }
  55. TEST_F(AuthConfigTest, databaseConfig) {
  56. // right now, "database_file" is handled separately, so the parser
  57. // doesn't recognize it, but it shouldn't throw an exception due to that.
  58. EXPECT_NO_THROW(configureAuthServer(
  59. server,
  60. Element::fromJSON(
  61. "{\"database_file\": \"should_be_ignored\"}")));
  62. }
  63. TEST_F(AuthConfigTest, exceptionGuarantee) {
  64. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  65. // This configuration contains an invalid item, which will trigger
  66. // an exception.
  67. EXPECT_THROW(configureAuthServer(
  68. server,
  69. Element::fromJSON(
  70. "{\"datasources\": [{\"type\": \"memory\"}], "
  71. " \"no_such_config_var\": 1}")),
  72. AuthConfigError);
  73. // The server state shouldn't change
  74. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  75. }
  76. TEST_F(AuthConfigTest, exceptionConversion) {
  77. // This configuration contains a bogus RR class, which will trigger an
  78. // exception from libdns++. configureAuthServer() should convert this
  79. // to AuthConfigError and rethrow the converted one.
  80. EXPECT_THROW(configureAuthServer(
  81. server,
  82. Element::fromJSON(
  83. "{\"datasources\": "
  84. " [{\"type\": \"memory\","
  85. " \"class\": \"BADCLASS\","
  86. " \"zones\": [{\"origin\": \"example.com\","
  87. " \"file\": \"example.zone\"}]}]}")),
  88. AuthConfigError);
  89. }
  90. TEST_F(AuthConfigTest, badConfig) {
  91. // These should normally not happen, but should be handled to avoid
  92. // an unexpected crash due to a bug of the caller.
  93. EXPECT_THROW(configureAuthServer(server, ElementPtr()), AuthConfigError);
  94. EXPECT_THROW(configureAuthServer(server, Element::fromJSON("[]")),
  95. AuthConfigError);
  96. }
  97. TEST_F(AuthConfigTest, unknownConfigVar) {
  98. EXPECT_THROW(createAuthConfigParser(server, "no_such_config_var"),
  99. AuthConfigError);
  100. }
  101. TEST_F(AuthConfigTest, exceptionFromCommit) {
  102. EXPECT_THROW(configureAuthServer(server, Element::fromJSON(
  103. "{\"_commit_throw\": 10}")),
  104. FatalError);
  105. }
  106. // Test invalid address configs are rejected
  107. TEST_F(AuthConfigTest, invalidListenAddressConfig) {
  108. // This currently passes simply because the config doesn't know listen_on
  109. isc::testutils::portconfig::invalidListenAddressConfig(server);
  110. }
  111. // Try setting addresses trough config
  112. TEST_F(AuthConfigTest, listenAddressConfig) {
  113. isc::testutils::portconfig::listenAddressConfig(server);
  114. }
  115. class MemoryDatasrcConfigTest : public AuthConfigTest {
  116. protected:
  117. MemoryDatasrcConfigTest() :
  118. parser(createAuthConfigParser(server, "datasources"))
  119. {}
  120. ~MemoryDatasrcConfigTest() {
  121. delete parser;
  122. }
  123. AuthConfigParser* parser;
  124. };
  125. TEST_F(MemoryDatasrcConfigTest, addZeroDataSrc) {
  126. parser->build(Element::fromJSON("[]"));
  127. parser->commit();
  128. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  129. }
  130. TEST_F(MemoryDatasrcConfigTest, addEmpty) {
  131. // By default, we don't have any in-memory data source.
  132. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  133. parser->build(Element::fromJSON("[{\"type\": \"memory\"}]"));
  134. parser->commit();
  135. EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount());
  136. }
  137. TEST_F(MemoryDatasrcConfigTest, addZeroZone) {
  138. parser->build(Element::fromJSON("[{\"type\": \"memory\","
  139. " \"zones\": []}]"));
  140. parser->commit();
  141. EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount());
  142. }
  143. TEST_F(MemoryDatasrcConfigTest, addOneZone) {
  144. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  145. "[{\"type\": \"memory\","
  146. " \"zones\": [{\"origin\": \"example.com\","
  147. " \"file\": \"" TEST_DATA_DIR
  148. "/example.zone\"}]}]")));
  149. EXPECT_NO_THROW(parser->commit());
  150. EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
  151. // Check it actually loaded something
  152. EXPECT_EQ(Zone::SUCCESS, server.getMemoryDataSrc(rrclass)->findZone(
  153. Name("ns.example.com.")).zone->find(Name("ns.example.com."),
  154. RRType::A()).code);
  155. }
  156. TEST_F(MemoryDatasrcConfigTest, addMultiZones) {
  157. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  158. "[{\"type\": \"memory\","
  159. " \"zones\": [{\"origin\": \"example.com\","
  160. " \"file\": \"" TEST_DATA_DIR
  161. "/example.zone\"},"
  162. " {\"origin\": \"example.org\","
  163. " \"file\": \"" TEST_DATA_DIR
  164. "/example.org.zone\"},"
  165. " {\"origin\": \"example.net\","
  166. " \"file\": \"" TEST_DATA_DIR
  167. "/example.net.zone\"}]}]")));
  168. EXPECT_NO_THROW(parser->commit());
  169. EXPECT_EQ(3, server.getMemoryDataSrc(rrclass)->getZoneCount());
  170. }
  171. TEST_F(MemoryDatasrcConfigTest, replace) {
  172. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  173. "[{\"type\": \"memory\","
  174. " \"zones\": [{\"origin\": \"example.com\","
  175. " \"file\": \"" TEST_DATA_DIR
  176. "/example.zone\"}]}]")));
  177. EXPECT_NO_THROW(parser->commit());
  178. EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
  179. EXPECT_EQ(isc::datasrc::result::SUCCESS,
  180. server.getMemoryDataSrc(rrclass)->findZone(
  181. Name("example.com")).code);
  182. // create a new parser, and install a new set of configuration. It
  183. // should replace the old one.
  184. delete parser;
  185. parser = createAuthConfigParser(server, "datasources");
  186. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  187. "[{\"type\": \"memory\","
  188. " \"zones\": [{\"origin\": \"example.org\","
  189. " \"file\": \"" TEST_DATA_DIR
  190. "/example.org.zone\"},"
  191. " {\"origin\": \"example.net\","
  192. " \"file\": \"" TEST_DATA_DIR
  193. "/example.net.zone\"}]}]")));
  194. EXPECT_NO_THROW(parser->commit());
  195. EXPECT_EQ(2, server.getMemoryDataSrc(rrclass)->getZoneCount());
  196. EXPECT_EQ(isc::datasrc::result::NOTFOUND,
  197. server.getMemoryDataSrc(rrclass)->findZone(
  198. Name("example.com")).code);
  199. }
  200. TEST_F(MemoryDatasrcConfigTest, exception) {
  201. // Load a zone
  202. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  203. "[{\"type\": \"memory\","
  204. " \"zones\": [{\"origin\": \"example.com\","
  205. " \"file\": \"" TEST_DATA_DIR
  206. "/example.zone\"}]}]")));
  207. EXPECT_NO_THROW(parser->commit());
  208. EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
  209. EXPECT_EQ(isc::datasrc::result::SUCCESS,
  210. server.getMemoryDataSrc(rrclass)->findZone(
  211. Name("example.com")).code);
  212. // create a new parser, and try to load something. It will throw,
  213. // the given master file should not exist
  214. delete parser;
  215. parser = createAuthConfigParser(server, "datasources");
  216. EXPECT_THROW(parser->build(Element::fromJSON(
  217. "[{\"type\": \"memory\","
  218. " \"zones\": [{\"origin\": \"example.org\","
  219. " \"file\": \"" TEST_DATA_DIR
  220. "/example.org.zone\"},"
  221. " {\"origin\": \"example.net\","
  222. " \"file\": \"" TEST_DATA_DIR
  223. "/nonexistent.zone\"}]}]")), isc::dns::MasterLoadError);
  224. // As that one throwed exception, it is not expected from us to
  225. // commit it
  226. // The original should be untouched
  227. EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
  228. EXPECT_EQ(isc::datasrc::result::SUCCESS,
  229. server.getMemoryDataSrc(rrclass)->findZone(
  230. Name("example.com")).code);
  231. }
  232. TEST_F(MemoryDatasrcConfigTest, remove) {
  233. EXPECT_NO_THROW(parser->build(Element::fromJSON(
  234. "[{\"type\": \"memory\","
  235. " \"zones\": [{\"origin\": \"example.com\","
  236. " \"file\": \"" TEST_DATA_DIR
  237. "/example.zone\"}]}]")));
  238. EXPECT_NO_THROW(parser->commit());
  239. EXPECT_EQ(1, server.getMemoryDataSrc(rrclass)->getZoneCount());
  240. delete parser;
  241. parser = createAuthConfigParser(server, "datasources");
  242. EXPECT_NO_THROW(parser->build(Element::fromJSON("[]")));
  243. EXPECT_NO_THROW(parser->commit());
  244. EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
  245. }
  246. TEST_F(MemoryDatasrcConfigTest, adDuplicateZones) {
  247. EXPECT_THROW(parser->build(
  248. Element::fromJSON(
  249. "[{\"type\": \"memory\","
  250. " \"zones\": [{\"origin\": \"example.com\","
  251. " \"file\": \"" TEST_DATA_DIR
  252. "/example.zone\"},"
  253. " {\"origin\": \"example.com\","
  254. " \"file\": \"" TEST_DATA_DIR
  255. "/example.com.zone\"}]}]")),
  256. AuthConfigError);
  257. }
  258. TEST_F(MemoryDatasrcConfigTest, addBadZone) {
  259. // origin is missing
  260. EXPECT_THROW(parser->build(
  261. Element::fromJSON(
  262. "[{\"type\": \"memory\","
  263. " \"zones\": [{\"file\": \"example.zone\"}]}]")),
  264. AuthConfigError);
  265. // missing zone file
  266. EXPECT_THROW(parser->build(
  267. Element::fromJSON(
  268. "[{\"type\": \"memory\","
  269. " \"zones\": [{\"origin\": \"example.com\"}]}]")),
  270. AuthConfigError);
  271. // bogus origin name
  272. EXPECT_THROW(parser->build(Element::fromJSON(
  273. "[{\"type\": \"memory\","
  274. " \"zones\": [{\"origin\": \"example..com\","
  275. " \"file\": \"example.zone\"}]}]")),
  276. EmptyLabel);
  277. // bogus RR class name
  278. EXPECT_THROW(parser->build(
  279. Element::fromJSON(
  280. "[{\"type\": \"memory\","
  281. " \"class\": \"BADCLASS\","
  282. " \"zones\": [{\"origin\": \"example.com\","
  283. " \"file\": \"example.zone\"}]}]")),
  284. InvalidRRClass);
  285. // valid RR class, but not currently supported
  286. EXPECT_THROW(parser->build(
  287. Element::fromJSON(
  288. "[{\"type\": \"memory\","
  289. " \"class\": \"CH\","
  290. " \"zones\": [{\"origin\": \"example.com\","
  291. " \"file\": \"example.zone\"}]}]")),
  292. isc::InvalidParameter);
  293. }
  294. TEST_F(MemoryDatasrcConfigTest, badDatasrcType) {
  295. EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"badsrc\"}]")),
  296. AuthConfigError);
  297. EXPECT_THROW(parser->build(Element::fromJSON("[{\"notype\": \"memory\"}]")),
  298. AuthConfigError);
  299. EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": 1}]")),
  300. isc::data::TypeError);
  301. EXPECT_THROW(parser->build(Element::fromJSON("[{\"type\": \"memory\"},"
  302. " {\"type\": \"memory\"}]")),
  303. AuthConfigError);
  304. }
  305. class StatisticsIntervalConfigTest : public AuthConfigTest {
  306. protected:
  307. StatisticsIntervalConfigTest() :
  308. parser(createAuthConfigParser(server, "statistics-interval"))
  309. {}
  310. ~StatisticsIntervalConfigTest() {
  311. delete parser;
  312. }
  313. AuthConfigParser* parser;
  314. };
  315. TEST_F(StatisticsIntervalConfigTest, setInterval) {
  316. // initially the timer is not configured.
  317. EXPECT_EQ(0, server.getStatisticsTimerInterval());
  318. // initialize the timer
  319. parser->build(Element::fromJSON("5"));
  320. parser->commit();
  321. EXPECT_EQ(5, server.getStatisticsTimerInterval());
  322. // reset the timer with a new interval
  323. delete parser;
  324. parser = createAuthConfigParser(server, "statistics-interval");
  325. ASSERT_NE(static_cast<void*>(NULL), parser);
  326. parser->build(Element::fromJSON("10"));
  327. parser->commit();
  328. EXPECT_EQ(10, server.getStatisticsTimerInterval());
  329. // disable the timer again
  330. delete parser;
  331. parser = createAuthConfigParser(server, "statistics-interval");
  332. ASSERT_NE(static_cast<void*>(NULL), parser);
  333. parser->build(Element::fromJSON("0"));
  334. parser->commit();
  335. EXPECT_EQ(0, server.getStatisticsTimerInterval());
  336. }
  337. TEST_F(StatisticsIntervalConfigTest, badInterval) {
  338. EXPECT_THROW(parser->build(Element::fromJSON("\"should be integer\"")),
  339. isc::data::TypeError);
  340. EXPECT_THROW(parser->build(Element::fromJSON("2.5")),
  341. isc::data::TypeError);
  342. EXPECT_THROW(parser->build(Element::fromJSON("-1")), AuthConfigError);
  343. // bounds check: interval value must be equal to or shorter than
  344. // 86400 seconds (1 day)
  345. EXPECT_NO_THROW(parser->build(Element::fromJSON("86400")));
  346. EXPECT_THROW(parser->build(Element::fromJSON("86401")), AuthConfigError);
  347. }
  348. }