dhcp_parsers.cc 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351
  1. // Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <dhcp/iface_mgr.h>
  15. #include <dhcp/libdhcp++.h>
  16. #include <dhcpsrv/cfgmgr.h>
  17. #include <dhcpsrv/dhcp_parsers.h>
  18. #include <hooks/hooks_manager.h>
  19. #include <util/encode/hex.h>
  20. #include <util/strutil.h>
  21. #include <boost/algorithm/string.hpp>
  22. #include <boost/foreach.hpp>
  23. #include <boost/lexical_cast.hpp>
  24. #include <map>
  25. #include <string>
  26. #include <vector>
  27. using namespace std;
  28. using namespace isc::data;
  29. using namespace isc::hooks;
  30. namespace isc {
  31. namespace dhcp {
  32. namespace {
  33. const char* ALL_IFACES_KEYWORD = "*";
  34. }
  35. // *********************** ParserContext *************************
  36. ParserContext::ParserContext(Option::Universe universe):
  37. boolean_values_(new BooleanStorage()),
  38. uint32_values_(new Uint32Storage()),
  39. string_values_(new StringStorage()),
  40. options_(new OptionStorage()),
  41. option_defs_(new OptionDefStorage()),
  42. hooks_libraries_(),
  43. universe_(universe)
  44. {
  45. }
  46. ParserContext::ParserContext(const ParserContext& rhs):
  47. boolean_values_(),
  48. uint32_values_(),
  49. string_values_(),
  50. options_(),
  51. option_defs_(),
  52. hooks_libraries_(),
  53. universe_(rhs.universe_)
  54. {
  55. copyContext(rhs);
  56. }
  57. ParserContext&
  58. // The cppcheck version 1.56 doesn't recognize that copyContext
  59. // copies all context fields.
  60. // cppcheck-suppress operatorEqVarError
  61. ParserContext::operator=(const ParserContext& rhs) {
  62. if (this != &rhs) {
  63. copyContext(rhs);
  64. }
  65. return (*this);
  66. }
  67. void
  68. ParserContext::copyContext(const ParserContext& ctx) {
  69. copyContextPointer(ctx.boolean_values_, boolean_values_);
  70. copyContextPointer(ctx.uint32_values_, uint32_values_);
  71. copyContextPointer(ctx.string_values_, string_values_);
  72. copyContextPointer(ctx.options_, options_);
  73. copyContextPointer(ctx.option_defs_, option_defs_);
  74. copyContextPointer(ctx.hooks_libraries_, hooks_libraries_);
  75. // Copy universe.
  76. universe_ = ctx.universe_;
  77. }
  78. template<typename T>
  79. void
  80. ParserContext::copyContextPointer(const boost::shared_ptr<T>& source_ptr,
  81. boost::shared_ptr<T>& dest_ptr) {
  82. if (source_ptr) {
  83. dest_ptr.reset(new T(*source_ptr));
  84. } else {
  85. dest_ptr.reset();
  86. }
  87. }
  88. // **************************** DebugParser *************************
  89. DebugParser::DebugParser(const std::string& param_name)
  90. :param_name_(param_name) {
  91. }
  92. void
  93. DebugParser::build(ConstElementPtr new_config) {
  94. value_ = new_config;
  95. std::cout << "Build for token: [" << param_name_ << "] = ["
  96. << value_->str() << "]" << std::endl;
  97. }
  98. void
  99. DebugParser::commit() {
  100. // Debug message. The whole DebugParser class is used only for parser
  101. // debugging, and is not used in production code. It is very convenient
  102. // to keep it around. Please do not turn this cout into logger calls.
  103. std::cout << "Commit for token: [" << param_name_ << "] = ["
  104. << value_->str() << "]" << std::endl;
  105. }
  106. // **************************** BooleanParser *************************
  107. template<> void ValueParser<bool>::build(isc::data::ConstElementPtr value) {
  108. // The Config Manager checks if user specified a
  109. // valid value for a boolean parameter: True or False.
  110. // We should have a boolean Element, use value directly
  111. try {
  112. value_ = value->boolValue();
  113. } catch (const isc::data::TypeError &) {
  114. isc_throw(BadValue, " Wrong value type for " << param_name_
  115. << " : build called with a non-boolean element.");
  116. }
  117. }
  118. // **************************** Uin32Parser *************************
  119. template<> void ValueParser<uint32_t>::build(ConstElementPtr value) {
  120. int64_t check;
  121. string x = value->str();
  122. try {
  123. check = boost::lexical_cast<int64_t>(x);
  124. } catch (const boost::bad_lexical_cast &) {
  125. isc_throw(BadValue, "Failed to parse value " << value->str()
  126. << " as unsigned 32-bit integer.");
  127. }
  128. if (check > std::numeric_limits<uint32_t>::max()) {
  129. isc_throw(BadValue, "Value " << value->str() << "is too large"
  130. << " for unsigned 32-bit integer.");
  131. }
  132. if (check < 0) {
  133. isc_throw(BadValue, "Value " << value->str() << "is negative."
  134. << " Only 0 or larger are allowed for unsigned 32-bit integer.");
  135. }
  136. // value is small enough to fit
  137. value_ = static_cast<uint32_t>(check);
  138. }
  139. // **************************** StringParser *************************
  140. template <> void ValueParser<std::string>::build(ConstElementPtr value) {
  141. value_ = value->str();
  142. boost::erase_all(value_, "\"");
  143. }
  144. // ******************** InterfaceListConfigParser *************************
  145. InterfaceListConfigParser::
  146. InterfaceListConfigParser(const std::string& param_name)
  147. : activate_all_(false),
  148. param_name_(param_name) {
  149. if (param_name_ != "interfaces") {
  150. isc_throw(BadValue, "Internal error. Interface configuration "
  151. "parser called for the wrong parameter: " << param_name);
  152. }
  153. }
  154. void
  155. InterfaceListConfigParser::build(ConstElementPtr value) {
  156. // First, we iterate over all specified entries and add it to the
  157. // local container so as we can do some basic validation, e.g. eliminate
  158. // duplicates.
  159. BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
  160. std::string iface_name = iface->stringValue();
  161. if (iface_name != ALL_IFACES_KEYWORD) {
  162. // Let's eliminate duplicates. We could possibly allow duplicates,
  163. // but if someone specified duplicated interface name it is likely
  164. // that he mistyped the configuration. Failing here should draw his
  165. // attention.
  166. if (isIfaceAdded(iface_name)) {
  167. isc_throw(isc::dhcp::DhcpConfigError, "duplicate interface"
  168. << " name '" << iface_name << "' specified in '"
  169. << param_name_ << "' configuration parameter");
  170. }
  171. // @todo check that this interface exists in the system!
  172. // The IfaceMgr exposes mechanisms to check this.
  173. // Add the interface name if ok.
  174. interfaces_.push_back(iface_name);
  175. } else {
  176. activate_all_ = true;
  177. }
  178. }
  179. }
  180. void
  181. InterfaceListConfigParser::commit() {
  182. CfgMgr& cfg_mgr = CfgMgr::instance();
  183. // Remove active interfaces and clear a flag which marks all interfaces
  184. // active
  185. cfg_mgr.deleteActiveIfaces();
  186. if (activate_all_) {
  187. // Activate all interfaces. There is not need to add their names
  188. // explicitly.
  189. cfg_mgr.activateAllIfaces();
  190. } else {
  191. // Explicitly add names of the interfaces which server should listen on.
  192. BOOST_FOREACH(std::string iface, interfaces_) {
  193. cfg_mgr.addActiveIface(iface);
  194. }
  195. }
  196. }
  197. bool
  198. InterfaceListConfigParser::isIfaceAdded(const std::string& iface) const {
  199. for (IfaceListStorage::const_iterator it = interfaces_.begin();
  200. it != interfaces_.end(); ++it) {
  201. if (iface == *it) {
  202. return (true);
  203. }
  204. }
  205. return (false);
  206. }
  207. // ******************** HooksLibrariesParser *************************
  208. HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name)
  209. : libraries_(), changed_(false)
  210. {
  211. // Sanity check on the name.
  212. if (param_name != "hooks-libraries") {
  213. isc_throw(BadValue, "Internal error. Hooks libraries "
  214. "parser called for the wrong parameter: " << param_name);
  215. }
  216. }
  217. void
  218. HooksLibrariesParser::build(ConstElementPtr value) {
  219. // Initialize.
  220. libraries_.clear();
  221. changed_ = false;
  222. // Extract the list of libraries.
  223. BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
  224. string libname = iface->str();
  225. boost::erase_all(libname, "\"");
  226. libraries_.push_back(libname);
  227. }
  228. // Check if the list of libraries has changed. If not, nothing is done
  229. // - the command "DhcpN libreload" is required to reload the same
  230. // libraries (this prevents needless reloads when anything else in the
  231. // configuration is changed).
  232. vector<string> current_libraries = HooksManager::getLibraryNames();
  233. if (current_libraries == libraries_) {
  234. return;
  235. }
  236. // Library list has changed, validate each of the libraries specified.
  237. vector<string> error_libs = HooksManager::validateLibraries(libraries_);
  238. if (!error_libs.empty()) {
  239. // Construct the list of libraries in error for the message.
  240. string error_list = error_libs[0];
  241. for (int i = 1; i < error_libs.size(); ++i) {
  242. error_list += (string(", ") + error_libs[i]);
  243. }
  244. isc_throw(DhcpConfigError, "hooks libraries failed to validate - "
  245. "library or libraries in error are: " + error_list);
  246. }
  247. // The library list has changed and the libraries are valid, so flag for
  248. // update when commit() is called.
  249. changed_ = true;
  250. }
  251. void
  252. HooksLibrariesParser::commit() {
  253. /// Commits the list of libraries to the configuration manager storage if
  254. /// the list of libraries has changed.
  255. if (changed_) {
  256. // TODO Delete any stored CalloutHandles before reloading the
  257. // libraries
  258. HooksManager::loadLibraries(libraries_);
  259. }
  260. }
  261. // Method for testing
  262. void
  263. HooksLibrariesParser::getLibraries(std::vector<std::string>& libraries,
  264. bool& changed) {
  265. libraries = libraries_;
  266. changed = changed_;
  267. }
  268. // **************************** OptionDataParser *************************
  269. OptionDataParser::OptionDataParser(const std::string&, OptionStoragePtr options,
  270. ParserContextPtr global_context)
  271. : boolean_values_(new BooleanStorage()),
  272. string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
  273. options_(options), option_descriptor_(false),
  274. global_context_(global_context) {
  275. if (!options_) {
  276. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  277. << "options storage may not be NULL");
  278. }
  279. if (!global_context_) {
  280. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  281. << "context may may not be NULL");
  282. }
  283. }
  284. void
  285. OptionDataParser::build(ConstElementPtr option_data_entries) {
  286. BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
  287. ParserPtr parser;
  288. if (param.first == "name" || param.first == "data" ||
  289. param.first == "space") {
  290. StringParserPtr name_parser(new StringParser(param.first,
  291. string_values_));
  292. parser = name_parser;
  293. } else if (param.first == "code") {
  294. Uint32ParserPtr code_parser(new Uint32Parser(param.first,
  295. uint32_values_));
  296. parser = code_parser;
  297. } else if (param.first == "csv-format") {
  298. BooleanParserPtr value_parser(new BooleanParser(param.first,
  299. boolean_values_));
  300. parser = value_parser;
  301. } else {
  302. isc_throw(DhcpConfigError,
  303. "Parser error: option-data parameter not supported: "
  304. << param.first);
  305. }
  306. parser->build(param.second);
  307. // Before we can create an option we need to get the data from
  308. // the child parsers. The only way to do it is to invoke commit
  309. // on them so as they store the values in appropriate storages
  310. // that this class provided to them. Note that this will not
  311. // modify values stored in the global storages so the configuration
  312. // will remain consistent even parsing fails somewhere further on.
  313. parser->commit();
  314. }
  315. // Try to create the option instance.
  316. createOption();
  317. }
  318. void
  319. OptionDataParser::commit() {
  320. if (!option_descriptor_.option) {
  321. // Before we can commit the new option should be configured. If it is
  322. // not than somebody must have called commit() before build().
  323. isc_throw(isc::InvalidOperation,
  324. "parser logic error: no option has been configured and"
  325. " thus there is nothing to commit. Has build() been called?");
  326. }
  327. uint16_t opt_type = option_descriptor_.option->getType();
  328. Subnet::OptionContainerPtr options = options_->getItems(option_space_);
  329. // The getItems() should never return NULL pointer. If there are no
  330. // options configured for the particular option space a pointer
  331. // to an empty container should be returned.
  332. assert(options);
  333. Subnet::OptionContainerTypeIndex& idx = options->get<1>();
  334. // Try to find options with the particular option code in the main
  335. // storage. If found, remove these options because they will be
  336. // replaced with new one.
  337. Subnet::OptionContainerTypeRange range = idx.equal_range(opt_type);
  338. if (std::distance(range.first, range.second) > 0) {
  339. idx.erase(range.first, range.second);
  340. }
  341. // Append new option to the main storage.
  342. options_->addItem(option_descriptor_, option_space_);
  343. }
  344. void
  345. OptionDataParser::createOption() {
  346. // Option code is held in the uint32_t storage but is supposed to
  347. // be uint16_t value. We need to check that value in the configuration
  348. // does not exceed range of uint8_t for DHCPv4, uint16_t for DHCPv6 and
  349. // is not zero.
  350. uint32_t option_code = uint32_values_->getParam("code");
  351. if (option_code == 0) {
  352. isc_throw(DhcpConfigError, "option code must not be zero."
  353. << " Option code '0' is reserved.");
  354. } else if (global_context_->universe_ == Option::V4 &&
  355. option_code > std::numeric_limits<uint8_t>::max()) {
  356. isc_throw(DhcpConfigError, "invalid option code '" << option_code
  357. << "', it must not exceed '"
  358. << static_cast<int>(std::numeric_limits<uint8_t>::max())
  359. << "'");
  360. } else if (global_context_->universe_ == Option::V6 &&
  361. option_code > std::numeric_limits<uint16_t>::max()) {
  362. isc_throw(DhcpConfigError, "invalid option code '" << option_code
  363. << "', it must not exceed '"
  364. << std::numeric_limits<uint16_t>::max()
  365. << "'");
  366. }
  367. // Check that the option name has been specified, is non-empty and does not
  368. // contain spaces
  369. std::string option_name = string_values_->getParam("name");
  370. if (option_name.empty()) {
  371. isc_throw(DhcpConfigError, "name of the option with code '"
  372. << option_code << "' is empty");
  373. } else if (option_name.find(" ") != std::string::npos) {
  374. isc_throw(DhcpConfigError, "invalid option name '" << option_name
  375. << "', space character is not allowed");
  376. }
  377. std::string option_space = string_values_->getParam("space");
  378. if (!OptionSpace::validateName(option_space)) {
  379. isc_throw(DhcpConfigError, "invalid option space name '"
  380. << option_space << "' specified for option '"
  381. << option_name << "' (code '" << option_code
  382. << "')");
  383. }
  384. const bool csv_format = boolean_values_->getParam("csv-format");
  385. // Find the Option Definition for the option by its option code.
  386. // findOptionDefinition will throw if not found, no need to test.
  387. OptionDefinitionPtr def;
  388. if (!(def = findServerSpaceOptionDefinition(option_space, option_code))) {
  389. // If we are not dealing with a standard option then we
  390. // need to search for its definition among user-configured
  391. // options. They are expected to be in the global storage
  392. // already.
  393. OptionDefContainerPtr defs =
  394. global_context_->option_defs_->getItems(option_space);
  395. // The getItems() should never return the NULL pointer. If there are
  396. // no option definitions for the particular option space a pointer
  397. // to an empty container should be returned.
  398. assert(defs);
  399. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  400. OptionDefContainerTypeRange range = idx.equal_range(option_code);
  401. if (std::distance(range.first, range.second) > 0) {
  402. def = *range.first;
  403. }
  404. // It's ok if we don't have option format if the option is
  405. // specified as hex
  406. if (!def && csv_format) {
  407. isc_throw(DhcpConfigError, "definition for the option '"
  408. << option_space << "." << option_name
  409. << "' having code '" << option_code
  410. << "' does not exist");
  411. }
  412. }
  413. // Get option data from the configuration database ('data' field).
  414. std::string option_data = string_values_->getParam("data");
  415. // Transform string of hexadecimal digits into binary format.
  416. std::vector<uint8_t> binary;
  417. std::vector<std::string> data_tokens;
  418. if (csv_format) {
  419. // If the option data is specified as a string of comma
  420. // separated values then we need to split this string into
  421. // individual values - each value will be used to initialize
  422. // one data field of an option.
  423. data_tokens = isc::util::str::tokens(option_data, ",");
  424. } else {
  425. // Otherwise, the option data is specified as a string of
  426. // hexadecimal digits that we have to turn into binary format.
  427. try {
  428. // The decodeHex function expects that the string contains an
  429. // even number of digits. If we don't meet this requirement,
  430. // we have to insert a leading 0.
  431. if (!option_data.empty() && option_data.length() % 2) {
  432. option_data = option_data.insert(0, "0");
  433. }
  434. util::encode::decodeHex(option_data, binary);
  435. } catch (...) {
  436. isc_throw(DhcpConfigError, "option data is not a valid"
  437. << " string of hexadecimal digits: " << option_data);
  438. }
  439. }
  440. OptionPtr option;
  441. if (!def) {
  442. if (csv_format) {
  443. isc_throw(DhcpConfigError, "the CSV option data format can be"
  444. " used to specify values for an option that has a"
  445. " definition. The option with code " << option_code
  446. << " does not have a definition.");
  447. }
  448. // @todo We have a limited set of option definitions intiialized at
  449. // the moment. In the future we want to initialize option definitions
  450. // for all options. Consequently an error will be issued if an option
  451. // definition does not exist for a particular option code. For now it is
  452. // ok to create generic option if definition does not exist.
  453. OptionPtr option(new Option(global_context_->universe_,
  454. static_cast<uint16_t>(option_code), binary));
  455. // The created option is stored in option_descriptor_ class member
  456. // until the commit stage when it is inserted into the main storage.
  457. // If an option with the same code exists in main storage already the
  458. // old option is replaced.
  459. option_descriptor_.option = option;
  460. option_descriptor_.persistent = false;
  461. } else {
  462. // Option name should match the definition. The option name
  463. // may seem to be redundant but in the future we may want
  464. // to reference options and definitions using their names
  465. // and/or option codes so keeping the option name in the
  466. // definition of option value makes sense.
  467. if (def->getName() != option_name) {
  468. isc_throw(DhcpConfigError, "specified option name '"
  469. << option_name << "' does not match the "
  470. << "option definition: '" << option_space
  471. << "." << def->getName() << "'");
  472. }
  473. // Option definition has been found so let's use it to create
  474. // an instance of our option.
  475. try {
  476. OptionPtr option = csv_format ?
  477. def->optionFactory(global_context_->universe_,
  478. option_code, data_tokens) :
  479. def->optionFactory(global_context_->universe_,
  480. option_code, binary);
  481. Subnet::OptionDescriptor desc(option, false);
  482. option_descriptor_.option = option;
  483. option_descriptor_.persistent = false;
  484. } catch (const isc::Exception& ex) {
  485. isc_throw(DhcpConfigError, "option data does not match"
  486. << " option definition (space: " << option_space
  487. << ", code: " << option_code << "): "
  488. << ex.what());
  489. }
  490. }
  491. // All went good, so we can set the option space name.
  492. option_space_ = option_space;
  493. }
  494. // **************************** OptionDataListParser *************************
  495. OptionDataListParser::OptionDataListParser(const std::string&,
  496. OptionStoragePtr options, ParserContextPtr global_context,
  497. OptionDataParserFactory* optionDataParserFactory)
  498. : options_(options), local_options_(new OptionStorage()),
  499. global_context_(global_context),
  500. optionDataParserFactory_(optionDataParserFactory) {
  501. if (!options_) {
  502. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  503. << "options storage may not be NULL");
  504. }
  505. if (!options_) {
  506. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  507. << "context may not be NULL");
  508. }
  509. if (!optionDataParserFactory_) {
  510. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  511. << "option data parser factory may not be NULL");
  512. }
  513. }
  514. void
  515. OptionDataListParser::build(ConstElementPtr option_data_list) {
  516. BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
  517. boost::shared_ptr<OptionDataParser>
  518. parser((*optionDataParserFactory_)("option-data",
  519. local_options_, global_context_));
  520. // options_ member will hold instances of all options thus
  521. // each OptionDataParser takes it as a storage.
  522. // Build the instance of a single option.
  523. parser->build(option_value);
  524. // Store a parser as it will be used to commit.
  525. parsers_.push_back(parser);
  526. }
  527. }
  528. void
  529. OptionDataListParser::commit() {
  530. BOOST_FOREACH(ParserPtr parser, parsers_) {
  531. parser->commit();
  532. }
  533. // Parsing was successful and we have all configured
  534. // options in local storage. We can now replace old values
  535. // with new values.
  536. std::swap(*local_options_, *options_);
  537. }
  538. // ******************************** OptionDefParser ****************************
  539. OptionDefParser::OptionDefParser(const std::string&,
  540. OptionDefStoragePtr storage)
  541. : storage_(storage), boolean_values_(new BooleanStorage()),
  542. string_values_(new StringStorage()), uint32_values_(new Uint32Storage()) {
  543. if (!storage_) {
  544. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  545. << "options storage may not be NULL");
  546. }
  547. }
  548. void
  549. OptionDefParser::build(ConstElementPtr option_def) {
  550. // Parse the elements that make up the option definition.
  551. BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
  552. std::string entry(param.first);
  553. ParserPtr parser;
  554. if (entry == "name" || entry == "type" || entry == "record-types"
  555. || entry == "space" || entry == "encapsulate") {
  556. StringParserPtr str_parser(new StringParser(entry,
  557. string_values_));
  558. parser = str_parser;
  559. } else if (entry == "code") {
  560. Uint32ParserPtr code_parser(new Uint32Parser(entry,
  561. uint32_values_));
  562. parser = code_parser;
  563. } else if (entry == "array") {
  564. BooleanParserPtr array_parser(new BooleanParser(entry,
  565. boolean_values_));
  566. parser = array_parser;
  567. } else {
  568. isc_throw(DhcpConfigError, "invalid parameter: " << entry);
  569. }
  570. parser->build(param.second);
  571. parser->commit();
  572. }
  573. // Create an instance of option definition.
  574. createOptionDef();
  575. // Get all items we collected so far for the particular option space.
  576. OptionDefContainerPtr defs = storage_->getItems(option_space_name_);
  577. // Check if there are any items with option code the same as the
  578. // one specified for the definition we are now creating.
  579. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  580. const OptionDefContainerTypeRange& range =
  581. idx.equal_range(option_definition_->getCode());
  582. // If there are any items with this option code already we need
  583. // to issue an error because we don't allow duplicates for
  584. // option definitions within an option space.
  585. if (std::distance(range.first, range.second) > 0) {
  586. isc_throw(DhcpConfigError, "duplicated option definition for"
  587. << " code '" << option_definition_->getCode() << "'");
  588. }
  589. }
  590. void
  591. OptionDefParser::commit() {
  592. if (storage_ && option_definition_ &&
  593. OptionSpace::validateName(option_space_name_)) {
  594. storage_->addItem(option_definition_, option_space_name_);
  595. }
  596. }
  597. void
  598. OptionDefParser::createOptionDef() {
  599. // Get the option space name and validate it.
  600. std::string space = string_values_->getParam("space");
  601. if (!OptionSpace::validateName(space)) {
  602. isc_throw(DhcpConfigError, "invalid option space name '"
  603. << space << "'");
  604. }
  605. // Get other parameters that are needed to create the
  606. // option definition.
  607. std::string name = string_values_->getParam("name");
  608. uint32_t code = uint32_values_->getParam("code");
  609. std::string type = string_values_->getParam("type");
  610. bool array_type = boolean_values_->getParam("array");
  611. std::string encapsulates = string_values_->getParam("encapsulate");
  612. // Create option definition.
  613. OptionDefinitionPtr def;
  614. // We need to check if user has set encapsulated option space
  615. // name. If so, different constructor will be used.
  616. if (!encapsulates.empty()) {
  617. // Arrays can't be used together with sub-options.
  618. if (array_type) {
  619. isc_throw(DhcpConfigError, "option '" << space << "."
  620. << "name" << "', comprising an array of data"
  621. << " fields may not encapsulate any option space");
  622. } else if (encapsulates == space) {
  623. isc_throw(DhcpConfigError, "option must not encapsulate"
  624. << " an option space it belongs to: '"
  625. << space << "." << name << "' is set to"
  626. << " encapsulate '" << space << "'");
  627. } else {
  628. def.reset(new OptionDefinition(name, code, type,
  629. encapsulates.c_str()));
  630. }
  631. } else {
  632. def.reset(new OptionDefinition(name, code, type, array_type));
  633. }
  634. // The record-types field may carry a list of comma separated names
  635. // of data types that form a record.
  636. std::string record_types = string_values_->getParam("record-types");
  637. // Split the list of record types into tokens.
  638. std::vector<std::string> record_tokens =
  639. isc::util::str::tokens(record_types, ",");
  640. // Iterate over each token and add a record type into
  641. // option definition.
  642. BOOST_FOREACH(std::string record_type, record_tokens) {
  643. try {
  644. boost::trim(record_type);
  645. if (!record_type.empty()) {
  646. def->addRecordField(record_type);
  647. }
  648. } catch (const Exception& ex) {
  649. isc_throw(DhcpConfigError, "invalid record type values"
  650. << " specified for the option definition: "
  651. << ex.what());
  652. }
  653. }
  654. // Check the option definition parameters are valid.
  655. try {
  656. def->validate();
  657. } catch (const isc::Exception& ex) {
  658. isc_throw(DhcpConfigError, "invalid option definition"
  659. << " parameters: " << ex.what());
  660. }
  661. // Option definition has been created successfully.
  662. option_space_name_ = space;
  663. option_definition_ = def;
  664. }
  665. // ******************************** OptionDefListParser ************************
  666. OptionDefListParser::OptionDefListParser(const std::string&,
  667. OptionDefStoragePtr storage) :storage_(storage) {
  668. if (!storage_) {
  669. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  670. << "storage may not be NULL");
  671. }
  672. }
  673. void
  674. OptionDefListParser::build(ConstElementPtr option_def_list) {
  675. // Clear existing items in the storage.
  676. // We are going to replace all of them.
  677. storage_->clearItems();
  678. if (!option_def_list) {
  679. isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
  680. << " option definitions is NULL");
  681. }
  682. BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
  683. boost::shared_ptr<OptionDefParser>
  684. parser(new OptionDefParser("single-option-def", storage_));
  685. parser->build(option_def);
  686. parser->commit();
  687. }
  688. }
  689. void
  690. OptionDefListParser::commit() {
  691. CfgMgr& cfg_mgr = CfgMgr::instance();
  692. cfg_mgr.deleteOptionDefs();
  693. // We need to move option definitions from the temporary
  694. // storage to the storage.
  695. std::list<std::string> space_names =
  696. storage_->getOptionSpaceNames();
  697. BOOST_FOREACH(std::string space_name, space_names) {
  698. BOOST_FOREACH(OptionDefinitionPtr def,
  699. *(storage_->getItems(space_name))) {
  700. // All option definitions should be initialized to non-NULL
  701. // values. The validation is expected to be made by the
  702. // OptionDefParser when creating an option definition.
  703. assert(def);
  704. cfg_mgr.addOptionDef(def, space_name);
  705. }
  706. }
  707. }
  708. //****************************** RelayInfoParser ********************************
  709. RelayInfoParser::RelayInfoParser(const std::string&,
  710. const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
  711. const Option::Universe& family)
  712. :storage_(relay_info), local_(isc::asiolink::IOAddress(
  713. family == Option::V4 ? "0.0.0.0" : "::")),
  714. string_values_(new StringStorage()), family_(family) {
  715. if (!relay_info) {
  716. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  717. << "relay-info storage may not be NULL");
  718. }
  719. };
  720. void
  721. RelayInfoParser::build(ConstElementPtr relay_info) {
  722. BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
  723. ParserPtr parser(createConfigParser(param.first));
  724. parser->build(param.second);
  725. parser->commit();
  726. }
  727. // Get the IP address
  728. boost::scoped_ptr<asiolink::IOAddress> ip;
  729. try {
  730. ip.reset(new asiolink::IOAddress(string_values_->getParam("ip-address")));
  731. } catch (...) {
  732. isc_throw(DhcpConfigError, "Failed to parse ip-address "
  733. "value: " << string_values_->getParam("ip-address"));
  734. }
  735. if ( (ip->isV4() && family_ != Option::V4) ||
  736. (ip->isV6() && family_ != Option::V6) ) {
  737. isc_throw(DhcpConfigError, "ip-address field " << ip->toText()
  738. << "does not have IP address of expected family type: "
  739. << (family_ == Option::V4?"IPv4":"IPv6"));
  740. }
  741. local_.addr_ = *ip;
  742. }
  743. isc::dhcp::ParserPtr
  744. RelayInfoParser::createConfigParser(const std::string& parameter) {
  745. DhcpConfigParser* parser = NULL;
  746. if (parameter.compare("ip-address") == 0) {
  747. parser = new StringParser(parameter, string_values_);
  748. } else {
  749. isc_throw(NotImplemented,
  750. "parser error: RelayInfoParser parameter not supported: "
  751. << parameter);
  752. }
  753. return (isc::dhcp::ParserPtr(parser));
  754. }
  755. void
  756. RelayInfoParser::commit() {
  757. *storage_ = local_;
  758. }
  759. //****************************** PoolParser ********************************
  760. PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
  761. :pools_(pools) {
  762. if (!pools_) {
  763. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  764. << "storage may not be NULL");
  765. }
  766. }
  767. void
  768. PoolParser::build(ConstElementPtr pools_list) {
  769. BOOST_FOREACH(ConstElementPtr text_pool, pools_list->listValue()) {
  770. // That should be a single pool representation. It should contain
  771. // text is form prefix/len or first - last. Note that spaces
  772. // are allowed
  773. string txt = text_pool->stringValue();
  774. // first let's remove any whitespaces
  775. boost::erase_all(txt, " "); // space
  776. boost::erase_all(txt, "\t"); // tabulation
  777. // Is this prefix/len notation?
  778. size_t pos = txt.find("/");
  779. if (pos != string::npos) {
  780. isc::asiolink::IOAddress addr("::");
  781. uint8_t len = 0;
  782. try {
  783. addr = isc::asiolink::IOAddress(txt.substr(0, pos));
  784. // start with the first character after /
  785. string prefix_len = txt.substr(pos + 1);
  786. // It is lexical cast to int and then downcast to uint8_t.
  787. // Direct cast to uint8_t (which is really an unsigned char)
  788. // will result in interpreting the first digit as output
  789. // value and throwing exception if length is written on two
  790. // digits (because there are extra characters left over).
  791. // No checks for values over 128. Range correctness will
  792. // be checked in Pool4 constructor.
  793. len = boost::lexical_cast<int>(prefix_len);
  794. } catch (...) {
  795. isc_throw(DhcpConfigError, "Failed to parse pool "
  796. "definition: " << text_pool->stringValue());
  797. }
  798. PoolPtr pool(poolMaker(addr, len));
  799. local_pools_.push_back(pool);
  800. continue;
  801. }
  802. // Is this min-max notation?
  803. pos = txt.find("-");
  804. if (pos != string::npos) {
  805. // using min-max notation
  806. isc::asiolink::IOAddress min(txt.substr(0,pos));
  807. isc::asiolink::IOAddress max(txt.substr(pos + 1));
  808. PoolPtr pool(poolMaker(min, max));
  809. local_pools_.push_back(pool);
  810. continue;
  811. }
  812. isc_throw(DhcpConfigError, "Failed to parse pool definition:"
  813. << text_pool->stringValue() <<
  814. ". Does not contain - (for min-max) nor / (prefix/len)");
  815. }
  816. }
  817. void
  818. PoolParser::commit() {
  819. if (pools_) {
  820. // local_pools_ holds the values produced by the build function.
  821. // At this point parsing should have completed successfuly so
  822. // we can append new data to the supplied storage.
  823. pools_->insert(pools_->end(), local_pools_.begin(), local_pools_.end());
  824. }
  825. }
  826. //****************************** SubnetConfigParser *************************
  827. SubnetConfigParser::SubnetConfigParser(const std::string&,
  828. ParserContextPtr global_context,
  829. const isc::asiolink::IOAddress& default_addr)
  830. : uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
  831. pools_(new PoolStorage()), options_(new OptionStorage()),
  832. global_context_(global_context),
  833. relay_info_(new isc::dhcp::Subnet::RelayInfo(default_addr)) {
  834. // The first parameter should always be "subnet", but we don't check
  835. // against that here in case some wants to reuse this parser somewhere.
  836. if (!global_context_) {
  837. isc_throw(isc::dhcp::DhcpConfigError, "parser logic error:"
  838. << "context storage may not be NULL");
  839. }
  840. }
  841. void
  842. SubnetConfigParser::build(ConstElementPtr subnet) {
  843. BOOST_FOREACH(ConfigPair param, subnet->mapValue()) {
  844. ParserPtr parser(createSubnetConfigParser(param.first));
  845. parser->build(param.second);
  846. parsers_.push_back(parser);
  847. }
  848. // In order to create new subnet we need to get the data out
  849. // of the child parsers first. The only way to do it is to
  850. // invoke commit on them because it will make them write
  851. // parsed data into storages we have supplied.
  852. // Note that triggering commits on child parsers does not
  853. // affect global data because we supplied pointers to storages
  854. // local to this object. Thus, even if this method fails
  855. // later on, the configuration remains consistent.
  856. BOOST_FOREACH(ParserPtr parser, parsers_) {
  857. parser->commit();
  858. }
  859. // Create a subnet.
  860. createSubnet();
  861. }
  862. void
  863. SubnetConfigParser::appendSubOptions(const std::string& option_space,
  864. OptionPtr& option) {
  865. // Only non-NULL options are stored in option container.
  866. // If this option pointer is NULL this is a serious error.
  867. assert(option);
  868. OptionDefinitionPtr def;
  869. if (isServerStdOption(option_space, option->getType())) {
  870. def = getServerStdOptionDefinition(option->getType());
  871. // Definitions for some of the standard options hasn't been
  872. // implemented so it is ok to leave here.
  873. if (!def) {
  874. return;
  875. }
  876. } else {
  877. const OptionDefContainerPtr defs =
  878. global_context_->option_defs_->getItems(option_space);
  879. const OptionDefContainerTypeIndex& idx = defs->get<1>();
  880. const OptionDefContainerTypeRange& range =
  881. idx.equal_range(option->getType());
  882. // There is no definition so we have to leave.
  883. if (std::distance(range.first, range.second) == 0) {
  884. return;
  885. }
  886. def = *range.first;
  887. // If the definition exists, it must be non-NULL.
  888. // Otherwise it is a programming error.
  889. assert(def);
  890. }
  891. // We need to get option definition for the particular option space
  892. // and code. This definition holds the information whether our
  893. // option encapsulates any option space.
  894. // Get the encapsulated option space name.
  895. std::string encapsulated_space = def->getEncapsulatedSpace();
  896. // If option space name is empty it means that our option does not
  897. // encapsulate any option space (does not include sub-options).
  898. if (!encapsulated_space.empty()) {
  899. // Get the sub-options that belong to the encapsulated
  900. // option space.
  901. const Subnet::OptionContainerPtr sub_opts =
  902. global_context_->options_->getItems(encapsulated_space);
  903. // Append sub-options to the option.
  904. BOOST_FOREACH(Subnet::OptionDescriptor desc, *sub_opts) {
  905. if (desc.option) {
  906. option->addOption(desc.option);
  907. }
  908. }
  909. }
  910. }
  911. void
  912. SubnetConfigParser::createSubnet() {
  913. std::string subnet_txt;
  914. try {
  915. subnet_txt = string_values_->getParam("subnet");
  916. } catch (const DhcpConfigError &) {
  917. // rethrow with precise error
  918. isc_throw(DhcpConfigError,
  919. "Mandatory subnet definition in subnet missing");
  920. }
  921. // Remove any spaces or tabs.
  922. boost::erase_all(subnet_txt, " ");
  923. boost::erase_all(subnet_txt, "\t");
  924. // The subnet format is prefix/len. We are going to extract
  925. // the prefix portion of a subnet string to create IOAddress
  926. // object from it. IOAddress will be passed to the Subnet's
  927. // constructor later on. In order to extract the prefix we
  928. // need to get all characters preceding "/".
  929. size_t pos = subnet_txt.find("/");
  930. if (pos == string::npos) {
  931. isc_throw(DhcpConfigError,
  932. "Invalid subnet syntax (prefix/len expected):" << subnet_txt);
  933. }
  934. // Try to create the address object. It also validates that
  935. // the address syntax is ok.
  936. isc::asiolink::IOAddress addr(subnet_txt.substr(0, pos));
  937. uint8_t len = boost::lexical_cast<unsigned int>(subnet_txt.substr(pos + 1));
  938. // Call the subclass's method to instantiate the subnet
  939. initSubnet(addr, len);
  940. // Add pools to it.
  941. for (PoolStorage::iterator it = pools_->begin(); it != pools_->end();
  942. ++it) {
  943. subnet_->addPool(*it);
  944. }
  945. // Configure interface, if defined
  946. // Get interface name. If it is defined, then the subnet is available
  947. // directly over specified network interface.
  948. std::string iface;
  949. try {
  950. iface = string_values_->getParam("interface");
  951. } catch (const DhcpConfigError &) {
  952. // iface not mandatory so swallow the exception
  953. }
  954. if (!iface.empty()) {
  955. if (!IfaceMgr::instance().getIface(iface)) {
  956. isc_throw(DhcpConfigError, "Specified interface name " << iface
  957. << " for subnet " << subnet_->toText()
  958. << " is not present" << " in the system.");
  959. }
  960. subnet_->setIface(iface);
  961. }
  962. // We are going to move configured options to the Subnet object.
  963. // Configured options reside in the container where options
  964. // are grouped by space names. Thus we need to get all space names
  965. // and iterate over all options that belong to them.
  966. std::list<std::string> space_names = options_->getOptionSpaceNames();
  967. BOOST_FOREACH(std::string option_space, space_names) {
  968. // Get all options within a particular option space.
  969. BOOST_FOREACH(Subnet::OptionDescriptor desc,
  970. *options_->getItems(option_space)) {
  971. // The pointer should be non-NULL. The validation is expected
  972. // to be performed by the OptionDataParser before adding an
  973. // option descriptor to the container.
  974. assert(desc.option);
  975. // We want to check whether an option with the particular
  976. // option code has been already added. If so, we want
  977. // to issue a warning.
  978. Subnet::OptionDescriptor existing_desc =
  979. subnet_->getOptionDescriptor("option_space",
  980. desc.option->getType());
  981. if (existing_desc.option) {
  982. duplicate_option_warning(desc.option->getType(), addr);
  983. }
  984. // Add sub-options (if any).
  985. appendSubOptions(option_space, desc.option);
  986. // Check if the option space defines a vendor-option
  987. uint32_t vendor_id = optionSpaceToVendorId(option_space);
  988. if (vendor_id) {
  989. // This is a vendor option
  990. subnet_->addVendorOption(desc.option, false, vendor_id);
  991. } else {
  992. // This is a normal option
  993. subnet_->addOption(desc.option, false, option_space);
  994. }
  995. }
  996. }
  997. // Check all global options and add them to the subnet object if
  998. // they have been configured in the global scope. If they have been
  999. // configured in the subnet scope we don't add global option because
  1000. // the one configured in the subnet scope always takes precedence.
  1001. space_names = global_context_->options_->getOptionSpaceNames();
  1002. BOOST_FOREACH(std::string option_space, space_names) {
  1003. // Get all global options for the particular option space.
  1004. BOOST_FOREACH(Subnet::OptionDescriptor desc,
  1005. *(global_context_->options_->getItems(option_space))) {
  1006. // The pointer should be non-NULL. The validation is expected
  1007. // to be performed by the OptionDataParser before adding an
  1008. // option descriptor to the container.
  1009. assert(desc.option);
  1010. // Check if the particular option has been already added.
  1011. // This would mean that it has been configured in the
  1012. // subnet scope. Since option values configured in the
  1013. // subnet scope take precedence over globally configured
  1014. // values we don't add option from the global storage
  1015. // if there is one already.
  1016. Subnet::OptionDescriptor existing_desc =
  1017. subnet_->getOptionDescriptor(option_space,
  1018. desc.option->getType());
  1019. if (!existing_desc.option) {
  1020. // Add sub-options (if any).
  1021. appendSubOptions(option_space, desc.option);
  1022. uint32_t vendor_id = optionSpaceToVendorId(option_space);
  1023. if (vendor_id) {
  1024. // This is a vendor option
  1025. subnet_->addVendorOption(desc.option, false, vendor_id);
  1026. } else {
  1027. // This is a normal option
  1028. subnet_->addOption(desc.option, false, option_space);
  1029. }
  1030. }
  1031. }
  1032. }
  1033. }
  1034. uint32_t
  1035. SubnetConfigParser::optionSpaceToVendorId(const std::string& option_space) {
  1036. if (option_space.size() < 8) {
  1037. // 8 is a minimal length of "vendor-X" format
  1038. return (0);
  1039. }
  1040. if (option_space.substr(0,7) != "vendor-") {
  1041. return (0);
  1042. }
  1043. // text after "vendor-", supposedly numbers only
  1044. string x = option_space.substr(7);
  1045. int64_t check;
  1046. try {
  1047. check = boost::lexical_cast<int64_t>(x);
  1048. } catch (const boost::bad_lexical_cast &) {
  1049. /// @todo: Should we throw here?
  1050. // isc_throw(BadValue, "Failed to parse vendor-X value (" << x
  1051. // << ") as unsigned 32-bit integer.");
  1052. return (0);
  1053. }
  1054. if (check > std::numeric_limits<uint32_t>::max()) {
  1055. /// @todo: Should we throw here?
  1056. //isc_throw(BadValue, "Value " << x << "is too large"
  1057. // << " for unsigned 32-bit integer.");
  1058. return (0);
  1059. }
  1060. if (check < 0) {
  1061. /// @todo: Should we throw here?
  1062. // isc_throw(BadValue, "Value " << x << "is negative."
  1063. // << " Only 0 or larger are allowed for unsigned 32-bit integer.");
  1064. return (0);
  1065. }
  1066. // value is small enough to fit
  1067. return (static_cast<uint32_t>(check));
  1068. }
  1069. isc::dhcp::Triplet<uint32_t>
  1070. SubnetConfigParser::getParam(const std::string& name) {
  1071. uint32_t value = 0;
  1072. try {
  1073. // look for local value
  1074. value = uint32_values_->getParam(name);
  1075. } catch (const DhcpConfigError &) {
  1076. try {
  1077. // no local, use global value
  1078. value = global_context_->uint32_values_->getParam(name);
  1079. } catch (const DhcpConfigError &) {
  1080. isc_throw(DhcpConfigError, "Mandatory parameter " << name
  1081. << " missing (no global default and no subnet-"
  1082. << "specific value)");
  1083. }
  1084. }
  1085. return (Triplet<uint32_t>(value));
  1086. }
  1087. //**************************** D2ClientConfigParser **********************
  1088. D2ClientConfigParser::D2ClientConfigParser(const std::string& entry_name)
  1089. : entry_name_(entry_name), boolean_values_(new BooleanStorage()),
  1090. uint32_values_(new Uint32Storage()), string_values_(new StringStorage()),
  1091. local_client_config_() {
  1092. }
  1093. D2ClientConfigParser::~D2ClientConfigParser() {
  1094. }
  1095. void
  1096. D2ClientConfigParser::build(isc::data::ConstElementPtr client_config) {
  1097. BOOST_FOREACH(ConfigPair param, client_config->mapValue()) {
  1098. ParserPtr parser(createConfigParser(param.first));
  1099. parser->build(param.second);
  1100. parser->commit();
  1101. }
  1102. bool enable_updates = boolean_values_->getParam("enable-updates");
  1103. if (!enable_updates && (client_config->mapValue().size() == 1)) {
  1104. // If enable-updates is the only parameter and it is false then
  1105. // we're done. This allows for an abbreviated configuration entry
  1106. // that only contains that flag. Use the default D2ClientConfig
  1107. // constructor to a create a disabled instance.
  1108. local_client_config_.reset(new D2ClientConfig());
  1109. return;
  1110. }
  1111. // Get all parameters that are needed to create the D2ClientConfig.
  1112. asiolink::IOAddress server_ip(string_values_->getParam("server-ip"));
  1113. uint32_t server_port = uint32_values_->getParam("server-port");
  1114. dhcp_ddns::NameChangeProtocol
  1115. ncr_protocol = dhcp_ddns:: stringToNcrProtocol(string_values_->
  1116. getParam("ncr-protocol"));
  1117. dhcp_ddns::NameChangeFormat
  1118. ncr_format = dhcp_ddns::stringToNcrFormat(string_values_->
  1119. getParam("ncr-format"));
  1120. std::string generated_prefix = string_values_->getParam("generated-prefix");
  1121. std::string qualifying_suffix = string_values_->
  1122. getParam("qualifying-suffix");
  1123. bool always_include_fqdn = boolean_values_->getParam("always-include-fqdn");
  1124. bool override_no_update = boolean_values_->getParam("override-no-update");
  1125. bool override_client_update = boolean_values_->
  1126. getParam("override-client-update");
  1127. bool replace_client_name = boolean_values_->getParam("replace-client-name");
  1128. // Attempt to create the new client config.
  1129. local_client_config_.reset(new D2ClientConfig(enable_updates, server_ip,
  1130. server_port, ncr_protocol,
  1131. ncr_format,
  1132. always_include_fqdn,
  1133. override_no_update,
  1134. override_client_update,
  1135. replace_client_name,
  1136. generated_prefix,
  1137. qualifying_suffix));
  1138. }
  1139. isc::dhcp::ParserPtr
  1140. D2ClientConfigParser::createConfigParser(const std::string& config_id) {
  1141. DhcpConfigParser* parser = NULL;
  1142. if (config_id.compare("server-port") == 0) {
  1143. parser = new Uint32Parser(config_id, uint32_values_);
  1144. } else if ((config_id.compare("server-ip") == 0) ||
  1145. (config_id.compare("ncr-protocol") == 0) ||
  1146. (config_id.compare("ncr-format") == 0) ||
  1147. (config_id.compare("generated-prefix") == 0) ||
  1148. (config_id.compare("qualifying-suffix") == 0)) {
  1149. parser = new StringParser(config_id, string_values_);
  1150. } else if ((config_id.compare("enable-updates") == 0) ||
  1151. (config_id.compare("always-include-fqdn") == 0) ||
  1152. (config_id.compare("allow-client-update") == 0) ||
  1153. (config_id.compare("override-no-update") == 0) ||
  1154. (config_id.compare("override-client-update") == 0) ||
  1155. (config_id.compare("replace-client-name") == 0)) {
  1156. parser = new BooleanParser(config_id, boolean_values_);
  1157. } else {
  1158. isc_throw(NotImplemented,
  1159. "parser error: D2ClientConfig parameter not supported: "
  1160. << config_id);
  1161. }
  1162. return (isc::dhcp::ParserPtr(parser));
  1163. }
  1164. void
  1165. D2ClientConfigParser::commit() {
  1166. // @todo if local_client_config_ is empty then shutdown the listener...
  1167. // @todo Should this also attempt to start a listener?
  1168. // In keeping with Interface, Subnet, and Hooks parsers, then this
  1169. // should initialize the listener. Failure to init it, should cause
  1170. // rollback. This gets sticky, because who owns the listener instance?
  1171. // Does CfgMgr maintain it or does the server class? If the latter
  1172. // how do we get that value here?
  1173. // I'm thinkikng D2ClientConfig could contain the listener instance
  1174. CfgMgr::instance().setD2ClientConfig(local_client_config_);
  1175. }
  1176. }; // namespace dhcp
  1177. }; // namespace isc