dhcp_parsers_unittest.cc 90 KB

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