d2_config.cc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  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 <d2/d2_log.h>
  15. #include <d2/d2_cfg_mgr.h>
  16. #include <dhcpsrv/dhcp_parsers.h>
  17. #include <exceptions/exceptions.h>
  18. #include <asiolink/io_error.h>
  19. #include <boost/foreach.hpp>
  20. #include <boost/lexical_cast.hpp>
  21. #include <boost/algorithm/string/predicate.hpp>
  22. #include <sstream>
  23. #include <string>
  24. namespace isc {
  25. namespace d2 {
  26. // *********************** D2Params *************************
  27. const char *D2Params::DFT_IP_ADDRESS = "127.0.0.1";
  28. const size_t D2Params::DFT_PORT = 53001;
  29. const size_t D2Params::DFT_DNS_SERVER_TIMEOUT = 100;
  30. const char *D2Params::DFT_NCR_PROTOCOL = "UDP";
  31. const char *D2Params::DFT_NCR_FORMAT = "JSON";
  32. D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
  33. const size_t port,
  34. const size_t dns_server_timeout,
  35. const dhcp_ddns::NameChangeProtocol& ncr_protocol,
  36. const dhcp_ddns::NameChangeFormat& ncr_format)
  37. : ip_address_(ip_address),
  38. port_(port),
  39. dns_server_timeout_(dns_server_timeout),
  40. ncr_protocol_(ncr_protocol),
  41. ncr_format_(ncr_format) {
  42. validateContents();
  43. }
  44. D2Params::D2Params()
  45. : ip_address_(isc::asiolink::IOAddress(DFT_IP_ADDRESS)),
  46. port_(DFT_PORT),
  47. dns_server_timeout_(DFT_DNS_SERVER_TIMEOUT),
  48. ncr_protocol_(dhcp_ddns::NCR_UDP),
  49. ncr_format_(dhcp_ddns::FMT_JSON) {
  50. validateContents();
  51. }
  52. D2Params::~D2Params(){};
  53. void
  54. D2Params::validateContents() {
  55. if ((ip_address_.toText() == "0.0.0.0") || (ip_address_.toText() == "::")) {
  56. isc_throw(D2CfgError,
  57. "D2Params: IP address cannot be \"" << ip_address_ << "\"");
  58. }
  59. if (port_ == 0) {
  60. isc_throw(D2CfgError, "D2Params: port cannot be 0");
  61. }
  62. if (dns_server_timeout_ < 1) {
  63. isc_throw(D2CfgError,
  64. "D2Params: DNS server timeout must be larger than 0");
  65. }
  66. if (ncr_format_ != dhcp_ddns::FMT_JSON) {
  67. isc_throw(D2CfgError, "D2Params: NCR Format:"
  68. << dhcp_ddns::ncrFormatToString(ncr_format_)
  69. << " is not yet supported");
  70. }
  71. if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
  72. isc_throw(D2CfgError, "D2Params: NCR Protocol:"
  73. << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
  74. << " is not yet supported");
  75. }
  76. }
  77. std::string
  78. D2Params::getConfigSummary() const {
  79. std::ostringstream s;
  80. s << "listening on " << getIpAddress() << ", port " << getPort()
  81. << ", using " << ncrProtocolToString(ncr_protocol_);
  82. return (s.str());
  83. }
  84. bool
  85. D2Params::operator == (const D2Params& other) const {
  86. return ((ip_address_ == other.ip_address_) &&
  87. (port_ == other.port_) &&
  88. (dns_server_timeout_ == other.dns_server_timeout_) &&
  89. (ncr_protocol_ == other.ncr_protocol_) &&
  90. (ncr_format_ == other.ncr_format_));
  91. }
  92. bool
  93. D2Params::operator != (const D2Params& other) const {
  94. return (!(*this == other));
  95. }
  96. std::string
  97. D2Params::toText() const {
  98. std::ostringstream stream;
  99. stream << ", ip_address: " << ip_address_.toText()
  100. << ", port: " << port_
  101. << ", dns_server_timeout_: " << dns_server_timeout_
  102. << ", ncr_protocol: "
  103. << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
  104. << ", ncr_format: " << ncr_format_
  105. << dhcp_ddns::ncrFormatToString(ncr_format_);
  106. return (stream.str());
  107. }
  108. std::ostream&
  109. operator<<(std::ostream& os, const D2Params& config) {
  110. os << config.toText();
  111. return (os);
  112. }
  113. // *********************** TSIGKeyInfo *************************
  114. // Note these values match correpsonding values for Bind9's
  115. // dnssec-keygen
  116. const char* TSIGKeyInfo::HMAC_MD5_STR = "HMAC-MD5";
  117. const char* TSIGKeyInfo::HMAC_SHA1_STR = "HMAC-SHA1";
  118. const char* TSIGKeyInfo::HMAC_SHA224_STR = "HMAC-SHA224";
  119. const char* TSIGKeyInfo::HMAC_SHA256_STR = "HMAC-SHA256";
  120. const char* TSIGKeyInfo::HMAC_SHA384_STR = "HMAC-SHA384";
  121. const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512";
  122. TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
  123. const std::string& secret)
  124. :name_(name), algorithm_(algorithm), secret_(secret), tsig_key_() {
  125. remakeKey();
  126. }
  127. TSIGKeyInfo::~TSIGKeyInfo() {
  128. }
  129. const dns::Name&
  130. TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) {
  131. if (boost::iequals(algorithm_id, HMAC_MD5_STR)) {
  132. return (dns::TSIGKey::HMACMD5_NAME());
  133. } else if (boost::iequals(algorithm_id, HMAC_SHA1_STR)) {
  134. return (dns::TSIGKey::HMACSHA1_NAME());
  135. } else if (boost::iequals(algorithm_id, HMAC_SHA224_STR)) {
  136. return (dns::TSIGKey::HMACSHA224_NAME());
  137. } else if (boost::iequals(algorithm_id, HMAC_SHA256_STR)) {
  138. return (dns::TSIGKey::HMACSHA256_NAME());
  139. } else if (boost::iequals(algorithm_id, HMAC_SHA384_STR)) {
  140. return (dns::TSIGKey::HMACSHA384_NAME());
  141. } else if (boost::iequals(algorithm_id, HMAC_SHA512_STR)) {
  142. return (dns::TSIGKey::HMACSHA512_NAME());
  143. }
  144. isc_throw(BadValue, "Unknown TSIG Key algorithm: " << algorithm_id);
  145. }
  146. void
  147. TSIGKeyInfo::remakeKey() {
  148. try {
  149. // Since our secret value is base64 encoded already, we need to
  150. // build the input string for the appropriate TSIGKey constructor.
  151. // If secret isn't a valid base64 value, the constructor will throw.
  152. std::ostringstream stream;
  153. stream << dns::Name(name_).toText() << ":"
  154. << secret_ << ":"
  155. << stringToAlgorithmName(algorithm_);
  156. tsig_key_.reset(new dns::TSIGKey(stream.str()));
  157. } catch (const std::exception& ex) {
  158. isc_throw(D2CfgError, "Cannot make TSIGKey: " << ex.what());
  159. }
  160. }
  161. // *********************** DnsServerInfo *************************
  162. const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";
  163. DnsServerInfo::DnsServerInfo(const std::string& hostname,
  164. isc::asiolink::IOAddress ip_address, uint32_t port,
  165. bool enabled)
  166. :hostname_(hostname), ip_address_(ip_address), port_(port),
  167. enabled_(enabled) {
  168. }
  169. DnsServerInfo::~DnsServerInfo() {
  170. }
  171. std::string
  172. DnsServerInfo::toText() const {
  173. std::ostringstream stream;
  174. stream << (getIpAddress().toText()) << " port:" << getPort();
  175. return (stream.str());
  176. }
  177. std::ostream&
  178. operator<<(std::ostream& os, const DnsServerInfo& server) {
  179. os << server.toText();
  180. return (os);
  181. }
  182. // *********************** DdnsDomain *************************
  183. DdnsDomain::DdnsDomain(const std::string& name,
  184. DnsServerInfoStoragePtr servers,
  185. const TSIGKeyInfoPtr& tsig_key_info)
  186. : name_(name), servers_(servers),
  187. tsig_key_info_(tsig_key_info) {
  188. }
  189. DdnsDomain::~DdnsDomain() {
  190. }
  191. const std::string
  192. DdnsDomain::getKeyName() const {
  193. if (tsig_key_info_) {
  194. return (tsig_key_info_->getName());
  195. }
  196. return ("");
  197. }
  198. // *********************** DdnsDomainLstMgr *************************
  199. const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
  200. DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
  201. domains_(new DdnsDomainMap()) {
  202. }
  203. DdnsDomainListMgr::~DdnsDomainListMgr () {
  204. }
  205. void
  206. DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
  207. if (!domains) {
  208. isc_throw(D2CfgError,
  209. "DdnsDomainListMgr::setDomains: Domain list may not be null");
  210. }
  211. domains_ = domains;
  212. // Look for the wild card domain. If present, set the member variable
  213. // to remember it. This saves us from having to look for it every time
  214. // we attempt a match.
  215. DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
  216. if (gotit != domains_->end()) {
  217. wildcard_domain_ = gotit->second;
  218. }
  219. }
  220. bool
  221. DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
  222. // First check the case of one domain to rule them all.
  223. if ((size() == 1) && (wildcard_domain_)) {
  224. domain = wildcard_domain_;
  225. return (true);
  226. }
  227. // Iterate over the domain map looking for the domain which matches
  228. // the longest portion of the given fqdn.
  229. size_t req_len = fqdn.size();
  230. size_t match_len = 0;
  231. DdnsDomainMapPair map_pair;
  232. DdnsDomainPtr best_match;
  233. BOOST_FOREACH (map_pair, *domains_) {
  234. std::string domain_name = map_pair.first;
  235. size_t dom_len = domain_name.size();
  236. // If the domain name is longer than the fqdn, then it cant be match.
  237. if (req_len < dom_len) {
  238. continue;
  239. }
  240. // If the lengths are identical and the names match we're done.
  241. if (req_len == dom_len) {
  242. if (boost::iequals(fqdn, domain_name)) {
  243. // exact match, done
  244. domain = map_pair.second;
  245. return (true);
  246. }
  247. } else {
  248. // The fqdn is longer than the domain name. Adjust the start
  249. // point of comparison by the excess in length. Only do the
  250. // comparison if the adjustment lands on a boundary. This
  251. // prevents "onetwo.net" from matching "two.net".
  252. size_t offset = req_len - dom_len;
  253. if ((fqdn[offset - 1] == '.') &&
  254. (boost::iequals(fqdn.substr(offset), domain_name))) {
  255. // Fqdn contains domain name, keep it if its better than
  256. // any we have matched so far.
  257. if (dom_len > match_len) {
  258. match_len = dom_len;
  259. best_match = map_pair.second;
  260. }
  261. }
  262. }
  263. }
  264. if (!best_match) {
  265. // There's no match. If they specified a wild card domain use it
  266. // otherwise there's no domain for this entry.
  267. if (wildcard_domain_) {
  268. domain = wildcard_domain_;
  269. return (true);
  270. }
  271. LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
  272. return (false);
  273. }
  274. domain = best_match;
  275. return (true);
  276. }
  277. // *************************** PARSERS ***********************************
  278. // *********************** TSIGKeyInfoParser *************************
  279. TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
  280. TSIGKeyInfoMapPtr keys)
  281. : entry_name_(entry_name), keys_(keys), local_scalars_() {
  282. if (!keys_) {
  283. isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
  284. " key storage cannot be null");
  285. }
  286. }
  287. TSIGKeyInfoParser::~TSIGKeyInfoParser() {
  288. }
  289. void
  290. TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
  291. isc::dhcp::ConfigPair config_pair;
  292. // For each element in the key configuration:
  293. // 1. Create a parser for the element.
  294. // 2. Invoke the parser's build method passing in the element's
  295. // configuration.
  296. // 3. Invoke the parser's commit method to store the element's parsed
  297. // data to the parser's local storage.
  298. BOOST_FOREACH (config_pair, key_config->mapValue()) {
  299. isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
  300. config_pair.second->
  301. getPosition()));
  302. parser->build(config_pair.second);
  303. parser->commit();
  304. }
  305. std::string name;
  306. std::string algorithm;
  307. std::string secret;
  308. std::map<std::string, isc::data::Element::Position> pos;
  309. // Fetch the key's parsed scalar values from parser's local storage.
  310. // All are required, if any are missing we'll throw.
  311. try {
  312. pos["name"] = local_scalars_.getParam("name", name);
  313. pos["algorithm"] = local_scalars_.getParam("algorithm", algorithm);
  314. pos["secret"] = local_scalars_.getParam("secret", secret);
  315. } catch (const std::exception& ex) {
  316. isc_throw(D2CfgError, "TSIG Key incomplete : " << ex.what()
  317. << " (" << key_config->getPosition() << ")");
  318. }
  319. // Name cannot be blank.
  320. if (name.empty()) {
  321. isc_throw(D2CfgError, "TSIG key must specify name (" << pos["name"] << ")");
  322. }
  323. // Currently, the premise is that key storage is always empty prior to
  324. // parsing so we are always adding keys never replacing them. Duplicates
  325. // are not allowed and should be flagged as a configuration error.
  326. if (keys_->find(name) != keys_->end()) {
  327. isc_throw(D2CfgError, "Duplicate TSIG key name specified : " << name
  328. << " (" << pos["name"] << ")");
  329. }
  330. // Algorithm must be valid.
  331. try {
  332. TSIGKeyInfo::stringToAlgorithmName(algorithm);
  333. } catch (const std::exception& ex) {
  334. isc_throw(D2CfgError, "TSIG key : " << ex.what() << " (" << pos["algorithm"] << ")");
  335. }
  336. // Secret cannot be blank.
  337. // Cryptolink lib doesn't offer any way to validate these. As long as it
  338. // isn't blank we'll accept it. If the content is bad, the call to in
  339. // TSIGKeyInfo::remakeKey() made in the TSIGKeyInfo ctor will throw.
  340. // We'll deal with that below.
  341. if (secret.empty()) {
  342. isc_throw(D2CfgError, "TSIG key must specify secret (" << pos["secret"] << ")");
  343. }
  344. // Everything should be valid, so create the key instance.
  345. // It is possible for the asiodns::dns::TSIGKey create to fail such as
  346. // with an invalid secret content.
  347. TSIGKeyInfoPtr key_info;
  348. try {
  349. key_info.reset(new TSIGKeyInfo(name, algorithm, secret));
  350. } catch (const std::exception& ex) {
  351. isc_throw(D2CfgError, ex.what() << " (" << key_config->getPosition() << ")");
  352. }
  353. // Add the new TSIGKeyInfo to the key storage.
  354. (*keys_)[name]=key_info;
  355. }
  356. isc::dhcp::ParserPtr
  357. TSIGKeyInfoParser::createConfigParser(const std::string& config_id,
  358. const isc::data::Element::Position& pos) {
  359. DhcpConfigParser* parser = NULL;
  360. // Based on the configuration id of the element, create the appropriate
  361. // parser. Scalars are set to use the parser's local scalar storage.
  362. if ((config_id == "name") ||
  363. (config_id == "algorithm") ||
  364. (config_id == "secret")) {
  365. parser = new isc::dhcp::StringParser(config_id,
  366. local_scalars_.getStringStorage());
  367. } else {
  368. isc_throw(NotImplemented,
  369. "parser error: TSIGKeyInfo parameter not supported: "
  370. << config_id << " (" << pos << ")");
  371. }
  372. // Return the new parser instance.
  373. return (isc::dhcp::ParserPtr(parser));
  374. }
  375. void
  376. TSIGKeyInfoParser::commit() {
  377. }
  378. // *********************** TSIGKeyInfoListParser *************************
  379. TSIGKeyInfoListParser::TSIGKeyInfoListParser(const std::string& list_name,
  380. TSIGKeyInfoMapPtr keys)
  381. :list_name_(list_name), keys_(keys), local_keys_(new TSIGKeyInfoMap()),
  382. parsers_() {
  383. if (!keys_) {
  384. isc_throw(D2CfgError, "TSIGKeyInfoListParser ctor:"
  385. " key storage cannot be null");
  386. }
  387. }
  388. TSIGKeyInfoListParser::~TSIGKeyInfoListParser() {
  389. }
  390. void
  391. TSIGKeyInfoListParser::
  392. build(isc::data::ConstElementPtr key_list) {
  393. int i = 0;
  394. isc::data::ConstElementPtr key_config;
  395. // For each key element in the key list:
  396. // 1. Create a parser for the key element.
  397. // 2. Invoke the parser's build method passing in the key's
  398. // configuration.
  399. // 3. Add the parser to a local collection of parsers.
  400. BOOST_FOREACH(key_config, key_list->listValue()) {
  401. // Create a name for the parser based on its position in the list.
  402. std::string entry_name = boost::lexical_cast<std::string>(i++);
  403. isc::dhcp::ParserPtr parser(new TSIGKeyInfoParser(entry_name,
  404. local_keys_));
  405. parser->build(key_config);
  406. parsers_.push_back(parser);
  407. }
  408. // Now that we know we have a valid list, commit that list to the
  409. // area given to us during construction (i.e. to the d2 context).
  410. *keys_ = *local_keys_;
  411. }
  412. void
  413. TSIGKeyInfoListParser::commit() {
  414. // Invoke commit on each server parser. This will cause each one to
  415. // create it's server instance and commit it to storage.
  416. BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
  417. parser->commit();
  418. }
  419. }
  420. // *********************** DnsServerInfoParser *************************
  421. DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
  422. DnsServerInfoStoragePtr servers)
  423. : entry_name_(entry_name), servers_(servers), local_scalars_() {
  424. if (!servers_) {
  425. isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
  426. " server storage cannot be null");
  427. }
  428. }
  429. DnsServerInfoParser::~DnsServerInfoParser() {
  430. }
  431. void
  432. DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
  433. isc::dhcp::ConfigPair config_pair;
  434. // For each element in the server configuration:
  435. // 1. Create a parser for the element.
  436. // 2. Invoke the parser's build method passing in the element's
  437. // configuration.
  438. // 3. Invoke the parser's commit method to store the element's parsed
  439. // data to the parser's local storage.
  440. BOOST_FOREACH (config_pair, server_config->mapValue()) {
  441. isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
  442. config_pair.second->
  443. getPosition()));
  444. parser->build(config_pair.second);
  445. parser->commit();
  446. }
  447. std::string hostname;
  448. std::string ip_address;
  449. uint32_t port = DnsServerInfo::STANDARD_DNS_PORT;
  450. std::map<std::string, isc::data::Element::Position> pos;
  451. // Fetch the server configuration's parsed scalar values from parser's
  452. // local storage. They're all optional, so no try-catch here.
  453. pos["hostname"] = local_scalars_.getParam("hostname", hostname,
  454. DCfgContextBase::OPTIONAL);
  455. pos["ip_address"] = local_scalars_.getParam("ip_address", ip_address,
  456. DCfgContextBase::OPTIONAL);
  457. pos["port"] = local_scalars_.getParam("port", port,
  458. DCfgContextBase::OPTIONAL);
  459. // The configuration must specify one or the other.
  460. if (hostname.empty() == ip_address.empty()) {
  461. isc_throw(D2CfgError, "Dns Server must specify one or the other"
  462. " of hostname or IP address"
  463. << " (" << server_config->getPosition() << ")");
  464. }
  465. // Port cannot be zero.
  466. if (port == 0) {
  467. isc_throw(D2CfgError, "Dns Server : port cannot be 0"
  468. << " (" << pos["port"] << ")");
  469. }
  470. DnsServerInfoPtr serverInfo;
  471. if (!hostname.empty()) {
  472. /// @todo when resolvable hostname is supported we create the entry
  473. /// as follows:
  474. ///
  475. /// @code
  476. /// // When hostname is specified, create a valid, blank IOAddress
  477. /// // and then create the DnsServerInfo.
  478. /// isc::asiolink::IOAddress io_addr(DnsServerInfo::EMPTY_IP_STR);
  479. /// serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
  480. ///
  481. /// @endcode
  482. ///
  483. /// Resolution will be done prior to connection during transaction
  484. /// processing.
  485. /// Until then we'll throw unsupported.
  486. isc_throw(D2CfgError, "Dns Server : hostname is not yet supported"
  487. << " (" << pos["hostname"] << ")");
  488. } else {
  489. try {
  490. // Create an IOAddress from the IP address string given and then
  491. // create the DnsServerInfo.
  492. isc::asiolink::IOAddress io_addr(ip_address);
  493. serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
  494. } catch (const isc::asiolink::IOError& ex) {
  495. isc_throw(D2CfgError, "Dns Server : invalid IP address : "
  496. << ip_address << " (" << pos["ip_address"] << ")");
  497. }
  498. }
  499. // Add the new DnsServerInfo to the server storage.
  500. servers_->push_back(serverInfo);
  501. }
  502. isc::dhcp::ParserPtr
  503. DnsServerInfoParser::createConfigParser(const std::string& config_id,
  504. const isc::data::Element::
  505. Position& pos) {
  506. DhcpConfigParser* parser = NULL;
  507. // Based on the configuration id of the element, create the appropriate
  508. // parser. Scalars are set to use the parser's local scalar storage.
  509. if ((config_id == "hostname") ||
  510. (config_id == "ip_address")) {
  511. parser = new isc::dhcp::StringParser(config_id,
  512. local_scalars_.getStringStorage());
  513. } else if (config_id == "port") {
  514. parser = new isc::dhcp::Uint32Parser(config_id,
  515. local_scalars_.getUint32Storage());
  516. } else {
  517. isc_throw(NotImplemented,
  518. "parser error: DnsServerInfo parameter not supported: "
  519. << config_id << " (" << pos << ")");
  520. }
  521. // Return the new parser instance.
  522. return (isc::dhcp::ParserPtr(parser));
  523. }
  524. void
  525. DnsServerInfoParser::commit() {
  526. }
  527. // *********************** DnsServerInfoListParser *************************
  528. DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
  529. DnsServerInfoStoragePtr servers)
  530. :list_name_(list_name), servers_(servers), parsers_() {
  531. if (!servers_) {
  532. isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
  533. " server storage cannot be null");
  534. }
  535. }
  536. DnsServerInfoListParser::~DnsServerInfoListParser(){
  537. }
  538. void
  539. DnsServerInfoListParser::
  540. build(isc::data::ConstElementPtr server_list){
  541. int i = 0;
  542. isc::data::ConstElementPtr server_config;
  543. // For each server element in the server list:
  544. // 1. Create a parser for the server element.
  545. // 2. Invoke the parser's build method passing in the server's
  546. // configuration.
  547. // 3. Add the parser to a local collection of parsers.
  548. BOOST_FOREACH(server_config, server_list->listValue()) {
  549. // Create a name for the parser based on its position in the list.
  550. std::string entry_name = boost::lexical_cast<std::string>(i++);
  551. isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
  552. servers_));
  553. parser->build(server_config);
  554. parsers_.push_back(parser);
  555. }
  556. // Domains must have at least one server.
  557. if (parsers_.size() == 0) {
  558. isc_throw (D2CfgError, "Server List must contain at least one server"
  559. << " (" << server_list->getPosition() << ")");
  560. }
  561. }
  562. void
  563. DnsServerInfoListParser::commit() {
  564. // Invoke commit on each server parser.
  565. BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
  566. parser->commit();
  567. }
  568. }
  569. // *********************** DdnsDomainParser *************************
  570. DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
  571. DdnsDomainMapPtr domains,
  572. TSIGKeyInfoMapPtr keys)
  573. : entry_name_(entry_name), domains_(domains), keys_(keys),
  574. local_servers_(new DnsServerInfoStorage()), local_scalars_() {
  575. if (!domains_) {
  576. isc_throw(D2CfgError,
  577. "DdnsDomainParser ctor, domain storage cannot be null");
  578. }
  579. }
  580. DdnsDomainParser::~DdnsDomainParser() {
  581. }
  582. void
  583. DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
  584. // For each element in the domain configuration:
  585. // 1. Create a parser for the element.
  586. // 2. Invoke the parser's build method passing in the element's
  587. // configuration.
  588. // 3. Invoke the parser's commit method to store the element's parsed
  589. // data to the parser's local storage.
  590. isc::dhcp::ConfigPair config_pair;
  591. BOOST_FOREACH(config_pair, domain_config->mapValue()) {
  592. isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
  593. config_pair.second->
  594. getPosition()));
  595. parser->build(config_pair.second);
  596. parser->commit();
  597. }
  598. // Now construct the domain.
  599. std::string name;
  600. std::string key_name;
  601. std::map<std::string, isc::data::Element::Position> pos;
  602. // Fetch the parsed scalar values from parser's local storage.
  603. // Any required that are missing will throw.
  604. try {
  605. pos["name"] = local_scalars_.getParam("name", name);
  606. pos["key_name"] = local_scalars_.getParam("key_name", key_name,
  607. DCfgContextBase::OPTIONAL);
  608. } catch (const std::exception& ex) {
  609. isc_throw(D2CfgError, "DdnsDomain incomplete : " << ex.what()
  610. << " (" << domain_config->getPosition() << ")");
  611. }
  612. // Blank domain names are not allowed.
  613. if (name.empty()) {
  614. isc_throw(D2CfgError, "DndsDomain : name cannot be blank ("
  615. << pos["name"] << ")");
  616. }
  617. // Currently, the premise is that domain storage is always empty
  618. // prior to parsing so always adding domains never replacing them.
  619. // Duplicates are not allowed and should be flagged as a configuration
  620. // error.
  621. if (domains_->find(name) != domains_->end()) {
  622. isc_throw(D2CfgError, "Duplicate domain specified:" << name
  623. << " (" << pos["name"] << ")");
  624. }
  625. // Key name is optional. If it is not blank, then find the key in the
  626. /// list of defined keys.
  627. TSIGKeyInfoPtr tsig_key_info;
  628. if (!key_name.empty()) {
  629. if (keys_) {
  630. TSIGKeyInfoMap::iterator kit = keys_->find(key_name);
  631. if (kit != keys_->end()) {
  632. tsig_key_info = kit->second;
  633. }
  634. }
  635. if (!tsig_key_info) {
  636. isc_throw(D2CfgError, "DdnsDomain : " << name
  637. << " specifies an undefined key: " << key_name
  638. << " (" << pos["key_name"] << ")");
  639. }
  640. }
  641. // Instantiate the new domain and add it to domain storage.
  642. DdnsDomainPtr domain(new DdnsDomain(name, local_servers_, tsig_key_info));
  643. // Add the new domain to the domain storage.
  644. (*domains_)[name] = domain;
  645. }
  646. isc::dhcp::ParserPtr
  647. DdnsDomainParser::createConfigParser(const std::string& config_id,
  648. const isc::data::Element::Position& pos) {
  649. DhcpConfigParser* parser = NULL;
  650. // Based on the configuration id of the element, create the appropriate
  651. // parser. Scalars are set to use the parser's local scalar storage.
  652. if ((config_id == "name") ||
  653. (config_id == "key_name")) {
  654. parser = new isc::dhcp::StringParser(config_id,
  655. local_scalars_.getStringStorage());
  656. } else if (config_id == "dns_servers") {
  657. // Server list parser is given in our local server storage. It will pass
  658. // this down to its server parsers and is where they will write their
  659. // server instances upon commit.
  660. parser = new DnsServerInfoListParser(config_id, local_servers_);
  661. } else {
  662. isc_throw(NotImplemented,
  663. "parser error: DdnsDomain parameter not supported: "
  664. << config_id << " (" << pos << ")");
  665. }
  666. // Return the new domain parser instance.
  667. return (isc::dhcp::ParserPtr(parser));
  668. }
  669. void
  670. DdnsDomainParser::commit() {
  671. }
  672. // *********************** DdnsDomainListParser *************************
  673. DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
  674. DdnsDomainMapPtr domains,
  675. TSIGKeyInfoMapPtr keys)
  676. :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
  677. if (!domains_) {
  678. isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
  679. " domain storage cannot be null");
  680. }
  681. }
  682. DdnsDomainListParser::~DdnsDomainListParser(){
  683. }
  684. void
  685. DdnsDomainListParser::
  686. build(isc::data::ConstElementPtr domain_list){
  687. // For each domain element in the domain list:
  688. // 1. Create a parser for the domain element.
  689. // 2. Invoke the parser's build method passing in the domain's
  690. // configuration.
  691. // 3. Add the parser to the local collection of parsers.
  692. int i = 0;
  693. isc::data::ConstElementPtr domain_config;
  694. BOOST_FOREACH(domain_config, domain_list->listValue()) {
  695. std::string entry_name = boost::lexical_cast<std::string>(i++);
  696. isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
  697. domains_, keys_));
  698. parser->build(domain_config);
  699. parsers_.push_back(parser);
  700. }
  701. }
  702. void
  703. DdnsDomainListParser::commit() {
  704. // Invoke commit on each server parser. This will cause each one to
  705. // create it's server instance and commit it to storage.
  706. BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
  707. parser->commit();
  708. }
  709. }
  710. // *********************** DdnsDomainListMgrParser *************************
  711. DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
  712. DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
  713. : entry_name_(entry_name), mgr_(mgr), keys_(keys),
  714. local_domains_(new DdnsDomainMap()), local_scalars_() {
  715. }
  716. DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
  717. }
  718. void
  719. DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
  720. // For each element in the domain manager configuration:
  721. // 1. Create a parser for the element.
  722. // 2. Invoke the parser's build method passing in the element's
  723. // configuration.
  724. // 3. Invoke the parser's commit method to store the element's parsed
  725. // data to the parser's local storage.
  726. isc::dhcp::ConfigPair config_pair;
  727. BOOST_FOREACH(config_pair, domain_config->mapValue()) {
  728. isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
  729. config_pair.second->
  730. getPosition()));
  731. parser->build(config_pair.second);
  732. parser->commit();
  733. }
  734. // Add the new domain to the domain storage.
  735. mgr_->setDomains(local_domains_);
  736. }
  737. isc::dhcp::ParserPtr
  738. DdnsDomainListMgrParser::createConfigParser(const std::string& config_id,
  739. const isc::data::Element::
  740. Position& pos) {
  741. DhcpConfigParser* parser = NULL;
  742. if (config_id == "ddns_domains") {
  743. // Domain list parser is given our local domain storage. It will pass
  744. // this down to its domain parsers and is where they will write their
  745. // domain instances upon commit.
  746. parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
  747. } else {
  748. isc_throw(NotImplemented, "parser error: "
  749. "DdnsDomainListMgr parameter not supported: " << config_id
  750. << " (" << pos << ")");
  751. }
  752. // Return the new domain parser instance.
  753. return (isc::dhcp::ParserPtr(parser));
  754. }
  755. void
  756. DdnsDomainListMgrParser::commit() {
  757. }
  758. }; // end of isc::dhcp namespace
  759. }; // end of isc namespace