d2_cfg_mgr.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <d2/d2_log.h>
  8. #include <d2/d2_cfg_mgr.h>
  9. #include <d2/d2_simple_parser.h>
  10. #include <util/encode/hex.h>
  11. #include <boost/foreach.hpp>
  12. using namespace isc::process;
  13. namespace isc {
  14. namespace d2 {
  15. namespace {
  16. typedef std::vector<uint8_t> ByteAddress;
  17. } // end of unnamed namespace
  18. // *********************** D2CfgContext *************************
  19. D2CfgContext::D2CfgContext()
  20. : d2_params_(new D2Params()),
  21. forward_mgr_(new DdnsDomainListMgr("forward-ddns")),
  22. reverse_mgr_(new DdnsDomainListMgr("reverse-ddns")),
  23. keys_(new TSIGKeyInfoMap()) {
  24. }
  25. D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
  26. d2_params_ = rhs.d2_params_;
  27. if (rhs.forward_mgr_) {
  28. forward_mgr_.reset(new DdnsDomainListMgr(rhs.forward_mgr_->getName()));
  29. forward_mgr_->setDomains(rhs.forward_mgr_->getDomains());
  30. }
  31. if (rhs.reverse_mgr_) {
  32. reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName()));
  33. reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
  34. }
  35. keys_ = rhs.keys_;
  36. }
  37. D2CfgContext::~D2CfgContext() {
  38. }
  39. // *********************** D2CfgMgr *************************
  40. const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa.";
  41. const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa.";
  42. D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
  43. // TSIG keys need to parse before the Domains, so we can catch Domains
  44. // that specify undefined keys. Create the necessary parsing order now.
  45. addToParseOrder("tsig-keys");
  46. addToParseOrder("forward-ddns");
  47. addToParseOrder("reverse-ddns");
  48. }
  49. D2CfgMgr::~D2CfgMgr() {
  50. }
  51. DCfgContextBasePtr
  52. D2CfgMgr::createNewContext() {
  53. return (DCfgContextBasePtr(new D2CfgContext()));
  54. }
  55. bool
  56. D2CfgMgr::forwardUpdatesEnabled() {
  57. // Forward updates are not enabled if no forward servers are defined.
  58. return (getD2CfgContext()->getForwardMgr()->size() > 0);
  59. }
  60. bool
  61. D2CfgMgr::reverseUpdatesEnabled() {
  62. // Reverse updates are not enabled if no reverse servers are defined.
  63. return (getD2CfgContext()->getReverseMgr()->size() > 0);
  64. }
  65. bool
  66. D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
  67. if (fqdn.empty()) {
  68. // This is a programmatic error and should not happen.
  69. isc_throw(D2CfgError, "matchForward passed an empty fqdn");
  70. }
  71. // Fetch the forward manager from the D2 context.
  72. DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr();
  73. // Call the manager's match method and return the result.
  74. return (mgr->matchDomain(fqdn, domain));
  75. }
  76. bool
  77. D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) {
  78. // Note, reverseIpAddress will throw if the ip_address is invalid.
  79. std::string reverse_address = reverseIpAddress(ip_address);
  80. // Fetch the reverse manager from the D2 context.
  81. DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
  82. return (mgr->matchDomain(reverse_address, domain));
  83. }
  84. std::string
  85. D2CfgMgr::reverseIpAddress(const std::string& address) {
  86. try {
  87. // Convert string address into an IOAddress and invoke the
  88. // appropriate reverse method.
  89. isc::asiolink::IOAddress ioaddr(address);
  90. if (ioaddr.isV4()) {
  91. return (reverseV4Address(ioaddr));
  92. }
  93. return (reverseV6Address(ioaddr));
  94. } catch (const isc::Exception& ex) {
  95. isc_throw(D2CfgError, "D2CfgMgr cannot reverse address: "
  96. << address << " : " << ex.what());
  97. }
  98. }
  99. std::string
  100. D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) {
  101. if (!ioaddr.isV4()) {
  102. isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :"
  103. << ioaddr);
  104. }
  105. // Get the address in byte vector form.
  106. const ByteAddress bytes = ioaddr.toBytes();
  107. // Walk backwards through vector outputting each octet and a dot.
  108. std::ostringstream stream;
  109. // We have to set the following variable to get
  110. // const_reverse_iterator type of rend(), otherwise Solaris GCC
  111. // complains on operator!= by trying to use the non-const variant.
  112. const ByteAddress::const_reverse_iterator end = bytes.rend();
  113. for (ByteAddress::const_reverse_iterator rit = bytes.rbegin();
  114. rit != end;
  115. ++rit)
  116. {
  117. stream << static_cast<unsigned int>(*rit) << ".";
  118. }
  119. // Tack on the suffix and we're done.
  120. stream << IPV4_REV_ZONE_SUFFIX;
  121. return(stream.str());
  122. }
  123. std::string
  124. D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) {
  125. if (!ioaddr.isV6()) {
  126. isc_throw(D2CfgError, "D2Cfg address is not IPv6 address: " << ioaddr);
  127. }
  128. // Turn the address into a string of digits.
  129. const ByteAddress bytes = ioaddr.toBytes();
  130. const std::string digits = isc::util::encode::encodeHex(bytes);
  131. // Walk backwards through string outputting each digits and a dot.
  132. std::ostringstream stream;
  133. // We have to set the following variable to get
  134. // const_reverse_iterator type of rend(), otherwise Solaris GCC
  135. // complains on operator!= by trying to use the non-const variant.
  136. const std::string::const_reverse_iterator end = digits.rend();
  137. for (std::string::const_reverse_iterator rit = digits.rbegin();
  138. rit != end;
  139. ++rit)
  140. {
  141. stream << static_cast<char>(*rit) << ".";
  142. }
  143. // Tack on the suffix and we're done.
  144. stream << IPV6_REV_ZONE_SUFFIX;
  145. return(stream.str());
  146. }
  147. const D2ParamsPtr&
  148. D2CfgMgr::getD2Params() {
  149. return (getD2CfgContext()->getD2Params());
  150. }
  151. std::string
  152. D2CfgMgr::getConfigSummary(const uint32_t) {
  153. return (getD2Params()->getConfigSummary());
  154. }
  155. namespace {
  156. template <typename int_type> int_type
  157. getInt(const std::string& name, isc::data::ConstElementPtr value) {
  158. int64_t val_int = value->intValue();
  159. if ((val_int < std::numeric_limits<int_type>::min()) ||
  160. (val_int > std::numeric_limits<int_type>::max())) {
  161. isc_throw(D2CfgError, "out of range value (" << val_int
  162. << ") specified for parameter '" << name
  163. << "' (" << value->getPosition() << ")");
  164. }
  165. return (static_cast<int_type>(val_int));
  166. }
  167. isc::asiolink::IOAddress
  168. getIOAddress(const std::string& name, isc::data::ConstElementPtr value) {
  169. std::string str = value->stringValue();
  170. try {
  171. return (isc::asiolink::IOAddress(str));
  172. } catch (const std::exception& ex) {
  173. isc_throw(D2CfgError, "invalid address (" << str
  174. << ") specified for parameter '" << name
  175. << "' (" << value->getPosition() << ")");
  176. }
  177. }
  178. dhcp_ddns::NameChangeProtocol
  179. getProtocol(const std::string& name, isc::data::ConstElementPtr value) {
  180. std::string str = value->stringValue();
  181. try {
  182. return (dhcp_ddns::stringToNcrProtocol(str));
  183. } catch (const std::exception& ex) {
  184. isc_throw(D2CfgError,
  185. "invalid NameChangeRequest protocol (" << str
  186. << ") specified for parameter '" << name
  187. << "' (" << value->getPosition() << ")");
  188. }
  189. }
  190. dhcp_ddns::NameChangeFormat
  191. getFormat(const std::string& name, isc::data::ConstElementPtr value) {
  192. std::string str = value->stringValue();
  193. try {
  194. return (dhcp_ddns::stringToNcrFormat(str));
  195. } catch (const std::exception& ex) {
  196. isc_throw(D2CfgError,
  197. "invalid NameChangeRequest format (" << str
  198. << ") specified for parameter '" << name
  199. << "' (" << value->getPosition() << ")");
  200. }
  201. }
  202. } // anon
  203. void
  204. D2CfgMgr::parseElement(const std::string& element_id,
  205. isc::data::ConstElementPtr element) {
  206. try {
  207. // Get D2 specific context.
  208. D2CfgContextPtr context = getD2CfgContext();
  209. if ((element_id == "ip-address") ||
  210. (element_id == "ncr-protocol") ||
  211. (element_id == "ncr-format") ||
  212. (element_id == "port") ||
  213. (element_id == "dns-server-timeout")) {
  214. // global scalar params require nothing extra be done
  215. } else if (element_id == "tsig-keys") {
  216. TSIGKeyInfoListParser parser;
  217. context->setKeys(parser.parse(element));
  218. } else if (element_id == "forward-ddns") {
  219. DdnsDomainListMgrParser parser;
  220. DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
  221. context->getKeys());
  222. context->setForwardMgr(mgr);
  223. } else if (element_id == "reverse-ddns") {
  224. DdnsDomainListMgrParser parser;
  225. DdnsDomainListMgrPtr mgr = parser.parse(element, element_id,
  226. context->getKeys());
  227. context->setReverseMgr(mgr);
  228. } else {
  229. // Shouldn't occur if the JSON parser is doing its job.
  230. isc_throw(D2CfgError, "Unsupported element: "
  231. << element_id << element->getPosition());
  232. }
  233. } catch (const D2CfgError& ex) {
  234. // Should already have a specific error and position info
  235. throw ex;
  236. } catch (const std::exception& ex) {
  237. isc_throw(D2CfgError, "element: " << element_id << " : " << ex.what()
  238. << element->getPosition());
  239. }
  240. };
  241. void
  242. D2CfgMgr::setCfgDefaults(isc::data::ElementPtr mutable_config) {
  243. D2SimpleParser::setAllDefaults(mutable_config);
  244. }
  245. void
  246. D2CfgMgr::buildParams(isc::data::ConstElementPtr params_config) {
  247. // Base class build creates parses and invokes build on each parser.
  248. // This populate the context scalar stores with all of the parameters.
  249. DCfgMgrBase::buildParams(params_config);
  250. // Fetch the parameters in the config, performing any logcial
  251. // validation required.
  252. asiolink::IOAddress ip_address(0);
  253. uint32_t port = 0;
  254. uint32_t dns_server_timeout = 0;
  255. dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP;
  256. dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON;
  257. // Assumes that params_config has had defaults added
  258. BOOST_FOREACH(isc::dhcp::ConfigPair param, params_config->mapValue()) {
  259. std::string entry(param.first);
  260. isc::data::ConstElementPtr value(param.second);
  261. try {
  262. if (entry == "ip-address") {
  263. ip_address = getIOAddress(entry, value);
  264. if ((ip_address.toText() == "0.0.0.0") ||
  265. (ip_address.toText() == "::")) {
  266. isc_throw(D2CfgError, "IP address cannot be \""
  267. << ip_address << "\""
  268. << " (" << value->getPosition() << ")");
  269. }
  270. } else if (entry == "port") {
  271. port = getInt<uint32_t>(entry, value);
  272. } else if (entry == "dns-server-timeout") {
  273. dns_server_timeout = getInt<uint32_t>(entry, value);
  274. } else if (entry == "ncr-protocol") {
  275. ncr_protocol = getProtocol(entry, value);
  276. if (ncr_protocol != dhcp_ddns::NCR_UDP) {
  277. isc_throw(D2CfgError, "ncr-protocol : "
  278. << dhcp_ddns::ncrProtocolToString(ncr_protocol)
  279. << " is not yet supported "
  280. << " (" << value->getPosition() << ")");
  281. }
  282. } else if (entry == "ncr-format") {
  283. ncr_format = getFormat(entry, value);
  284. if (ncr_format != dhcp_ddns::FMT_JSON) {
  285. isc_throw(D2CfgError, "NCR Format:"
  286. << dhcp_ddns::ncrFormatToString(ncr_format)
  287. << " is not yet supported"
  288. << " (" << value->getPosition() << ")");
  289. }
  290. } else {
  291. isc_throw(D2CfgError,
  292. "unsupported parameter '" << entry
  293. << " (" << value->getPosition() << ")");
  294. }
  295. } catch (const isc::data::TypeError&) {
  296. isc_throw(D2CfgError,
  297. "invalid value type specified for parameter '" << entry
  298. << " (" << value->getPosition() << ")");
  299. }
  300. }
  301. // Attempt to create the new client config. This ought to fly as
  302. // we already validated everything.
  303. D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout,
  304. ncr_protocol, ncr_format));
  305. getD2CfgContext()->getD2Params() = params;
  306. }
  307. }; // end of isc::dhcp namespace
  308. }; // end of isc namespace