config_unittest.cc 15 KB

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