config_unittest.cc 16 KB

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