d2_config.cc 33 KB

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