d2_config.cc 34 KB

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