dhcp_parsers_unittest.cc 89 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379
  1. // Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <cc/command_interpreter.h>
  8. #include <cc/data.h>
  9. #include <cc/simple_parser.h>
  10. #include <dhcp/option.h>
  11. #include <dhcp/option_custom.h>
  12. #include <dhcp/option_int.h>
  13. #include <dhcp/option_string.h>
  14. #include <dhcp/option6_addrlst.h>
  15. #include <dhcp/tests/iface_mgr_test_config.h>
  16. #include <dhcpsrv/cfgmgr.h>
  17. #include <dhcpsrv/subnet.h>
  18. #include <dhcpsrv/cfg_mac_source.h>
  19. #include <dhcpsrv/parsers/dhcp_parsers.h>
  20. #include <dhcpsrv/tests/test_libraries.h>
  21. #include <dhcpsrv/testutils/config_result_check.h>
  22. #include <exceptions/exceptions.h>
  23. #include <hooks/hooks_parser.h>
  24. #include <hooks/hooks_manager.h>
  25. #include <testutils/test_to_element.h>
  26. #include <gtest/gtest.h>
  27. #include <boost/foreach.hpp>
  28. #include <boost/pointer_cast.hpp>
  29. #include <boost/scoped_ptr.hpp>
  30. #include <map>
  31. #include <string>
  32. using namespace std;
  33. using namespace isc;
  34. using namespace isc::asiolink;
  35. using namespace isc::config;
  36. using namespace isc::data;
  37. using namespace isc::dhcp;
  38. using namespace isc::dhcp::test;
  39. using namespace isc::hooks;
  40. using namespace isc::test;
  41. namespace {
  42. /// @brief DHCP Parser test fixture class
  43. class DhcpParserTest : public ::testing::Test {
  44. public:
  45. /// @brief Constructor
  46. DhcpParserTest() {
  47. resetIfaceCfg();
  48. }
  49. /// @brief Destructor.
  50. virtual ~DhcpParserTest() {
  51. resetIfaceCfg();
  52. }
  53. /// @brief Resets selection of the interfaces from previous tests.
  54. void resetIfaceCfg() {
  55. CfgMgr::instance().clear();
  56. }
  57. };
  58. /// @brief Check BooleanParser basic functionality.
  59. ///
  60. /// Verifies that the parser:
  61. /// 1. Does not allow empty for storage.
  62. /// 2. Rejects a non-boolean element.
  63. /// 3. Builds with a valid true value.
  64. /// 4. Bbuils with a valid false value.
  65. /// 5. Updates storage upon commit.
  66. TEST_F(DhcpParserTest, booleanParserTest) {
  67. const std::string name = "boolParm";
  68. // Verify that parser does not allow empty for storage.
  69. BooleanStoragePtr bs;
  70. EXPECT_THROW(BooleanParser(name, bs), isc::dhcp::DhcpConfigError);
  71. // Construct parser for testing.
  72. BooleanStoragePtr storage(new BooleanStorage());
  73. BooleanParser parser(name, storage);
  74. // Verify that parser with rejects a non-boolean element.
  75. ElementPtr wrong_element = Element::create("I am a string");
  76. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  77. // Verify that parser will build with a valid true value.
  78. bool test_value = true;
  79. ElementPtr element = Element::create(test_value);
  80. ASSERT_NO_THROW(parser.build(element));
  81. // Verify that commit updates storage.
  82. bool actual_value = !test_value;
  83. parser.commit();
  84. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  85. EXPECT_EQ(test_value, actual_value);
  86. // Verify that parser will build with a valid false value.
  87. test_value = false;
  88. element->setValue(test_value);
  89. EXPECT_NO_THROW(parser.build(element));
  90. // Verify that commit updates storage.
  91. actual_value = !test_value;
  92. parser.commit();
  93. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  94. EXPECT_EQ(test_value, actual_value);
  95. }
  96. /// @brief Check StringParser basic functionality
  97. ///
  98. /// Verifies that the parser:
  99. /// 1. Does not allow empty for storage.
  100. /// 2. Builds with a nont string value.
  101. /// 3. Builds with a string value.
  102. /// 4. Updates storage upon commit.
  103. TEST_F(DhcpParserTest, stringParserTest) {
  104. const std::string name = "strParm";
  105. // Verify that parser does not allow empty for storage.
  106. StringStoragePtr bs;
  107. EXPECT_THROW(StringParser(name, bs), isc::dhcp::DhcpConfigError);
  108. // Construct parser for testing.
  109. StringStoragePtr storage(new StringStorage());
  110. StringParser parser(name, storage);
  111. // Verify that parser with accepts a non-string element.
  112. ElementPtr element = Element::create(9999);
  113. EXPECT_NO_THROW(parser.build(element));
  114. // Verify that commit updates storage.
  115. parser.commit();
  116. std::string actual_value;
  117. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  118. EXPECT_EQ("9999", actual_value);
  119. // Verify that parser will build with a string value.
  120. const std::string test_value = "test value";
  121. element = Element::create(test_value);
  122. ASSERT_NO_THROW(parser.build(element));
  123. // Verify that commit updates storage.
  124. parser.commit();
  125. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  126. EXPECT_EQ(test_value, actual_value);
  127. // Verify that parser with accepts a boolean true element.
  128. element = Element::create(true);
  129. EXPECT_NO_THROW(parser.build(element));
  130. // Verify that commit updates storage.
  131. parser.commit();
  132. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  133. EXPECT_EQ("true", actual_value);
  134. // Verify that parser with accepts a boolean true element.
  135. element = Element::create(false);
  136. EXPECT_NO_THROW(parser.build(element));
  137. // Verify that commit updates storage.
  138. parser.commit();
  139. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  140. EXPECT_EQ("false", actual_value);
  141. }
  142. /// @brief Check Uint32Parser basic functionality
  143. ///
  144. /// Verifies that the parser:
  145. /// 1. Does not allow empty for storage.
  146. /// 2. Rejects a non-integer element.
  147. /// 3. Rejects a negative value.
  148. /// 4. Rejects too large a value.
  149. /// 5. Builds with value of zero.
  150. /// 6. Builds with a value greater than zero.
  151. /// 7. Updates storage upon commit.
  152. TEST_F(DhcpParserTest, uint32ParserTest) {
  153. const std::string name = "intParm";
  154. // Verify that parser does not allow empty for storage.
  155. Uint32StoragePtr bs;
  156. EXPECT_THROW(Uint32Parser(name, bs), isc::dhcp::DhcpConfigError);
  157. // Construct parser for testing.
  158. Uint32StoragePtr storage(new Uint32Storage());
  159. Uint32Parser parser(name, storage);
  160. // Verify that parser with rejects a non-integer element.
  161. ElementPtr wrong_element = Element::create("I am a string");
  162. EXPECT_THROW(parser.build(wrong_element), isc::BadValue);
  163. // Verify that parser with rejects a negative value.
  164. ElementPtr int_element = Element::create(-1);
  165. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  166. // Verify that parser with rejects too large a value provided we are on
  167. // 64-bit platform.
  168. if (sizeof(long) > sizeof(uint32_t)) {
  169. long max = (long)(std::numeric_limits<uint32_t>::max()) + 1;
  170. int_element->setValue(max);
  171. EXPECT_THROW(parser.build(int_element), isc::BadValue);
  172. }
  173. // Verify that parser will build with value of zero.
  174. int test_value = 0;
  175. int_element->setValue((long)test_value);
  176. ASSERT_NO_THROW(parser.build(int_element));
  177. // Verify that commit updates storage.
  178. parser.commit();
  179. uint32_t actual_value = 0;
  180. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  181. EXPECT_EQ(test_value, actual_value);
  182. // Verify that parser will build with a valid positive value.
  183. test_value = 77;
  184. int_element->setValue((long)test_value);
  185. ASSERT_NO_THROW(parser.build(int_element));
  186. // Verify that commit updates storage.
  187. parser.commit();
  188. EXPECT_NO_THROW((actual_value = storage->getParam(name)));
  189. EXPECT_EQ(test_value, actual_value);
  190. }
  191. /// Verifies the code that parses mac sources and adds them to CfgMgr
  192. TEST_F(DhcpParserTest, MacSources) {
  193. // That's an equivalent of the following snippet:
  194. // "mac-sources: [ \"duid\", \"ipv6\" ]";
  195. ElementPtr values = Element::createList();
  196. values->add(Element::create("duid"));
  197. values->add(Element::create("ipv6-link-local"));
  198. // Let's grab server configuration from CfgMgr
  199. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
  200. ASSERT_TRUE(cfg);
  201. CfgMACSource& sources = cfg->getMACSources();
  202. // This should parse the configuration and check that it doesn't throw.
  203. MACSourcesListConfigParser parser;
  204. EXPECT_NO_THROW(parser.parse(sources, values));
  205. // Finally, check the sources that were configured
  206. CfgMACSources configured_sources = cfg->getMACSources().get();
  207. ASSERT_EQ(2, configured_sources.size());
  208. EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
  209. EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, configured_sources[1]);
  210. }
  211. /// @brief Check MACSourcesListConfigParser rejecting empty list
  212. ///
  213. /// Verifies that the code rejects an empty mac-sources list.
  214. TEST_F(DhcpParserTest, MacSourcesEmpty) {
  215. // That's an equivalent of the following snippet:
  216. // "mac-sources: [ \"duid\", \"ipv6\" ]";
  217. ElementPtr values = Element::createList();
  218. // Let's grab server configuration from CfgMgr
  219. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
  220. ASSERT_TRUE(cfg);
  221. CfgMACSource& sources = cfg->getMACSources();
  222. // This should throw, because if specified, at least one MAC source
  223. // has to be specified.
  224. MACSourcesListConfigParser parser;
  225. EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
  226. }
  227. /// @brief Check MACSourcesListConfigParser rejecting empty list
  228. ///
  229. /// Verifies that the code rejects fake mac source.
  230. TEST_F(DhcpParserTest, MacSourcesBogus) {
  231. // That's an equivalent of the following snippet:
  232. // "mac-sources: [ \"duid\", \"ipv6\" ]";
  233. ElementPtr values = Element::createList();
  234. values->add(Element::create("from-ebay"));
  235. values->add(Element::create("just-guess-it"));
  236. // Let's grab server configuration from CfgMgr
  237. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
  238. ASSERT_TRUE(cfg);
  239. CfgMACSource& sources = cfg->getMACSources();
  240. // This should throw, because these are not valid sources.
  241. MACSourcesListConfigParser parser;
  242. EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
  243. }
  244. /// Verifies the code that properly catches duplicate entries
  245. /// in mac-sources definition.
  246. TEST_F(DhcpParserTest, MacSourcesDuplicate) {
  247. // That's an equivalent of the following snippet:
  248. // "mac-sources: [ \"duid\", \"ipv6\" ]";
  249. ElementPtr values = Element::createList();
  250. values->add(Element::create("ipv6-link-local"));
  251. values->add(Element::create("duid"));
  252. values->add(Element::create("duid"));
  253. values->add(Element::create("duid"));
  254. // Let's grab server configuration from CfgMgr
  255. SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
  256. ASSERT_TRUE(cfg);
  257. CfgMACSource& sources = cfg->getMACSources();
  258. // This should parse the configuration and check that it throws.
  259. MACSourcesListConfigParser parser;
  260. EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
  261. }
  262. /// @brief Test Fixture class which provides basic structure for testing
  263. /// configuration parsing. This is essentially the same structure provided
  264. /// by dhcp servers.
  265. class ParseConfigTest : public ::testing::Test {
  266. public:
  267. /// @brief Constructor
  268. ParseConfigTest()
  269. :family_(AF_INET6) {
  270. reset_context();
  271. CfgMgr::instance().clear();
  272. }
  273. ~ParseConfigTest() {
  274. reset_context();
  275. CfgMgr::instance().clear();
  276. }
  277. /// @brief Parses a configuration.
  278. ///
  279. /// Parse the given configuration, populating the context storage with
  280. /// the parsed elements.
  281. ///
  282. /// @param config_set is the set of elements to parse.
  283. /// @return returns an ConstElementPtr containing the numeric result
  284. /// code and outcome comment.
  285. isc::data::ConstElementPtr parseElementSet(isc::data::ConstElementPtr
  286. config_set) {
  287. // Answer will hold the result.
  288. ConstElementPtr answer;
  289. if (!config_set) {
  290. answer = isc::config::createAnswer(1,
  291. string("Can't parse NULL config"));
  292. return (answer);
  293. }
  294. ConfigPair config_pair;
  295. try {
  296. // Iterate over the config elements.
  297. const std::map<std::string, ConstElementPtr>& values_map =
  298. config_set->mapValue();
  299. BOOST_FOREACH(config_pair, values_map) {
  300. // These are the simple parsers. No need to go through
  301. // the ParserPtr hooplas with them.
  302. if ((config_pair.first == "option-data") ||
  303. (config_pair.first == "option-def") ||
  304. (config_pair.first == "dhcp-ddns")) {
  305. continue;
  306. }
  307. // We also don't care about the default values that may be been
  308. // inserted
  309. if ((config_pair.first == "preferred-lifetime") ||
  310. (config_pair.first == "valid-lifetime") ||
  311. (config_pair.first == "renew-timer") ||
  312. (config_pair.first == "rebind-timer")) {
  313. continue;
  314. }
  315. if (config_pair.first == "hooks-libraries") {
  316. HooksLibrariesParser hook_parser;
  317. HooksConfig& libraries =
  318. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  319. hook_parser.parse(libraries, config_pair.second);
  320. libraries.verifyLibraries(config_pair.second->getPosition());
  321. libraries.loadLibraries();
  322. continue;
  323. }
  324. }
  325. // The option definition parser is the next one to be run.
  326. std::map<std::string, ConstElementPtr>::const_iterator
  327. def_config = values_map.find("option-def");
  328. if (def_config != values_map.end()) {
  329. CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
  330. OptionDefListParser def_list_parser;
  331. def_list_parser.parse(cfg_def, def_config->second);
  332. }
  333. // The option values parser is the next one to be run.
  334. std::map<std::string, ConstElementPtr>::const_iterator
  335. option_config = values_map.find("option-data");
  336. if (option_config != values_map.end()) {
  337. CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
  338. OptionDataListParser option_list_parser(family_);
  339. option_list_parser.parse(cfg_option, option_config->second);
  340. }
  341. // The dhcp-ddns parser is the next one to be run.
  342. std::map<std::string, ConstElementPtr>::const_iterator
  343. d2_client_config = values_map.find("dhcp-ddns");
  344. if (d2_client_config != values_map.end()) {
  345. // Used to be done by parser commit
  346. D2ClientConfigParser parser;
  347. D2ClientConfigPtr cfg = parser.parse(d2_client_config->second);
  348. CfgMgr::instance().setD2ClientConfig(cfg);
  349. }
  350. // Everything was fine. Configuration is successful.
  351. answer = isc::config::createAnswer(0, "Configuration committed.");
  352. } catch (const isc::Exception& ex) {
  353. answer = isc::config::createAnswer(1,
  354. string("Configuration parsing failed: ") + ex.what());
  355. } catch (...) {
  356. answer = isc::config::createAnswer(1,
  357. string("Configuration parsing failed"));
  358. }
  359. return (answer);
  360. }
  361. /// @brief DHCP-specific method that sets global, and option specific defaults
  362. ///
  363. /// This method sets the defaults in the global scope, in option definitions,
  364. /// and in option data.
  365. ///
  366. /// @param global pointer to the Element tree that holds configuration
  367. /// @param global_defaults array with global default values
  368. /// @param option_defaults array with option-data default values
  369. /// @param option_def_defaults array with default values for option definitions
  370. /// @return number of default values inserted.
  371. size_t setAllDefaults(isc::data::ElementPtr global,
  372. const SimpleDefaults& global_defaults,
  373. const SimpleDefaults& option_defaults,
  374. const SimpleDefaults& option_def_defaults) {
  375. size_t cnt = 0;
  376. // Set global defaults first.
  377. cnt = SimpleParser::setDefaults(global, global_defaults);
  378. // Now set option definition defaults for each specified option definition
  379. ConstElementPtr option_defs = global->get("option-def");
  380. if (option_defs) {
  381. BOOST_FOREACH(ElementPtr single_def, option_defs->listValue()) {
  382. cnt += SimpleParser::setDefaults(single_def, option_def_defaults);
  383. }
  384. }
  385. ConstElementPtr options = global->get("option-data");
  386. if (options) {
  387. BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
  388. cnt += SimpleParser::setDefaults(single_option, option_defaults);
  389. }
  390. }
  391. return (cnt);
  392. }
  393. /// This table defines default values for option definitions in DHCPv6
  394. static const SimpleDefaults OPTION6_DEF_DEFAULTS;
  395. /// This table defines default values for option definitions in DHCPv4
  396. static const SimpleDefaults OPTION4_DEF_DEFAULTS;
  397. /// This table defines default values for options in DHCPv6
  398. static const SimpleDefaults OPTION6_DEFAULTS;
  399. /// This table defines default values for options in DHCPv4
  400. static const SimpleDefaults OPTION4_DEFAULTS;
  401. /// This table defines default values for both DHCPv4 and DHCPv6
  402. static const SimpleDefaults GLOBAL6_DEFAULTS;
  403. /// @brief sets all default values for DHCPv4 and DHCPv6
  404. ///
  405. /// This function largely duplicates what SimpleParser4 and SimpleParser6 classes
  406. /// provide. However, since there are tons of unit-tests in dhcpsrv that need
  407. /// this functionality and there are good reasons to keep those classes in
  408. /// src/bin/dhcp{4,6}, the most straightforward way is to simply copy the
  409. /// minimum code here. Hence this method.
  410. ///
  411. /// @todo - TKM, I think this is fairly hideous and we should figure out a
  412. /// a way to not have to replicate in this fashion. It may be minimum code
  413. /// now, but it won't be fairly soon.
  414. ///
  415. /// @param config configuration structure to be filled with default values
  416. /// @param v6 true = DHCPv6, false = DHCPv4
  417. void setAllDefaults(ElementPtr config, bool v6) {
  418. if (v6) {
  419. setAllDefaults(config, GLOBAL6_DEFAULTS, OPTION6_DEFAULTS,
  420. OPTION6_DEF_DEFAULTS);
  421. } else {
  422. setAllDefaults(config, GLOBAL6_DEFAULTS, OPTION4_DEFAULTS,
  423. OPTION4_DEF_DEFAULTS);
  424. }
  425. /// D2 client configuration code is in this library
  426. ConstElementPtr d2_client = config->get("dhcp-ddns");
  427. if (d2_client) {
  428. D2ClientConfigParser::setAllDefaults(d2_client);
  429. }
  430. }
  431. /// @brief Convenience method for parsing a configuration
  432. ///
  433. /// Given a configuration string, convert it into Elements
  434. /// and parse them.
  435. /// @param config is the configuration string to parse
  436. ///
  437. /// @return retuns 0 if the configuration parsed successfully,
  438. /// non-zero otherwise failure.
  439. int parseConfiguration(const std::string& config, bool v6 = false) {
  440. int rcode_ = 1;
  441. // Turn config into elements.
  442. // Test json just to make sure its valid.
  443. ElementPtr json = Element::fromJSON(config);
  444. EXPECT_TRUE(json);
  445. if (json) {
  446. setAllDefaults(json, v6);
  447. ConstElementPtr status = parseElementSet(json);
  448. ConstElementPtr comment = parseAnswer(rcode_, status);
  449. error_text_ = comment->stringValue();
  450. // If error was reported, the error string should contain
  451. // position of the data element which caused failure.
  452. if (rcode_ != 0) {
  453. std::cout << "Error text:" << error_text_ << std::endl;
  454. EXPECT_TRUE(errorContainsPosition(status, "<string>"));
  455. }
  456. }
  457. return (rcode_);
  458. }
  459. /// @brief Find an option for a given space and code within the parser
  460. /// context.
  461. /// @param space is the space name of the desired option.
  462. /// @param code is the numeric "type" of the desired option.
  463. /// @return returns an OptionPtr which points to the found
  464. /// option or is empty.
  465. /// ASSERT_ tests don't work inside functions that return values
  466. OptionPtr getOptionPtr(std::string space, uint32_t code)
  467. {
  468. OptionPtr option_ptr;
  469. OptionContainerPtr options = CfgMgr::instance().getStagingCfg()->
  470. getCfgOption()->getAll(space);
  471. // Should always be able to get options list even if it is empty.
  472. EXPECT_TRUE(options);
  473. if (options) {
  474. // Attempt to find desired option.
  475. const OptionContainerTypeIndex& idx = options->get<1>();
  476. const OptionContainerTypeRange& range = idx.equal_range(code);
  477. int cnt = std::distance(range.first, range.second);
  478. EXPECT_EQ(1, cnt);
  479. if (cnt == 1) {
  480. OptionDescriptor desc = *(idx.begin());
  481. option_ptr = desc.option_;
  482. EXPECT_TRUE(option_ptr);
  483. }
  484. }
  485. return (option_ptr);
  486. }
  487. /// @brief Wipes the contents of the context to allowing another parsing
  488. /// during a given test if needed.
  489. void reset_context(){
  490. // Note set context universe to V6 as it has to be something.
  491. CfgMgr::instance().clear();
  492. family_ = AF_INET6;
  493. // Ensure no hooks libraries are loaded.
  494. HooksManager::unloadLibraries();
  495. // Set it to minimal, disabled config
  496. D2ClientConfigPtr tmp(new D2ClientConfig());
  497. CfgMgr::instance().setD2ClientConfig(tmp);
  498. }
  499. /// Allows the tests to interrogate the state of the libraries (if required).
  500. const isc::hooks::HookLibsCollection& getLibraries() {
  501. return (CfgMgr::instance().getStagingCfg()->getHooksConfig().get());
  502. }
  503. /// @brief specifies IP protocol family (AF_INET or AF_INET6)
  504. uint16_t family_;
  505. /// @brief Error string if the parsing failed
  506. std::string error_text_;
  507. };
  508. /// This table defines default values for option definitions in DHCPv6
  509. const SimpleDefaults ParseConfigTest::OPTION6_DEF_DEFAULTS = {
  510. { "record-types", Element::string, ""},
  511. { "space", Element::string, "dhcp6"},
  512. { "array", Element::boolean, "false"},
  513. { "encapsulate", Element::string, "" }
  514. };
  515. /// This table defines default values for option definitions in DHCPv4
  516. const SimpleDefaults ParseConfigTest::OPTION4_DEF_DEFAULTS = {
  517. { "record-types", Element::string, ""},
  518. { "space", Element::string, "dhcp4"},
  519. { "array", Element::boolean, "false"},
  520. { "encapsulate", Element::string, "" }
  521. };
  522. /// This table defines default values for options in DHCPv6
  523. const SimpleDefaults ParseConfigTest::OPTION6_DEFAULTS = {
  524. { "space", Element::string, "dhcp6"},
  525. { "csv-format", Element::boolean, "true"}
  526. };
  527. /// This table defines default values for options in DHCPv4
  528. const SimpleDefaults ParseConfigTest::OPTION4_DEFAULTS = {
  529. { "space", Element::string, "dhcp4"},
  530. { "csv-format", Element::boolean, "true"}
  531. };
  532. /// This table defines default values for both DHCPv4 and DHCPv6
  533. const SimpleDefaults ParseConfigTest::GLOBAL6_DEFAULTS = {
  534. { "renew-timer", Element::integer, "900" },
  535. { "rebind-timer", Element::integer, "1800" },
  536. { "preferred-lifetime", Element::integer, "3600" },
  537. { "valid-lifetime", Element::integer, "7200" }
  538. };
  539. /// @brief Option configuration class
  540. ///
  541. /// This class handles option-def and option-data which can be recovered
  542. /// using the toElement() method
  543. class CfgOptionsTest : public CfgToElement {
  544. public:
  545. /// @brief Constructor
  546. ///
  547. /// @param cfg the server configuration where to get option-{def,data}
  548. CfgOptionsTest(SrvConfigPtr cfg) :
  549. cfg_option_def_(cfg->getCfgOptionDef()),
  550. cfg_option_(cfg->getCfgOption()) { }
  551. /// @brief Unparse a configuration object
  552. ///
  553. /// @return a pointer to unparsed configuration (a map with
  554. /// not empty option-def and option-data lists)
  555. ElementPtr toElement() const {
  556. ElementPtr result = Element::createMap();
  557. // Set option-def
  558. ConstElementPtr option_def = cfg_option_def_->toElement();
  559. if (!option_def->empty()) {
  560. result->set("option-def", option_def);
  561. }
  562. // Set option-data
  563. ConstElementPtr option_data = cfg_option_->toElement();
  564. if (!option_data->empty()) {
  565. result->set("option-data", option_data);
  566. }
  567. return (result);
  568. }
  569. /// @brief Run a toElement test (Element version)
  570. ///
  571. /// Use the runToElementTest template but add defaults to the config
  572. ///
  573. /// @param family the address family
  574. /// @param config the expected result without defaults
  575. void runCfgOptionsTest(uint16_t family, ConstElementPtr expected) {
  576. ConstElementPtr option_def = expected->get("option-def");
  577. if (option_def) {
  578. SimpleParser::setListDefaults(option_def,
  579. family == AF_INET ?
  580. ParseConfigTest::OPTION4_DEF_DEFAULTS :
  581. ParseConfigTest::OPTION6_DEF_DEFAULTS);
  582. }
  583. ConstElementPtr option_data = expected->get("option-data");
  584. if (option_data) {
  585. SimpleParser::setListDefaults(option_data,
  586. family == AF_INET ?
  587. ParseConfigTest::OPTION4_DEFAULTS :
  588. ParseConfigTest::OPTION6_DEFAULTS);
  589. }
  590. runToElementTest<CfgOptionsTest>(expected, *this);
  591. }
  592. /// @brief Run a toElement test
  593. ///
  594. /// Use the runToElementTest template but add defaults to the config
  595. ///
  596. /// @param family the address family
  597. /// @param expected the expected result without defaults
  598. void runCfgOptionsTest(uint16_t family, std::string config) {
  599. ConstElementPtr json;
  600. ASSERT_NO_THROW(json = Element::fromJSON(config)) << config;
  601. runCfgOptionsTest(family, json);
  602. }
  603. private:
  604. /// @brief Pointer to option definitions configuration.
  605. CfgOptionDefPtr cfg_option_def_;
  606. /// @brief Reference to options (data) configuration.
  607. CfgOptionPtr cfg_option_;
  608. };
  609. /// @brief Check basic parsing of option definitions.
  610. ///
  611. /// Note that this tests basic operation of the OptionDefinitionListParser and
  612. /// OptionDefinitionParser. It uses a simple configuration consisting of
  613. /// one definition and verifies that it is parsed and committed to storage
  614. /// correctly.
  615. TEST_F(ParseConfigTest, basicOptionDefTest) {
  616. // Configuration string.
  617. std::string config =
  618. "{ \"option-def\": [ {"
  619. " \"name\": \"foo\","
  620. " \"code\": 100,"
  621. " \"type\": \"ipv4-address\","
  622. " \"array\": false,"
  623. " \"record-types\": \"\","
  624. " \"space\": \"isc\","
  625. " \"encapsulate\": \"\""
  626. " } ]"
  627. "}";
  628. // Verify that the configuration string parses.
  629. int rcode = parseConfiguration(config);
  630. ASSERT_EQ(0, rcode);
  631. // Verify that the option definition can be retrieved.
  632. OptionDefinitionPtr def =
  633. CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  634. ASSERT_TRUE(def);
  635. // Verify that the option definition is correct.
  636. EXPECT_EQ("foo", def->getName());
  637. EXPECT_EQ(100, def->getCode());
  638. EXPECT_FALSE(def->getArrayType());
  639. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
  640. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  641. // Check if libdhcp++ runtime options have been updated.
  642. OptionDefinitionPtr def_libdhcp = LibDHCP::getRuntimeOptionDef("isc", 100);
  643. ASSERT_TRUE(def_libdhcp);
  644. // The LibDHCP should return a separate instance of the option definition
  645. // but the values should be equal.
  646. EXPECT_TRUE(def_libdhcp != def);
  647. EXPECT_TRUE(*def_libdhcp == *def);
  648. // Check if it can be unparsed.
  649. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  650. cfg.runCfgOptionsTest(family_, config);
  651. }
  652. /// @brief Check minimal parsing of option definitions.
  653. ///
  654. /// Same than basic but without optional parameters set to their default.
  655. TEST_F(ParseConfigTest, minimalOptionDefTest) {
  656. // Configuration string.
  657. std::string config =
  658. "{ \"option-def\": [ {"
  659. " \"name\": \"foo\","
  660. " \"code\": 100,"
  661. " \"type\": \"ipv4-address\","
  662. " \"space\": \"isc\""
  663. " } ]"
  664. "}";
  665. // Verify that the configuration string parses.
  666. int rcode = parseConfiguration(config);
  667. ASSERT_EQ(0, rcode);
  668. // Verify that the option definition can be retrieved.
  669. OptionDefinitionPtr def =
  670. CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("isc", 100);
  671. ASSERT_TRUE(def);
  672. // Verify that the option definition is correct.
  673. EXPECT_EQ("foo", def->getName());
  674. EXPECT_EQ(100, def->getCode());
  675. EXPECT_FALSE(def->getArrayType());
  676. EXPECT_EQ(OPT_IPV4_ADDRESS_TYPE, def->getType());
  677. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  678. // Check if it can be unparsed.
  679. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  680. cfg.runCfgOptionsTest(family_, config);
  681. }
  682. /// @brief Check parsing of option definitions using default dhcp6 space.
  683. ///
  684. /// Same than minimal but using the fact the default universe is V6
  685. /// so the default space is dhcp6
  686. TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
  687. // Configuration string.
  688. std::string config =
  689. "{ \"option-def\": [ {"
  690. " \"name\": \"foo\","
  691. " \"code\": 10000,"
  692. " \"type\": \"ipv6-address\""
  693. " } ]"
  694. "}";
  695. // Verify that the configuration string parses.
  696. int rcode = parseConfiguration(config, true);
  697. ASSERT_EQ(0, rcode);
  698. // Verify that the option definition can be retrieved.
  699. OptionDefinitionPtr def =
  700. CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 10000);
  701. ASSERT_TRUE(def);
  702. // Verify that the option definition is correct.
  703. EXPECT_EQ("foo", def->getName());
  704. EXPECT_EQ(10000, def->getCode());
  705. EXPECT_FALSE(def->getArrayType());
  706. EXPECT_EQ(OPT_IPV6_ADDRESS_TYPE, def->getType());
  707. EXPECT_TRUE(def->getEncapsulatedSpace().empty());
  708. // Check if it can be unparsed.
  709. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  710. cfg.runCfgOptionsTest(family_, config);
  711. }
  712. /// @brief Check basic parsing of options.
  713. ///
  714. /// Note that this tests basic operation of the OptionDataListParser and
  715. /// OptionDataParser. It uses a simple configuration consisting of one
  716. /// one definition and matching option data. It verifies that the option
  717. /// is parsed and committed to storage correctly.
  718. TEST_F(ParseConfigTest, basicOptionDataTest) {
  719. // Configuration string.
  720. std::string config =
  721. "{ \"option-def\": [ {"
  722. " \"name\": \"foo\","
  723. " \"code\": 100,"
  724. " \"type\": \"ipv4-address\","
  725. " \"space\": \"isc\""
  726. " } ], "
  727. " \"option-data\": [ {"
  728. " \"name\": \"foo\","
  729. " \"space\": \"isc\","
  730. " \"code\": 100,"
  731. " \"data\": \"192.0.2.0\","
  732. " \"csv-format\": true"
  733. " } ]"
  734. "}";
  735. // Verify that the configuration string parses.
  736. int rcode = parseConfiguration(config);
  737. ASSERT_EQ(0, rcode);
  738. // Verify that the option can be retrieved.
  739. OptionPtr opt_ptr = getOptionPtr("isc", 100);
  740. ASSERT_TRUE(opt_ptr);
  741. // Verify that the option data is correct.
  742. std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
  743. EXPECT_EQ(val, opt_ptr->toText());
  744. // Check if it can be unparsed.
  745. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  746. cfg.runCfgOptionsTest(family_, config);
  747. }
  748. /// @brief Check minimal parsing of options.
  749. ///
  750. /// Same than basic but without optional parameters set to their default.
  751. TEST_F(ParseConfigTest, minimalOptionDataTest) {
  752. // Configuration string.
  753. std::string config =
  754. "{ \"option-def\": [ {"
  755. " \"name\": \"foo\","
  756. " \"code\": 100,"
  757. " \"type\": \"ipv4-address\","
  758. " \"space\": \"isc\""
  759. " } ], "
  760. " \"option-data\": [ {"
  761. " \"name\": \"foo\","
  762. " \"space\": \"isc\","
  763. " \"data\": \"192.0.2.0\""
  764. " } ]"
  765. "}";
  766. // Verify that the configuration string parses.
  767. int rcode = parseConfiguration(config);
  768. ASSERT_EQ(0, rcode);
  769. // Verify that the option can be retrieved.
  770. OptionPtr opt_ptr = getOptionPtr("isc", 100);
  771. ASSERT_TRUE(opt_ptr);
  772. // Verify that the option data is correct.
  773. std::string val = "type=00100, len=00004: 192.0.2.0 (ipv4-address)";
  774. EXPECT_EQ(val, opt_ptr->toText());
  775. ElementPtr expected = Element::fromJSON(config);
  776. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  777. opt_data->set("code", Element::create(100));
  778. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  779. cfg.runCfgOptionsTest(family_, expected);
  780. }
  781. /// @brief Check parsing of options with escape characters.
  782. ///
  783. /// Note that this tests basic operation of the OptionDataListParser and
  784. /// OptionDataParser. It uses a simple configuration consisting of one
  785. /// one definition and matching option data. It verifies that the option
  786. /// is parsed and committed to storage correctly and that its content
  787. /// has the actual character (e.g. an actual backslash, not double backslash).
  788. TEST_F(ParseConfigTest, escapedOptionDataTest) {
  789. family_ = AF_INET;
  790. // We need to use double escapes here. The first backslash will
  791. // be consumed by C++ preprocessor, so the actual string will
  792. // have two backslash characters: \\SMSBoot\\x64\\wdsnbp.com.
  793. //
  794. std::string config =
  795. "{\"option-data\": [ {"
  796. " \"name\": \"boot-file-name\","
  797. " \"data\": \"\\\\SMSBoot\\\\x64\\\\wdsnbp.com\""
  798. " } ]"
  799. "}";
  800. // Verify that the configuration string parses.
  801. int rcode = parseConfiguration(config);
  802. ASSERT_EQ(0, rcode);
  803. // Verify that the option can be retrieved.
  804. OptionPtr opt = getOptionPtr(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
  805. ASSERT_TRUE(opt);
  806. util::OutputBuffer buf(100);
  807. uint8_t exp[] = { DHO_BOOT_FILE_NAME, 23, '\\', 'S', 'M', 'S', 'B', 'o', 'o',
  808. 't', '\\', 'x', '6', '4', '\\', 'w', 'd', 's', 'n', 'b',
  809. 'p', '.', 'c', 'o', 'm' };
  810. ASSERT_EQ(25, sizeof(exp));
  811. opt->pack(buf);
  812. EXPECT_EQ(Option::OPTION4_HDR_LEN + 23, buf.getLength());
  813. EXPECT_TRUE(0 == memcmp(buf.getData(), exp, 25));
  814. ElementPtr expected = Element::fromJSON(config);
  815. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  816. opt_data->set("code", Element::create(DHO_BOOT_FILE_NAME));
  817. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  818. cfg.runCfgOptionsTest(family_, expected);
  819. }
  820. // This test checks behavior of the configuration parser for option data
  821. // for different values of csv-format parameter and when there is an option
  822. // definition present.
  823. TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) {
  824. std::string config =
  825. "{ \"option-data\": [ {"
  826. " \"name\": \"swap-server\","
  827. " \"space\": \"dhcp4\","
  828. " \"code\": 16,"
  829. " \"data\": \"192.0.2.0\""
  830. " } ]"
  831. "}";
  832. // The default universe is V6. We need to change it to use dhcp4 option
  833. // space.
  834. family_ = AF_INET;
  835. int rcode = 0;
  836. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  837. ASSERT_EQ(0, rcode);
  838. // Verify that the option data is correct.
  839. OptionCustomPtr addr_opt = boost::dynamic_pointer_cast<
  840. OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
  841. ASSERT_TRUE(addr_opt);
  842. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  843. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  844. cfg.runCfgOptionsTest(family_, config);
  845. // Explicitly enable csv-format.
  846. CfgMgr::instance().clear();
  847. config =
  848. "{ \"option-data\": [ {"
  849. " \"name\": \"swap-server\","
  850. " \"space\": \"dhcp4\","
  851. " \"code\": 16,"
  852. " \"csv-format\": true,"
  853. " \"data\": \"192.0.2.0\""
  854. " } ]"
  855. "}";
  856. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  857. ASSERT_EQ(0, rcode);
  858. // Verify that the option data is correct.
  859. addr_opt = boost::dynamic_pointer_cast<
  860. OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
  861. ASSERT_TRUE(addr_opt);
  862. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  863. // To make runToElementTest to work the csv-format must be removed...
  864. // Explicitly disable csv-format and use hex instead.
  865. CfgMgr::instance().clear();
  866. config =
  867. "{ \"option-data\": [ {"
  868. " \"name\": \"swap-server\","
  869. " \"space\": \"dhcp4\","
  870. " \"code\": 16,"
  871. " \"csv-format\": false,"
  872. " \"data\": \"C0000200\""
  873. " } ]"
  874. "}";
  875. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  876. ASSERT_EQ(0, rcode);
  877. // Verify that the option data is correct.
  878. addr_opt = boost::dynamic_pointer_cast<
  879. OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
  880. ASSERT_TRUE(addr_opt);
  881. EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
  882. CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg());
  883. cfg2.runCfgOptionsTest(family_, config);
  884. }
  885. // This test checks behavior of the configuration parser for option data
  886. // for different values of csv-format parameter and when there is no
  887. // option definition.
  888. TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
  889. // This option doesn't have any definition. It is ok to use such
  890. // an option but the data should be specified in hex, not as CSV.
  891. // Note that the parser will by default use the CSV format for the
  892. // data but only in case there is a suitable option definition.
  893. std::string config =
  894. "{ \"option-data\": [ {"
  895. " \"name\": \"foo-name\","
  896. " \"space\": \"dhcp6\","
  897. " \"code\": 25000,"
  898. " \"data\": \"1, 2, 5\""
  899. " } ]"
  900. "}";
  901. int rcode = 0;
  902. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  903. EXPECT_NE(0, rcode);
  904. CfgMgr::instance().clear();
  905. // The data specified here will work both for CSV format and hex format.
  906. // What we want to test here is that when the csv-format is enforced, the
  907. // parser will fail because of lack of an option definition.
  908. config =
  909. "{ \"option-data\": [ {"
  910. " \"name\": \"foo-name\","
  911. " \"space\": \"dhcp6\","
  912. " \"code\": 25000,"
  913. " \"csv-format\": true,"
  914. " \"data\": \"0\""
  915. " } ]"
  916. "}";
  917. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  918. EXPECT_NE(0, rcode);
  919. CfgMgr::instance().clear();
  920. // The same test case as above, but for the data specified in hex should
  921. // be successful.
  922. config =
  923. "{ \"option-data\": [ {"
  924. " \"name\": \"foo-name\","
  925. " \"space\": \"dhcp6\","
  926. " \"code\": 25000,"
  927. " \"csv-format\": false,"
  928. " \"data\": \"0\""
  929. " } ]"
  930. "}";
  931. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  932. ASSERT_EQ(0, rcode);
  933. OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
  934. ASSERT_TRUE(opt);
  935. ASSERT_EQ(1, opt->getData().size());
  936. EXPECT_EQ(0, opt->getData()[0]);
  937. ElementPtr expected = Element::fromJSON(config);
  938. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  939. opt_data->remove("name");
  940. opt_data->set("data", Element::create(std::string("00")));
  941. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  942. cfg.runCfgOptionsTest(family_, expected);
  943. CfgMgr::instance().clear();
  944. // When csv-format is not specified, the parser will check if the definition
  945. // exists or not. Since there is no definition, the parser will accept the
  946. // data in hex.
  947. config =
  948. "{ \"option-data\": [ {"
  949. " \"name\": \"foo-name\","
  950. " \"space\": \"dhcp6\","
  951. " \"code\": 25000,"
  952. " \"csv-format\": false,"
  953. " \"data\": \"123456\""
  954. " } ]"
  955. "}";
  956. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  957. EXPECT_EQ(0, rcode);
  958. opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
  959. ASSERT_TRUE(opt);
  960. ASSERT_EQ(3, opt->getData().size());
  961. EXPECT_EQ(0x12, opt->getData()[0]);
  962. EXPECT_EQ(0x34, opt->getData()[1]);
  963. EXPECT_EQ(0x56, opt->getData()[2]);
  964. expected = Element::fromJSON(config);
  965. opt_data = expected->get("option-data")->getNonConst(0);
  966. opt_data->remove("name");
  967. CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg());
  968. cfg2.runCfgOptionsTest(family_, expected);
  969. }
  970. // This test verifies that the option name is not mandatory, if the option
  971. // code has been specified.
  972. TEST_F(ParseConfigTest, optionDataNoName) {
  973. std::string config =
  974. "{ \"option-data\": [ {"
  975. " \"space\": \"dhcp6\","
  976. " \"code\": 23,"
  977. " \"data\": \"2001:db8:1::1\""
  978. " } ]"
  979. "}";
  980. int rcode = 0;
  981. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  982. EXPECT_EQ(0, rcode);
  983. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  984. Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
  985. ASSERT_TRUE(opt);
  986. ASSERT_EQ(1, opt->getAddresses().size());
  987. EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
  988. ElementPtr expected = Element::fromJSON(config);
  989. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  990. opt_data->set("name", Element::create(std::string("dns-servers")));
  991. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  992. cfg.runCfgOptionsTest(family_, expected);
  993. }
  994. // This test verifies that the option code is not mandatory, if the option
  995. // name has been specified.
  996. TEST_F(ParseConfigTest, optionDataNoCode) {
  997. std::string config =
  998. "{ \"option-data\": [ {"
  999. " \"space\": \"dhcp6\","
  1000. " \"name\": \"dns-servers\","
  1001. " \"data\": \"2001:db8:1::1\""
  1002. " } ]"
  1003. "}";
  1004. int rcode = 0;
  1005. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1006. EXPECT_EQ(0, rcode);
  1007. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  1008. Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
  1009. ASSERT_TRUE(opt);
  1010. ASSERT_EQ(1, opt->getAddresses().size());
  1011. EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
  1012. ElementPtr expected = Element::fromJSON(config);
  1013. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1014. opt_data->set("code", Element::create(D6O_NAME_SERVERS));
  1015. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1016. cfg.runCfgOptionsTest(family_, expected);
  1017. }
  1018. // This test verifies that the option data configuration with a minimal
  1019. // set of parameters works as expected.
  1020. TEST_F(ParseConfigTest, optionDataMinimal) {
  1021. std::string config =
  1022. "{ \"option-data\": [ {"
  1023. " \"name\": \"dns-servers\","
  1024. " \"data\": \"2001:db8:1::10\""
  1025. " } ]"
  1026. "}";
  1027. int rcode = 0;
  1028. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1029. EXPECT_EQ(0, rcode);
  1030. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  1031. Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
  1032. ASSERT_TRUE(opt);
  1033. ASSERT_EQ(1, opt->getAddresses().size());
  1034. EXPECT_EQ( "2001:db8:1::10", opt->getAddresses()[0].toText());
  1035. ElementPtr expected = Element::fromJSON(config);
  1036. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1037. opt_data->set("code", Element::create(D6O_NAME_SERVERS));
  1038. opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE)));
  1039. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1040. cfg.runCfgOptionsTest(family_, expected);
  1041. CfgMgr::instance().clear();
  1042. // This time using an option code.
  1043. config =
  1044. "{ \"option-data\": [ {"
  1045. " \"code\": 23,"
  1046. " \"data\": \"2001:db8:1::20\""
  1047. " } ]"
  1048. "}";
  1049. rcode = 0;
  1050. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1051. EXPECT_EQ(0, rcode);
  1052. opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
  1053. 23));
  1054. ASSERT_TRUE(opt);
  1055. ASSERT_EQ(1, opt->getAddresses().size());
  1056. EXPECT_EQ( "2001:db8:1::20", opt->getAddresses()[0].toText());
  1057. expected = Element::fromJSON(config);
  1058. opt_data = expected->get("option-data")->getNonConst(0);
  1059. opt_data->set("name", Element::create(std::string("dns-servers")));
  1060. opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE)));
  1061. CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg());
  1062. cfg2.runCfgOptionsTest(family_, expected);
  1063. }
  1064. // This test verifies that the option data configuration with a minimal
  1065. // set of parameters works as expected when option definition is
  1066. // created in the configuration file.
  1067. TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
  1068. // Configuration string.
  1069. std::string config =
  1070. "{ \"option-def\": [ {"
  1071. " \"name\": \"foo-name\","
  1072. " \"code\": 2345,"
  1073. " \"type\": \"ipv6-address\","
  1074. " \"array\": true,"
  1075. " \"space\": \"dhcp6\""
  1076. " } ],"
  1077. " \"option-data\": [ {"
  1078. " \"name\": \"foo-name\","
  1079. " \"data\": \"2001:db8:1::10, 2001:db8:1::123\""
  1080. " } ]"
  1081. "}";
  1082. int rcode = 0;
  1083. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1084. EXPECT_EQ(0, rcode);
  1085. Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  1086. Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 2345));
  1087. ASSERT_TRUE(opt);
  1088. ASSERT_EQ(2, opt->getAddresses().size());
  1089. EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText());
  1090. EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText());
  1091. ElementPtr expected = Element::fromJSON(config);
  1092. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1093. opt_data->set("code", Element::create(2345));
  1094. opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE)));
  1095. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1096. cfg.runCfgOptionsTest(family_, expected);
  1097. CfgMgr::instance().clear();
  1098. // Do the same test but now use an option code.
  1099. config =
  1100. "{ \"option-def\": [ {"
  1101. " \"name\": \"foo-name\","
  1102. " \"code\": 2345,"
  1103. " \"type\": \"ipv6-address\","
  1104. " \"array\": true,"
  1105. " \"space\": \"dhcp6\""
  1106. " } ],"
  1107. " \"option-data\": [ {"
  1108. " \"code\": 2345,"
  1109. " \"data\": \"2001:db8:1::10, 2001:db8:1::123\""
  1110. " } ]"
  1111. "}";
  1112. rcode = 0;
  1113. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1114. EXPECT_EQ(0, rcode);
  1115. opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
  1116. 2345));
  1117. ASSERT_TRUE(opt);
  1118. ASSERT_EQ(2, opt->getAddresses().size());
  1119. EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText());
  1120. EXPECT_EQ("2001:db8:1::123", opt->getAddresses()[1].toText());
  1121. expected = Element::fromJSON(config);
  1122. opt_data = expected->get("option-data")->getNonConst(0);
  1123. opt_data->set("name", Element::create(std::string("foo-name")));
  1124. opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE)));
  1125. CfgOptionsTest cfg2(CfgMgr::instance().getStagingCfg());
  1126. cfg2.runCfgOptionsTest(family_, expected);
  1127. }
  1128. // This test verifies an empty option data configuration is supported.
  1129. TEST_F(ParseConfigTest, emptyOptionData) {
  1130. // Configuration string.
  1131. const std::string config =
  1132. "{ \"option-data\": [ {"
  1133. " \"name\": \"dhcp4o6-server-addr\""
  1134. " } ]"
  1135. "}";
  1136. int rcode = 0;
  1137. ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
  1138. EXPECT_EQ(0, rcode);
  1139. const Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
  1140. Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER));
  1141. ASSERT_TRUE(opt);
  1142. ASSERT_EQ(0, opt->getAddresses().size());
  1143. ElementPtr expected = Element::fromJSON(config);
  1144. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1145. opt_data->set("code", Element::create(D6O_DHCPV4_O_DHCPV6_SERVER));
  1146. opt_data->set("space", Element::create(std::string(DHCP6_OPTION_SPACE)));
  1147. opt_data->set("csv-format", Element::create(false));
  1148. opt_data->set("data", Element::create(std::string("")));
  1149. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1150. cfg.runCfgOptionsTest(family_, expected);
  1151. }
  1152. // This test verifies an option data without suboptions is supported
  1153. TEST_F(ParseConfigTest, optionDataNoSubOption) {
  1154. // Configuration string.
  1155. const std::string config =
  1156. "{ \"option-data\": [ {"
  1157. " \"name\": \"vendor-encapsulated-options\""
  1158. " } ]"
  1159. "}";
  1160. // The default universe is V6. We need to change it to use dhcp4 option
  1161. // space.
  1162. family_ = AF_INET;
  1163. int rcode = 0;
  1164. ASSERT_NO_THROW(rcode = parseConfiguration(config));
  1165. EXPECT_EQ(0, rcode);
  1166. const OptionPtr opt = getOptionPtr(DHCP4_OPTION_SPACE, DHO_VENDOR_ENCAPSULATED_OPTIONS);
  1167. ASSERT_TRUE(opt);
  1168. ASSERT_EQ(0, opt->getOptions().size());
  1169. ElementPtr expected = Element::fromJSON(config);
  1170. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1171. opt_data->set("code", Element::create(DHO_VENDOR_ENCAPSULATED_OPTIONS));
  1172. opt_data->set("space", Element::create(std::string(DHCP4_OPTION_SPACE)));
  1173. opt_data->set("csv-format", Element::create(false));
  1174. opt_data->set("data", Element::create(std::string("")));
  1175. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1176. cfg.runCfgOptionsTest(family_, expected);
  1177. }
  1178. // This tests option-data in CSV format and embedded commas.
  1179. TEST_F(ParseConfigTest, commaCSVFormatOptionData) {
  1180. // Configuration string.
  1181. std::string config =
  1182. "{ \"option-data\": [ {"
  1183. " \"csv-format\": true,"
  1184. " \"code\": 41,"
  1185. " \"data\": \"EST5EDT4\\\\,M3.2.0/02:00\\\\,M11.1.0/02:00\","
  1186. " \"space\": \"dhcp6\""
  1187. " } ]"
  1188. "}";
  1189. // Verify that the configuration string parses.
  1190. int rcode = parseConfiguration(config, true);
  1191. ASSERT_EQ(0, rcode);
  1192. // Verify that the option can be retrieved.
  1193. OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 41);
  1194. ASSERT_TRUE(opt);
  1195. // Get the option as an option string.
  1196. OptionStringPtr opt_str = boost::dynamic_pointer_cast<OptionString>(opt);
  1197. ASSERT_TRUE(opt_str);
  1198. // Verify that the option data is correct.
  1199. string val = "EST5EDT4,M3.2.0/02:00,M11.1.0/02:00";
  1200. EXPECT_EQ(val, opt_str->getValue());
  1201. ElementPtr expected = Element::fromJSON(config);
  1202. ElementPtr opt_data = expected->get("option-data")->getNonConst(0);
  1203. opt_data->remove("csv-format");
  1204. opt_data->set("name", Element::create(std::string("new-posix-timezone")));
  1205. CfgOptionsTest cfg(CfgMgr::instance().getStagingCfg());
  1206. cfg.runCfgOptionsTest(family_, expected);
  1207. }
  1208. /// The next set of tests check basic operation of the HooksLibrariesParser.
  1209. //
  1210. // Convenience function to set a configuration of zero or more hooks
  1211. // libraries:
  1212. //
  1213. // lib1 - No parameters
  1214. // lib2 - Empty parameters statement
  1215. // lib3 - Valid parameters
  1216. std::string
  1217. setHooksLibrariesConfig(const char* lib1 = NULL, const char* lib2 = NULL,
  1218. const char* lib3 = NULL) {
  1219. const string lbrace("{");
  1220. const string rbrace("}");
  1221. const string quote("\"");
  1222. const string comma_space(", ");
  1223. const string library("\"library\": ");
  1224. string config = string("{ \"hooks-libraries\": [");
  1225. if (lib1 != NULL) {
  1226. // Library 1 has no parameters
  1227. config += lbrace;
  1228. config += library + quote + std::string(lib1) + quote;
  1229. config += rbrace;
  1230. if (lib2 != NULL) {
  1231. // Library 2 has an empty parameters statement
  1232. config += comma_space + lbrace;
  1233. config += library + quote + std::string(lib2) + quote + comma_space;
  1234. config += string("\"parameters\": {}");
  1235. config += rbrace;
  1236. if (lib3 != NULL) {
  1237. // Library 3 has valid parameters
  1238. config += comma_space + lbrace;
  1239. config += library + quote + std::string(lib3) + quote + comma_space;
  1240. config += string("\"parameters\": {");
  1241. config += string(" \"svalue\": \"string value\", ");
  1242. config += string(" \"ivalue\": 42, "); // Integer value
  1243. config += string(" \"bvalue\": true"); // Boolean value
  1244. config += string("}");
  1245. config += rbrace;
  1246. }
  1247. }
  1248. }
  1249. config += std::string("] }");
  1250. return (config);
  1251. }
  1252. // hooks-libraries element that does not contain anything.
  1253. TEST_F(ParseConfigTest, noHooksLibraries) {
  1254. // Check that no libraries are currently loaded
  1255. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1256. EXPECT_TRUE(hooks_libraries.empty());
  1257. // Create an empty hooks-libraries configuration element.
  1258. const string config = setHooksLibrariesConfig();
  1259. // Verify that the configuration string parses.
  1260. const int rcode = parseConfiguration(config);
  1261. ASSERT_TRUE(rcode == 0) << error_text_;
  1262. // Verify that the configuration object unparses.
  1263. ConstElementPtr expected;
  1264. ASSERT_NO_THROW(expected =
  1265. Element::fromJSON(config)->get("hooks-libraries"));
  1266. ASSERT_TRUE(expected);
  1267. const HooksConfig& cfg =
  1268. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1269. runToElementTest<HooksConfig>(expected, cfg);
  1270. // Check that the parser recorded nothing.
  1271. isc::hooks::HookLibsCollection libraries = getLibraries();
  1272. EXPECT_TRUE(libraries.empty());
  1273. // Check that there are still no libraries loaded.
  1274. hooks_libraries = HooksManager::getLibraryNames();
  1275. EXPECT_TRUE(hooks_libraries.empty());
  1276. }
  1277. // hooks-libraries element that contains a single library.
  1278. TEST_F(ParseConfigTest, oneHooksLibrary) {
  1279. // Check that no libraries are currently loaded
  1280. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1281. EXPECT_TRUE(hooks_libraries.empty());
  1282. // Configuration with hooks-libraries set to a single library.
  1283. const string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1);
  1284. // Verify that the configuration string parses.
  1285. const int rcode = parseConfiguration(config);
  1286. ASSERT_TRUE(rcode == 0) << error_text_;
  1287. // Verify that the configuration object unparses.
  1288. ConstElementPtr expected;
  1289. ASSERT_NO_THROW(expected =
  1290. Element::fromJSON(config)->get("hooks-libraries"));
  1291. ASSERT_TRUE(expected);
  1292. const HooksConfig& cfg =
  1293. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1294. runToElementTest<HooksConfig>(expected, cfg);
  1295. // Check that the parser recorded a single library.
  1296. isc::hooks::HookLibsCollection libraries = getLibraries();
  1297. ASSERT_EQ(1, libraries.size());
  1298. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1299. // Check that the change was propagated to the hooks manager.
  1300. hooks_libraries = HooksManager::getLibraryNames();
  1301. ASSERT_EQ(1, hooks_libraries.size());
  1302. EXPECT_EQ(CALLOUT_LIBRARY_1, hooks_libraries[0]);
  1303. }
  1304. // hooks-libraries element that contains two libraries
  1305. TEST_F(ParseConfigTest, twoHooksLibraries) {
  1306. // Check that no libraries are currently loaded
  1307. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1308. EXPECT_TRUE(hooks_libraries.empty());
  1309. // Configuration with hooks-libraries set to two libraries.
  1310. const string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1311. CALLOUT_LIBRARY_2);
  1312. // Verify that the configuration string parses.
  1313. const int rcode = parseConfiguration(config);
  1314. ASSERT_TRUE(rcode == 0) << error_text_;
  1315. // Verify that the configuration object unparses.
  1316. ConstElementPtr expected;
  1317. ASSERT_NO_THROW(expected =
  1318. Element::fromJSON(config)->get("hooks-libraries"));
  1319. ASSERT_TRUE(expected);
  1320. const HooksConfig& cfg =
  1321. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1322. runToElementTest<HooksConfig>(expected, cfg);
  1323. // Check that the parser recorded two libraries in the expected order.
  1324. isc::hooks::HookLibsCollection libraries = getLibraries();
  1325. ASSERT_EQ(2, libraries.size());
  1326. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1327. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
  1328. // Verify that the change was propagated to the hooks manager.
  1329. hooks_libraries = HooksManager::getLibraryNames();
  1330. ASSERT_EQ(2, hooks_libraries.size());
  1331. EXPECT_EQ(CALLOUT_LIBRARY_1, hooks_libraries[0]);
  1332. EXPECT_EQ(CALLOUT_LIBRARY_2, hooks_libraries[1]);
  1333. }
  1334. // Configure with two libraries, then reconfigure with the same libraries.
  1335. TEST_F(ParseConfigTest, reconfigureSameHooksLibraries) {
  1336. // Check that no libraries are currently loaded
  1337. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1338. EXPECT_TRUE(hooks_libraries.empty());
  1339. // Configuration with hooks-libraries set to two libraries.
  1340. const std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1341. CALLOUT_LIBRARY_2);
  1342. // Verify that the configuration string parses. The twoHooksLibraries
  1343. // test shows that the list will be as expected.
  1344. int rcode = parseConfiguration(config);
  1345. ASSERT_TRUE(rcode == 0) << error_text_;
  1346. // Verify that the configuration object unparses.
  1347. ConstElementPtr expected;
  1348. ASSERT_NO_THROW(expected =
  1349. Element::fromJSON(config)->get("hooks-libraries"));
  1350. ASSERT_TRUE(expected);
  1351. const HooksConfig& cfg =
  1352. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1353. runToElementTest<HooksConfig>(expected, cfg);
  1354. // The previous test shows that the parser correctly recorded the two
  1355. // libraries and that they loaded correctly.
  1356. // Parse the string again.
  1357. rcode = parseConfiguration(config);
  1358. ASSERT_TRUE(rcode == 0) << error_text_;
  1359. // The list has not changed between the two parse operations. However,
  1360. // the paramters (or the files they could point to) could have
  1361. // changed, so the libraries are reloaded anyway.
  1362. const HooksConfig& cfg2 =
  1363. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1364. runToElementTest<HooksConfig>(expected, cfg2);
  1365. isc::hooks::HookLibsCollection libraries = getLibraries();
  1366. ASSERT_EQ(2, libraries.size());
  1367. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1368. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
  1369. // ... and check that the same two libraries are still loaded in the
  1370. // HooksManager.
  1371. hooks_libraries = HooksManager::getLibraryNames();
  1372. ASSERT_EQ(2, hooks_libraries.size());
  1373. EXPECT_EQ(CALLOUT_LIBRARY_1, hooks_libraries[0]);
  1374. EXPECT_EQ(CALLOUT_LIBRARY_2, hooks_libraries[1]);
  1375. }
  1376. // Configure the hooks with two libraries, then reconfigure with the same
  1377. // libraries, but in reverse order.
  1378. TEST_F(ParseConfigTest, reconfigureReverseHooksLibraries) {
  1379. // Check that no libraries are currently loaded
  1380. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1381. EXPECT_TRUE(hooks_libraries.empty());
  1382. // Configuration with hooks-libraries set to two libraries.
  1383. std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1384. CALLOUT_LIBRARY_2);
  1385. // Verify that the configuration string parses. The twoHooksLibraries
  1386. // test shows that the list will be as expected.
  1387. int rcode = parseConfiguration(config);
  1388. ASSERT_TRUE(rcode == 0) << error_text_;
  1389. // A previous test shows that the parser correctly recorded the two
  1390. // libraries and that they loaded correctly.
  1391. // Parse the reversed set of libraries.
  1392. config = setHooksLibrariesConfig(CALLOUT_LIBRARY_2, CALLOUT_LIBRARY_1);
  1393. rcode = parseConfiguration(config);
  1394. ASSERT_TRUE(rcode == 0) << error_text_;
  1395. // The list has changed, and this is what we should see.
  1396. isc::hooks::HookLibsCollection libraries = getLibraries();
  1397. ASSERT_EQ(2, libraries.size());
  1398. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[0].first);
  1399. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[1].first);
  1400. // ... and check that this was propagated to the HooksManager.
  1401. hooks_libraries = HooksManager::getLibraryNames();
  1402. ASSERT_EQ(2, hooks_libraries.size());
  1403. EXPECT_EQ(CALLOUT_LIBRARY_2, hooks_libraries[0]);
  1404. EXPECT_EQ(CALLOUT_LIBRARY_1, hooks_libraries[1]);
  1405. }
  1406. // Configure the hooks with two libraries, then reconfigure with
  1407. // no libraries.
  1408. TEST_F(ParseConfigTest, reconfigureZeroHooksLibraries) {
  1409. // Check that no libraries are currently loaded
  1410. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1411. EXPECT_TRUE(hooks_libraries.empty());
  1412. // Configuration with hooks-libraries set to two libraries.
  1413. std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1414. CALLOUT_LIBRARY_2);
  1415. // Verify that the configuration string parses.
  1416. int rcode = parseConfiguration(config);
  1417. ASSERT_TRUE(rcode == 0) << error_text_;
  1418. // A previous test shows that the parser correctly recorded the two
  1419. // libraries and that they loaded correctly.
  1420. // Parse the string again, this time without any libraries.
  1421. config = setHooksLibrariesConfig();
  1422. rcode = parseConfiguration(config);
  1423. ASSERT_TRUE(rcode == 0) << error_text_;
  1424. // Verify that the configuration object unparses.
  1425. ConstElementPtr expected;
  1426. ASSERT_NO_THROW(expected =
  1427. Element::fromJSON(config)->get("hooks-libraries"));
  1428. ASSERT_TRUE(expected);
  1429. const HooksConfig& cfg =
  1430. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1431. runToElementTest<HooksConfig>(expected, cfg);
  1432. // The list has changed, and this is what we should see.
  1433. isc::hooks::HookLibsCollection libraries = getLibraries();
  1434. EXPECT_TRUE(libraries.empty());
  1435. // Check that no libraries are currently loaded
  1436. hooks_libraries = HooksManager::getLibraryNames();
  1437. EXPECT_TRUE(hooks_libraries.empty());
  1438. }
  1439. // Check with a set of libraries, some of which are invalid.
  1440. TEST_F(ParseConfigTest, invalidHooksLibraries) {
  1441. // Check that no libraries are currently loaded
  1442. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1443. EXPECT_TRUE(hooks_libraries.empty());
  1444. // Configuration string. This contains an invalid library which should
  1445. // trigger an error in the "build" stage.
  1446. const std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1447. NOT_PRESENT_LIBRARY,
  1448. CALLOUT_LIBRARY_2);
  1449. // Verify that the configuration fails to parse. (Syntactically it's OK,
  1450. // but the library is invalid).
  1451. const int rcode = parseConfiguration(config);
  1452. ASSERT_FALSE(rcode == 0) << error_text_;
  1453. // Check that the message contains the library in error.
  1454. EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
  1455. "Error text returned from parse failure is " << error_text_;
  1456. // Check that the parser recorded the names but, as they were in error,
  1457. // does not flag them as changed.
  1458. isc::hooks::HookLibsCollection libraries = getLibraries();
  1459. ASSERT_EQ(3, libraries.size());
  1460. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1461. EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
  1462. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2].first);
  1463. // ...and check it did not alter the libraries in the hooks manager.
  1464. hooks_libraries = HooksManager::getLibraryNames();
  1465. EXPECT_TRUE(hooks_libraries.empty());
  1466. }
  1467. // Check that trying to reconfigure with an invalid set of libraries fails.
  1468. TEST_F(ParseConfigTest, reconfigureInvalidHooksLibraries) {
  1469. // Check that no libraries are currently loaded
  1470. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1471. EXPECT_TRUE(hooks_libraries.empty());
  1472. // Configure with a single library.
  1473. std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1);
  1474. int rcode = parseConfiguration(config);
  1475. ASSERT_TRUE(rcode == 0) << error_text_;
  1476. // A previous test shows that the parser correctly recorded the two
  1477. // libraries and that they loaded correctly.
  1478. // Configuration string. This contains an invalid library which should
  1479. // trigger an error in the "build" stage.
  1480. config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1, NOT_PRESENT_LIBRARY,
  1481. CALLOUT_LIBRARY_2);
  1482. // Verify that the configuration fails to parse. (Syntactically it's OK,
  1483. // but the library is invalid).
  1484. rcode = parseConfiguration(config);
  1485. EXPECT_FALSE(rcode == 0) << error_text_;
  1486. // Check that the message contains the library in error.
  1487. EXPECT_FALSE(error_text_.find(NOT_PRESENT_LIBRARY) == string::npos) <<
  1488. "Error text returned from parse failure is " << error_text_;
  1489. // Check that the parser recorded the names but, as the library set was
  1490. // incorrect, did not mark the configuration as changed.
  1491. isc::hooks::HookLibsCollection libraries = getLibraries();
  1492. ASSERT_EQ(3, libraries.size());
  1493. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1494. EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
  1495. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2].first);
  1496. // ... but check that the hooks manager was not updated with the incorrect
  1497. // names.
  1498. hooks_libraries.clear();
  1499. hooks_libraries = HooksManager::getLibraryNames();
  1500. ASSERT_EQ(1, hooks_libraries.size());
  1501. EXPECT_EQ(CALLOUT_LIBRARY_1, hooks_libraries[0]);
  1502. }
  1503. // Check that if hooks-libraries contains invalid syntax, it is detected.
  1504. TEST_F(ParseConfigTest, invalidSyntaxHooksLibraries) {
  1505. // Element holds a mixture of (valid) maps and non-maps.
  1506. string config1 = "{ \"hooks-libraries\": [ "
  1507. "{ \"library\": \"/opt/lib/lib1\" }, "
  1508. "\"/opt/lib/lib2\" "
  1509. "] }";
  1510. string error1 = "one or more entries in the hooks-libraries list is not"
  1511. " a map";
  1512. int rcode = parseConfiguration(config1);
  1513. ASSERT_NE(0, rcode);
  1514. EXPECT_TRUE(error_text_.find(error1) != string::npos) <<
  1515. "Error text returned from parse failure is " << error_text_;
  1516. // Element holds valid maps, except one where the library element is not
  1517. // a string.
  1518. string config2 = "{ \"hooks-libraries\": [ "
  1519. "{ \"library\": \"/opt/lib/lib1\" }, "
  1520. "{ \"library\": 123 } "
  1521. "] }";
  1522. string error2 = "value of 'library' element is not a string giving"
  1523. " the path to a hooks library";
  1524. rcode = parseConfiguration(config2);
  1525. ASSERT_NE(0, rcode);
  1526. EXPECT_TRUE(error_text_.find(error2) != string::npos) <<
  1527. "Error text returned from parse failure is " << error_text_;
  1528. // Element holds valid maps, except one where the library element is the
  1529. // empty string.
  1530. string config3 = "{ \"hooks-libraries\": [ "
  1531. "{ \"library\": \"/opt/lib/lib1\" }, "
  1532. "{ \"library\": \"\" } "
  1533. "] }";
  1534. string error3 = "value of 'library' element must not be blank";
  1535. rcode = parseConfiguration(config3);
  1536. ASSERT_NE(0, rcode);
  1537. EXPECT_TRUE(error_text_.find(error3) != string::npos) <<
  1538. "Error text returned from parse failure is " << error_text_;
  1539. // Element holds valid maps, except one where the library element is all
  1540. // spaces.
  1541. string config4 = "{ \"hooks-libraries\": [ "
  1542. "{ \"library\": \"/opt/lib/lib1\" }, "
  1543. "{ \"library\": \" \" } "
  1544. "] }";
  1545. string error4 = "value of 'library' element must not be blank";
  1546. rcode = parseConfiguration(config4);
  1547. ASSERT_NE(0, rcode);
  1548. EXPECT_TRUE(error_text_.find(error3) != string::npos) <<
  1549. "Error text returned from parse failure is " << error_text_;
  1550. // Element holds valid maps, except one that does not contain a
  1551. // 'library' element.
  1552. string config5 = "{ \"hooks-libraries\": [ "
  1553. "{ \"library\": \"/opt/lib/lib1\" }, "
  1554. "{ \"parameters\": { \"alpha\": 123 } }, "
  1555. "{ \"library\": \"/opt/lib/lib2\" } "
  1556. "] }";
  1557. string error5 = "one or more hooks-libraries elements are missing the"
  1558. " name of the library";
  1559. rcode = parseConfiguration(config5);
  1560. ASSERT_NE(0, rcode);
  1561. EXPECT_TRUE(error_text_.find(error5) != string::npos) <<
  1562. "Error text returned from parse failure is " << error_text_;
  1563. }
  1564. // Check that some parameters may have configuration parameters configured.
  1565. TEST_F(ParseConfigTest, HooksLibrariesParameters) {
  1566. // Check that no libraries are currently loaded
  1567. vector<string> hooks_libraries = HooksManager::getLibraryNames();
  1568. EXPECT_TRUE(hooks_libraries.empty());
  1569. // Configuration string. This contains an invalid library which should
  1570. // trigger an error in the "build" stage.
  1571. const std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
  1572. CALLOUT_LIBRARY_2,
  1573. CALLOUT_PARAMS_LIBRARY);
  1574. // Verify that the configuration fails to parse. (Syntactically it's OK,
  1575. // but the library is invalid).
  1576. const int rcode = parseConfiguration(config);
  1577. ASSERT_EQ(0, rcode);
  1578. // Verify that the configuration object unparses.
  1579. ConstElementPtr expected;
  1580. ASSERT_NO_THROW(expected =
  1581. Element::fromJSON(config)->get("hooks-libraries"));
  1582. ASSERT_TRUE(expected);
  1583. const HooksConfig& cfg =
  1584. CfgMgr::instance().getStagingCfg()->getHooksConfig();
  1585. runToElementTest<HooksConfig>(expected, cfg);
  1586. // Check that the parser recorded the names.
  1587. isc::hooks::HookLibsCollection libraries = getLibraries();
  1588. ASSERT_EQ(3, libraries.size());
  1589. EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
  1590. EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
  1591. EXPECT_EQ(CALLOUT_PARAMS_LIBRARY, libraries[2].first);
  1592. // Also, check that the third library has its parameters specified.
  1593. // They were set by setHooksLibrariesConfig. The first has no
  1594. // parameters, the second one has an empty map and the third
  1595. // one has actual parameters.
  1596. EXPECT_FALSE(libraries[0].second);
  1597. EXPECT_TRUE(libraries[1].second);
  1598. ASSERT_TRUE(libraries[2].second);
  1599. // Ok, get the parameter for the third library.
  1600. ConstElementPtr params = libraries[2].second;
  1601. // It must be a map.
  1602. ASSERT_EQ(Element::map, params->getType());
  1603. // This map should have 3 parameters:
  1604. // - svalue (and will expect its value to be "string value")
  1605. // - ivalue (and will expect its value to be 42)
  1606. // - bvalue (and will expect its value to be true)
  1607. ConstElementPtr svalue = params->get("svalue");
  1608. ConstElementPtr ivalue = params->get("ivalue");
  1609. ConstElementPtr bvalue = params->get("bvalue");
  1610. // There should be no extra parameters.
  1611. EXPECT_FALSE(params->get("nonexistent"));
  1612. ASSERT_TRUE(svalue);
  1613. ASSERT_TRUE(ivalue);
  1614. ASSERT_TRUE(bvalue);
  1615. ASSERT_EQ(Element::string, svalue->getType());
  1616. ASSERT_EQ(Element::integer, ivalue->getType());
  1617. ASSERT_EQ(Element::boolean, bvalue->getType());
  1618. EXPECT_EQ("string value", svalue->stringValue());
  1619. EXPECT_EQ(42, ivalue->intValue());
  1620. EXPECT_EQ(true, bvalue->boolValue());
  1621. }
  1622. /// @brief Checks that a valid, enabled D2 client configuration works correctly.
  1623. TEST_F(ParseConfigTest, validD2Config) {
  1624. // Configuration string containing valid values.
  1625. std::string config_str =
  1626. "{ \"dhcp-ddns\" :"
  1627. " {"
  1628. " \"enable-updates\" : true, "
  1629. " \"server-ip\" : \"192.0.2.0\", "
  1630. " \"server-port\" : 3432, "
  1631. " \"sender-ip\" : \"192.0.2.1\", "
  1632. " \"sender-port\" : 3433, "
  1633. " \"max-queue-size\" : 2048, "
  1634. " \"ncr-protocol\" : \"UDP\", "
  1635. " \"ncr-format\" : \"JSON\", "
  1636. " \"always-include-fqdn\" : true, "
  1637. " \"override-no-update\" : true, "
  1638. " \"override-client-update\" : true, "
  1639. " \"replace-client-name\" : \"when-present\", "
  1640. " \"generated-prefix\" : \"test.prefix\", "
  1641. " \"qualifying-suffix\" : \"test.suffix.\" "
  1642. " }"
  1643. "}";
  1644. // Verify that the configuration string parses.
  1645. int rcode = parseConfiguration(config_str);
  1646. ASSERT_TRUE(rcode == 0) << error_text_;
  1647. // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
  1648. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  1649. D2ClientConfigPtr d2_client_config;
  1650. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  1651. ASSERT_TRUE(d2_client_config);
  1652. // Verify that the configuration values are as expected.
  1653. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  1654. EXPECT_EQ("192.0.2.0", d2_client_config->getServerIp().toText());
  1655. EXPECT_EQ(3432, d2_client_config->getServerPort());
  1656. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  1657. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  1658. EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
  1659. EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
  1660. EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
  1661. EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
  1662. EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
  1663. EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
  1664. // Verify that the configuration object unparses.
  1665. ConstElementPtr expected;
  1666. ASSERT_NO_THROW(expected = Element::fromJSON(config_str)->get("dhcp-ddns"));
  1667. ASSERT_TRUE(expected);
  1668. runToElementTest<D2ClientConfig>(expected, *d2_client_config);
  1669. // Another valid Configuration string.
  1670. // This one is disabled, has IPV6 server ip, control flags false,
  1671. // empty prefix/suffix
  1672. std::string config_str2 =
  1673. "{ \"dhcp-ddns\" :"
  1674. " {"
  1675. " \"enable-updates\" : false, "
  1676. " \"server-ip\" : \"2001:db8::\", "
  1677. " \"server-port\" : 43567, "
  1678. " \"sender-ip\" : \"2001:db8::1\", "
  1679. " \"sender-port\" : 3433, "
  1680. " \"max-queue-size\" : 2048, "
  1681. " \"ncr-protocol\" : \"UDP\", "
  1682. " \"ncr-format\" : \"JSON\", "
  1683. " \"always-include-fqdn\" : false, "
  1684. " \"override-no-update\" : false, "
  1685. " \"override-client-update\" : false, "
  1686. " \"replace-client-name\" : \"never\", "
  1687. " \"generated-prefix\" : \"\", "
  1688. " \"qualifying-suffix\" : \"\" "
  1689. " }"
  1690. "}";
  1691. // Verify that the configuration string parses.
  1692. rcode = parseConfiguration(config_str2);
  1693. ASSERT_TRUE(rcode == 0) << error_text_;
  1694. // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
  1695. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  1696. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  1697. ASSERT_TRUE(d2_client_config);
  1698. // Verify that the configuration values are as expected.
  1699. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  1700. EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
  1701. EXPECT_EQ(43567, d2_client_config->getServerPort());
  1702. EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
  1703. EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
  1704. EXPECT_FALSE(d2_client_config->getAlwaysIncludeFqdn());
  1705. EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
  1706. EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
  1707. EXPECT_EQ(D2ClientConfig::RCM_NEVER, d2_client_config->getReplaceClientNameMode());
  1708. EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
  1709. EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
  1710. ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns"));
  1711. ASSERT_TRUE(expected);
  1712. runToElementTest<D2ClientConfig>(expected, *d2_client_config);
  1713. }
  1714. /// @brief Checks that D2 client can be configured with enable flag of
  1715. /// false only.
  1716. TEST_F(ParseConfigTest, validDisabledD2Config) {
  1717. // Configuration string. This defines a disabled D2 client config.
  1718. std::string config_str =
  1719. "{ \"dhcp-ddns\" :"
  1720. " {"
  1721. " \"enable-updates\" : false"
  1722. " }"
  1723. "}";
  1724. // Verify that the configuration string parses.
  1725. int rcode = parseConfiguration(config_str);
  1726. ASSERT_TRUE(rcode == 0) << error_text_;
  1727. // Verify that DHCP-DDNS is disabled.
  1728. EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
  1729. // Make sure fetched config agrees.
  1730. D2ClientConfigPtr d2_client_config;
  1731. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  1732. EXPECT_TRUE(d2_client_config);
  1733. EXPECT_FALSE(d2_client_config->getEnableUpdates());
  1734. }
  1735. /// @brief Checks that given a partial configuration, parser supplies
  1736. /// default values
  1737. TEST_F(ParseConfigTest, parserDefaultsD2Config) {
  1738. // Configuration string. This defines an enabled D2 client config
  1739. // with the mandatory parameter in such a case, all other parameters
  1740. // are optional and their default values will be used.
  1741. std::string config_str =
  1742. "{ \"dhcp-ddns\" :"
  1743. " {"
  1744. " \"enable-updates\" : true, "
  1745. " \"qualifying-suffix\" : \"test.suffix.\" "
  1746. " }"
  1747. "}";
  1748. // Verify that the configuration string parses.
  1749. int rcode = parseConfiguration(config_str);
  1750. ASSERT_TRUE(rcode == 0) << error_text_;
  1751. // Verify that DHCP-DDNS is enabled.
  1752. EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
  1753. // Make sure fetched config is correct.
  1754. D2ClientConfigPtr d2_client_config;
  1755. ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
  1756. EXPECT_TRUE(d2_client_config);
  1757. EXPECT_TRUE(d2_client_config->getEnableUpdates());
  1758. EXPECT_EQ(D2ClientConfig::DFT_SERVER_IP,
  1759. d2_client_config->getServerIp().toText());
  1760. EXPECT_EQ(D2ClientConfig::DFT_SERVER_PORT,
  1761. d2_client_config->getServerPort());
  1762. EXPECT_EQ(dhcp_ddns::stringToNcrProtocol(D2ClientConfig::DFT_NCR_PROTOCOL),
  1763. d2_client_config->getNcrProtocol());
  1764. EXPECT_EQ(dhcp_ddns::stringToNcrFormat(D2ClientConfig::DFT_NCR_FORMAT),
  1765. d2_client_config->getNcrFormat());
  1766. EXPECT_EQ(D2ClientConfig::DFT_ALWAYS_INCLUDE_FQDN,
  1767. d2_client_config->getAlwaysIncludeFqdn());
  1768. EXPECT_EQ(D2ClientConfig::DFT_OVERRIDE_NO_UPDATE,
  1769. d2_client_config->getOverrideNoUpdate());
  1770. EXPECT_EQ(D2ClientConfig::DFT_OVERRIDE_CLIENT_UPDATE,
  1771. d2_client_config->getOverrideClientUpdate());
  1772. EXPECT_EQ(D2ClientConfig::
  1773. stringToReplaceClientNameMode(D2ClientConfig::
  1774. DFT_REPLACE_CLIENT_NAME_MODE),
  1775. d2_client_config->getReplaceClientNameMode());
  1776. EXPECT_EQ(D2ClientConfig::DFT_GENERATED_PREFIX,
  1777. d2_client_config->getGeneratedPrefix());
  1778. EXPECT_EQ("test.suffix.",
  1779. d2_client_config->getQualifyingSuffix());
  1780. }
  1781. /// @brief Check various invalid D2 client configurations.
  1782. TEST_F(ParseConfigTest, invalidD2Config) {
  1783. std::string invalid_configs[] = {
  1784. // Must supply qualifying-suffix when updates are enabled
  1785. "{ \"dhcp-ddns\" :"
  1786. " {"
  1787. " \"enable-updates\" : true"
  1788. " }"
  1789. "}",
  1790. // Invalid server ip value
  1791. "{ \"dhcp-ddns\" :"
  1792. " {"
  1793. " \"enable-updates\" : true, "
  1794. " \"server-ip\" : \"x192.0.2.0\", "
  1795. " \"server-port\" : 53001, "
  1796. " \"ncr-protocol\" : \"UDP\", "
  1797. " \"ncr-format\" : \"JSON\", "
  1798. " \"always-include-fqdn\" : true, "
  1799. " \"override-no-update\" : true, "
  1800. " \"override-client-update\" : true, "
  1801. " \"replace-client-name\" : true, "
  1802. " \"generated-prefix\" : \"test.prefix\", "
  1803. " \"qualifying-suffix\" : \"test.suffix.\" "
  1804. " }"
  1805. "}",
  1806. // Unknown protocol
  1807. "{ \"dhcp-ddns\" :"
  1808. " {"
  1809. " \"enable-updates\" : true, "
  1810. " \"server-ip\" : \"192.0.2.0\", "
  1811. " \"server-port\" : 53001, "
  1812. " \"ncr-protocol\" : \"Bogus\", "
  1813. " \"ncr-format\" : \"JSON\", "
  1814. " \"always-include-fqdn\" : true, "
  1815. " \"override-no-update\" : true, "
  1816. " \"override-client-update\" : true, "
  1817. " \"replace-client-name\" : true, "
  1818. " \"generated-prefix\" : \"test.prefix\", "
  1819. " \"qualifying-suffix\" : \"test.suffix.\" "
  1820. " }"
  1821. "}",
  1822. // Unsupported protocol
  1823. "{ \"dhcp-ddns\" :"
  1824. " {"
  1825. " \"enable-updates\" : true, "
  1826. " \"server-ip\" : \"192.0.2.0\", "
  1827. " \"server-port\" : 53001, "
  1828. " \"ncr-protocol\" : \"TCP\", "
  1829. " \"ncr-format\" : \"JSON\", "
  1830. " \"always-include-fqdn\" : true, "
  1831. " \"override-no-update\" : true, "
  1832. " \"override-client-update\" : true, "
  1833. " \"replace-client-name\" : true, "
  1834. " \"generated-prefix\" : \"test.prefix\", "
  1835. " \"qualifying-suffix\" : \"test.suffix.\" "
  1836. " }"
  1837. "}",
  1838. // Unknown format
  1839. "{ \"dhcp-ddns\" :"
  1840. " {"
  1841. " \"enable-updates\" : true, "
  1842. " \"server-ip\" : \"192.0.2.0\", "
  1843. " \"server-port\" : 53001, "
  1844. " \"ncr-protocol\" : \"UDP\", "
  1845. " \"ncr-format\" : \"Bogus\", "
  1846. " \"always-include-fqdn\" : true, "
  1847. " \"override-no-update\" : true, "
  1848. " \"override-client-update\" : true, "
  1849. " \"replace-client-name\" : true, "
  1850. " \"generated-prefix\" : \"test.prefix\", "
  1851. " \"qualifying-suffix\" : \"test.suffix.\" "
  1852. " }"
  1853. "}",
  1854. // Invalid Port
  1855. "{ \"dhcp-ddns\" :"
  1856. " {"
  1857. " \"enable-updates\" : true, "
  1858. " \"server-ip\" : \"192.0.2.0\", "
  1859. " \"server-port\" : \"bogus\", "
  1860. " \"ncr-protocol\" : \"UDP\", "
  1861. " \"ncr-format\" : \"JSON\", "
  1862. " \"always-include-fqdn\" : true, "
  1863. " \"override-no-update\" : true, "
  1864. " \"override-client-update\" : true, "
  1865. " \"replace-client-name\" : true, "
  1866. " \"generated-prefix\" : \"test.prefix\", "
  1867. " \"qualifying-suffix\" : \"test.suffix.\" "
  1868. " }"
  1869. "}",
  1870. // Mismatched server and sender IPs
  1871. "{ \"dhcp-ddns\" :"
  1872. " {"
  1873. " \"enable-updates\" : true, "
  1874. " \"server-ip\" : \"192.0.2.0\", "
  1875. " \"server-port\" : 3432, "
  1876. " \"sender-ip\" : \"3001::5\", "
  1877. " \"sender-port\" : 3433, "
  1878. " \"max-queue-size\" : 2048, "
  1879. " \"ncr-protocol\" : \"UDP\", "
  1880. " \"ncr-format\" : \"JSON\", "
  1881. " \"always-include-fqdn\" : true, "
  1882. " \"override-no-update\" : true, "
  1883. " \"override-client-update\" : true, "
  1884. " \"replace-client-name\" : true, "
  1885. " \"generated-prefix\" : \"test.prefix\", "
  1886. " \"qualifying-suffix\" : \"test.suffix.\" "
  1887. " }"
  1888. "}",
  1889. // Identical server and sender IP/port
  1890. "{ \"dhcp-ddns\" :"
  1891. " {"
  1892. " \"enable-updates\" : true, "
  1893. " \"server-ip\" : \"3001::5\", "
  1894. " \"server-port\" : 3433, "
  1895. " \"sender-ip\" : \"3001::5\", "
  1896. " \"sender-port\" : 3433, "
  1897. " \"max-queue-size\" : 2048, "
  1898. " \"ncr-protocol\" : \"UDP\", "
  1899. " \"ncr-format\" : \"JSON\", "
  1900. " \"always-include-fqdn\" : true, "
  1901. " \"override-no-update\" : true, "
  1902. " \"override-client-update\" : true, "
  1903. " \"replace-client-name\" : true, "
  1904. " \"generated-prefix\" : \"test.prefix\", "
  1905. " \"qualifying-suffix\" : \"test.suffix.\" "
  1906. " }"
  1907. "}",
  1908. // Invalid replace-client-name value
  1909. "{ \"dhcp-ddns\" :"
  1910. " {"
  1911. " \"enable-updates\" : true, "
  1912. " \"server-ip\" : \"3001::5\", "
  1913. " \"server-port\" : 3433, "
  1914. " \"sender-ip\" : \"3001::5\", "
  1915. " \"sender-port\" : 3434, "
  1916. " \"max-queue-size\" : 2048, "
  1917. " \"ncr-protocol\" : \"UDP\", "
  1918. " \"ncr-format\" : \"JSON\", "
  1919. " \"always-include-fqdn\" : true, "
  1920. " \"override-no-update\" : true, "
  1921. " \"override-client-update\" : true, "
  1922. " \"replace-client-name\" : \"BOGUS\", "
  1923. " \"generated-prefix\" : \"test.prefix\", "
  1924. " \"qualifying-suffix\" : \"test.suffix.\" "
  1925. " }"
  1926. "}",
  1927. // stop
  1928. ""
  1929. };
  1930. // Fetch the original config.
  1931. D2ClientConfigPtr original_config;
  1932. ASSERT_NO_THROW(original_config = CfgMgr::instance().getD2ClientConfig());
  1933. // Iterate through the invalid configuration strings, attempting to
  1934. // parse each one. They should fail to parse, but fail gracefully.
  1935. D2ClientConfigPtr current_config;
  1936. int i = 0;
  1937. while (!invalid_configs[i].empty()) {
  1938. // Verify that the configuration string parses without throwing.
  1939. int rcode = parseConfiguration(invalid_configs[i]);
  1940. // Verify that parse result indicates a parsing error.
  1941. ASSERT_TRUE(rcode != 0) << "Invalid config #: " << i
  1942. << " should not have passed!";
  1943. // Verify that the "official" config still matches the original config.
  1944. ASSERT_NO_THROW(current_config =
  1945. CfgMgr::instance().getD2ClientConfig());
  1946. EXPECT_EQ(*original_config, *current_config);
  1947. ++i;
  1948. }
  1949. }
  1950. /// @brief Checks that a valid relay info structure for IPv4 can be handled
  1951. TEST_F(ParseConfigTest, validRelayInfo4) {
  1952. // Relay information structure. Very simple for now.
  1953. std::string config_str =
  1954. " {"
  1955. " \"ip-address\" : \"192.0.2.1\""
  1956. " }";
  1957. ElementPtr json = Element::fromJSON(config_str);
  1958. // We need to set the default ip-address to something.
  1959. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("0.0.0.0")));
  1960. RelayInfoParser parser(Option::V4);
  1961. // Subnet4 parser will pass 0.0.0.0 to the RelayInfoParser
  1962. EXPECT_NO_THROW(parser.parse(result, json));
  1963. EXPECT_EQ("192.0.2.1", result->addr_.toText());
  1964. }
  1965. /// @brief Checks that a bogus relay info structure for IPv4 is rejected.
  1966. TEST_F(ParseConfigTest, bogusRelayInfo4) {
  1967. // Invalid config (wrong family type of the ip-address field)
  1968. std::string config_str_bogus1 =
  1969. " {"
  1970. " \"ip-address\" : \"2001:db8::1\""
  1971. " }";
  1972. ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
  1973. // Invalid config (that thing is not an IPv4 address)
  1974. std::string config_str_bogus2 =
  1975. " {"
  1976. " \"ip-address\" : \"256.345.123.456\""
  1977. " }";
  1978. ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
  1979. // Invalid config (ip-address is mandatory)
  1980. std::string config_str_bogus3 =
  1981. " {"
  1982. " }";
  1983. ElementPtr json_bogus3 = Element::fromJSON(config_str_bogus3);
  1984. // We need to set the default ip-address to something.
  1985. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(IOAddress::IPV4_ZERO_ADDRESS()));
  1986. RelayInfoParser parser(Option::V4);
  1987. // wrong family type
  1988. EXPECT_THROW(parser.parse(result, json_bogus1), DhcpConfigError);
  1989. // Too large byte values in pseudo-IPv4 addr
  1990. EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
  1991. // Mandatory ip-address is missing. What a pity.
  1992. EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
  1993. }
  1994. /// @brief Checks that a valid relay info structure for IPv6 can be handled
  1995. TEST_F(ParseConfigTest, validRelayInfo6) {
  1996. // Relay information structure. Very simple for now.
  1997. std::string config_str =
  1998. " {"
  1999. " \"ip-address\" : \"2001:db8::1\""
  2000. " }";
  2001. ElementPtr json = Element::fromJSON(config_str);
  2002. // We need to set the default ip-address to something.
  2003. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
  2004. RelayInfoParser parser(Option::V6);
  2005. // Subnet4 parser will pass :: to the RelayInfoParser
  2006. EXPECT_NO_THROW(parser.parse(result, json));
  2007. EXPECT_EQ("2001:db8::1", result->addr_.toText());
  2008. }
  2009. /// @brief Checks that a valid relay info structure for IPv6 can be handled
  2010. TEST_F(ParseConfigTest, bogusRelayInfo6) {
  2011. // Invalid config (wrong family type of the ip-address field
  2012. std::string config_str_bogus1 =
  2013. " {"
  2014. " \"ip-address\" : \"192.0.2.1\""
  2015. " }";
  2016. ElementPtr json_bogus1 = Element::fromJSON(config_str_bogus1);
  2017. // That IPv6 address doesn't look right
  2018. std::string config_str_bogus2 =
  2019. " {"
  2020. " \"ip-address\" : \"2001:db8:::4\""
  2021. " }";
  2022. ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
  2023. // Missing mandatory ip-address field.
  2024. std::string config_str_bogus3 =
  2025. " {"
  2026. " }";
  2027. ElementPtr json_bogus3 = Element::fromJSON(config_str_bogus3);
  2028. // We need to set the default ip-address to something.
  2029. Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
  2030. RelayInfoParser parser(Option::V6);
  2031. // Negative scenario (wrong family type)
  2032. EXPECT_THROW(parser.parse(result, json_bogus1), DhcpConfigError);
  2033. // Looks like IPv6 address, but has too many colons
  2034. EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
  2035. // Mandatory ip-address is missing. What a pity.
  2036. EXPECT_THROW(parser.parse(result, json_bogus3), DhcpConfigError);
  2037. }
  2038. // There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
  2039. // (see CtrlDhcpv4SrvTest.commandSocketBasic in
  2040. // src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc).
  2041. }; // Anonymous namespace