diff options
Diffstat (limited to 'src/lib/d2srv')
-rw-r--r-- | src/lib/d2srv/.gitattributes | 2 | ||||
-rw-r--r-- | src/lib/d2srv/Makefile.am | 83 | ||||
-rw-r--r-- | src/lib/d2srv/d2_cfg_mgr.cc | 326 | ||||
-rw-r--r-- | src/lib/d2srv/d2_cfg_mgr.h | 339 | ||||
-rw-r--r-- | src/lib/d2srv/d2_config.cc | 644 | ||||
-rw-r--r-- | src/lib/d2srv/d2_config.h | 898 | ||||
-rw-r--r-- | src/lib/d2srv/d2_log.cc | 23 | ||||
-rw-r--r-- | src/lib/d2srv/d2_log.h | 25 | ||||
-rw-r--r-- | src/lib/d2srv/d2_messages.cc | 171 | ||||
-rw-r--r-- | src/lib/d2srv/d2_messages.h | 89 | ||||
-rw-r--r-- | src/lib/d2srv/d2_messages.mes | 396 | ||||
-rw-r--r-- | src/lib/d2srv/d2_simple_parser.cc | 309 | ||||
-rw-r--r-- | src/lib/d2srv/d2_simple_parser.h | 96 | ||||
-rw-r--r-- | src/lib/d2srv/tests/.gitignore | 1 | ||||
-rw-r--r-- | src/lib/d2srv/tests/Makefile.am | 36 | ||||
-rw-r--r-- | src/lib/d2srv/tests/run_unittests.cc | 20 |
16 files changed, 3458 insertions, 0 deletions
diff --git a/src/lib/d2srv/.gitattributes b/src/lib/d2srv/.gitattributes new file mode 100644 index 0000000000..784616b535 --- /dev/null +++ b/src/lib/d2srv/.gitattributes @@ -0,0 +1,2 @@ +/d2_messages.cc -diff merge=ours +/d2_messages.h -diff merge=ours diff --git a/src/lib/d2srv/Makefile.am b/src/lib/d2srv/Makefile.am new file mode 100644 index 0000000000..1ab1e8f839 --- /dev/null +++ b/src/lib/d2srv/Makefile.am @@ -0,0 +1,83 @@ +SUBDIRS = . tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +EXTRA_DIST = + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libkea-d2srv.la +libkea_d2srv_la_SOURCES = +libkea_d2srv_la_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h +libkea_d2srv_la_SOURCES += d2_config.cc d2_config.h +libkea_d2srv_la_SOURCES += d2_log.cc d2_log.h +libkea_d2srv_la_SOURCES += d2_messages.cc d2_messages.h +libkea_d2srv_la_SOURCES += d2_simple_parser.cc d2_simple_parser.h +EXTRA_DIST += d2_messages.mes + +libkea_d2srv_la_CXXFLAGS = $(AM_CXXFLAGS) +libkea_d2srv_la_CPPFLAGS = $(AM_CPPFLAGS) + +libkea_d2srv_la_LIBADD = +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/http/libkea-http.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_d2srv_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_d2srv_la_LIBADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) + +libkea_d2srv_la_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f d2_messages.h d2_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: d2_messages.h d2_messages.cc + @echo Message files regenerated + +d2_messages.h d2_messages.cc: d2_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/d2/d2_messages.mes + +else + +messages d2_messages.h d2_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + +# Specify the headers for copying into the installation directory tree. +libkea_d2srv_includedir = $(pkgincludedir)/d2srv +libkea_d2srv_include_HEADERS = + d2_cfg_mgr.h \ + d2_config.h \ + d2_log.h \ + d2_messages.h \ + d2_simple_parser.h diff --git a/src/lib/d2srv/d2_cfg_mgr.cc b/src/lib/d2srv/d2_cfg_mgr.cc new file mode 100644 index 0000000000..351b9caa67 --- /dev/null +++ b/src/lib/d2srv/d2_cfg_mgr.cc @@ -0,0 +1,326 @@ +// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2srv/d2_log.h> +#include <d2srv/d2_cfg_mgr.h> +#include <d2srv/d2_simple_parser.h> +#include <cc/command_interpreter.h> +#include <util/encode/hex.h> + +#include <boost/foreach.hpp> + +using namespace isc::asiolink; +using namespace isc::config; +using namespace isc::data; +using namespace isc::process; + +namespace isc { +namespace d2 { + +namespace { + +typedef std::vector<uint8_t> ByteAddress; + +} // end of unnamed namespace + +// *********************** D2CfgContext ************************* + +D2CfgContext::D2CfgContext() + : d2_params_(new D2Params()), + forward_mgr_(new DdnsDomainListMgr("forward-ddns")), + reverse_mgr_(new DdnsDomainListMgr("reverse-ddns")), + keys_(new TSIGKeyInfoMap()), + control_socket_(ConstElementPtr()) { +} + +D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : ConfigBase(rhs) { + d2_params_ = rhs.d2_params_; + if (rhs.forward_mgr_) { + forward_mgr_.reset(new DdnsDomainListMgr(rhs.forward_mgr_->getName())); + forward_mgr_->setDomains(rhs.forward_mgr_->getDomains()); + } + + if (rhs.reverse_mgr_) { + reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName())); + reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains()); + } + + keys_ = rhs.keys_; + + control_socket_ = rhs.control_socket_; + + hooks_config_ = rhs.hooks_config_; +} + +D2CfgContext::~D2CfgContext() { +} + +ElementPtr +D2CfgContext::toElement() const { + ElementPtr d2 = ConfigBase::toElement(); + // Set user-context + contextToElement(d2); + // Set ip-address + const IOAddress& ip_address = d2_params_->getIpAddress(); + d2->set("ip-address", Element::create(ip_address.toText())); + // Set port + size_t port = d2_params_->getPort(); + d2->set("port", Element::create(static_cast<int64_t>(port))); + // Set dns-server-timeout + size_t dns_server_timeout = d2_params_->getDnsServerTimeout(); + d2->set("dns-server-timeout", + Element::create(static_cast<int64_t>(dns_server_timeout))); + // Set ncr-protocol + const dhcp_ddns::NameChangeProtocol& ncr_protocol = + d2_params_->getNcrProtocol(); + d2->set("ncr-protocol", + Element::create(dhcp_ddns::ncrProtocolToString(ncr_protocol))); + // Set ncr-format + const dhcp_ddns::NameChangeFormat& ncr_format = d2_params_->getNcrFormat(); + d2->set("ncr-format", + Element::create(dhcp_ddns::ncrFormatToString(ncr_format))); + // Set forward-ddns + ElementPtr forward_ddns = Element::createMap(); + forward_ddns->set("ddns-domains", forward_mgr_->toElement()); + d2->set("forward-ddns", forward_ddns); + // Set reverse-ddns + ElementPtr reverse_ddns = Element::createMap(); + reverse_ddns->set("ddns-domains", reverse_mgr_->toElement()); + d2->set("reverse-ddns", reverse_ddns); + // Set tsig-keys + ElementPtr tsig_keys = Element::createList(); + for (TSIGKeyInfoMap::const_iterator key = keys_->begin(); + key != keys_->end(); ++key) { + tsig_keys->add(key->second->toElement()); + } + d2->set("tsig-keys", tsig_keys); + // Set control-socket (skip if null as empty is not legal) + if (!isNull(control_socket_)) { + d2->set("control-socket", UserContext::toElement(control_socket_)); + } + // Set hooks-libraries + d2->set("hooks-libraries", hooks_config_.toElement()); + // Set DhcpDdns + ElementPtr result = Element::createMap(); + result->set("DhcpDdns", d2); + + return (result); +} + +// *********************** D2CfgMgr ************************* + +const char* D2CfgMgr::IPV4_REV_ZONE_SUFFIX = "in-addr.arpa."; + +const char* D2CfgMgr::IPV6_REV_ZONE_SUFFIX = "ip6.arpa."; + +D2CfgMgr::D2CfgMgr() : DCfgMgrBase(ConfigPtr(new D2CfgContext())) { +} + +D2CfgMgr::~D2CfgMgr() { +} + +ConfigPtr +D2CfgMgr::createNewContext() { + return (ConfigPtr(new D2CfgContext())); +} + +bool +D2CfgMgr::forwardUpdatesEnabled() { + // Forward updates are not enabled if no forward servers are defined. + return (getD2CfgContext()->getForwardMgr()->size() > 0); +} + +bool +D2CfgMgr::reverseUpdatesEnabled() { + // Reverse updates are not enabled if no reverse servers are defined. + return (getD2CfgContext()->getReverseMgr()->size() > 0); +} + +bool +D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) { + if (fqdn.empty()) { + // This is a programmatic error and should not happen. + isc_throw(D2CfgError, "matchForward passed an empty fqdn"); + } + + // Fetch the forward manager from the D2 context. + DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr(); + + // Call the manager's match method and return the result. + return (mgr->matchDomain(fqdn, domain)); +} + +bool +D2CfgMgr::matchReverse(const std::string& ip_address, DdnsDomainPtr& domain) { + // Note, reverseIpAddress will throw if the ip_address is invalid. + std::string reverse_address = reverseIpAddress(ip_address); + + // Fetch the reverse manager from the D2 context. + DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr(); + + return (mgr->matchDomain(reverse_address, domain)); +} + +std::string +D2CfgMgr::reverseIpAddress(const std::string& address) { + try { + // Convert string address into an IOAddress and invoke the + // appropriate reverse method. + isc::asiolink::IOAddress ioaddr(address); + if (ioaddr.isV4()) { + return (reverseV4Address(ioaddr)); + } + + return (reverseV6Address(ioaddr)); + + } catch (const isc::Exception& ex) { + isc_throw(D2CfgError, "D2CfgMgr cannot reverse address: " + << address << " : " << ex.what()); + } +} + +std::string +D2CfgMgr::reverseV4Address(const isc::asiolink::IOAddress& ioaddr) { + if (!ioaddr.isV4()) { + isc_throw(D2CfgError, "D2CfgMgr address is not IPv4 address :" + << ioaddr); + } + + // Get the address in byte vector form. + const ByteAddress bytes = ioaddr.toBytes(); + + // Walk backwards through vector outputting each octet and a dot. + std::ostringstream stream; + + // We have to set the following variable to get + // const_reverse_iterator type of rend(), otherwise Solaris GCC + // complains on operator!= by trying to use the non-const variant. + const ByteAddress::const_reverse_iterator end = bytes.rend(); + + for (ByteAddress::const_reverse_iterator rit = bytes.rbegin(); + rit != end; + ++rit) + { + stream << static_cast<unsigned int>(*rit) << "."; + } + + // Tack on the suffix and we're done. + stream << IPV4_REV_ZONE_SUFFIX; + return(stream.str()); +} + +std::string +D2CfgMgr::reverseV6Address(const isc::asiolink::IOAddress& ioaddr) { + if (!ioaddr.isV6()) { + isc_throw(D2CfgError, "D2Cfg address is not IPv6 address: " << ioaddr); + } + + // Turn the address into a string of digits. + const ByteAddress bytes = ioaddr.toBytes(); + const std::string digits = isc::util::encode::encodeHex(bytes); + + // Walk backwards through string outputting each digits and a dot. + std::ostringstream stream; + + // We have to set the following variable to get + // const_reverse_iterator type of rend(), otherwise Solaris GCC + // complains on operator!= by trying to use the non-const variant. + const std::string::const_reverse_iterator end = digits.rend(); + + for (std::string::const_reverse_iterator rit = digits.rbegin(); + rit != end; + ++rit) + { + stream << static_cast<char>(*rit) << "."; + } + + // Tack on the suffix and we're done. + stream << IPV6_REV_ZONE_SUFFIX; + return(stream.str()); +} + +const D2ParamsPtr& +D2CfgMgr::getD2Params() { + return (getD2CfgContext()->getD2Params()); +} + +const isc::data::ConstElementPtr +D2CfgMgr::getControlSocketInfo() { + return (getD2CfgContext()->getControlSocketInfo()); +} + +std::string +D2CfgMgr::getConfigSummary(const uint32_t) { + return (getD2Params()->getConfigSummary()); +} + +void +D2CfgMgr::setCfgDefaults(ElementPtr mutable_config) { + D2SimpleParser::setAllDefaults(mutable_config); +} + +isc::data::ConstElementPtr +D2CfgMgr::parse(isc::data::ConstElementPtr config_set, bool check_only) { + // Do a sanity check first. + if (!config_set) { + isc_throw(D2CfgError, "Mandatory config parameter not provided"); + } + + D2CfgContextPtr ctx = getD2CfgContext(); + + // Set the defaults + ElementPtr cfg = boost::const_pointer_cast<Element>(config_set); + D2SimpleParser::setAllDefaults(cfg); + + // And parse the configuration. + ConstElementPtr answer; + std::string excuse; + try { + // Do the actual parsing + D2SimpleParser parser; + parser.parse(ctx, cfg, check_only); + } catch (const isc::Exception& ex) { + excuse = ex.what(); + answer = createAnswer(CONTROL_RESULT_ERROR, excuse); + } catch (...) { + excuse = "undefined configuration parsing error"; + answer = createAnswer(CONTROL_RESULT_ERROR, excuse); + } + + // At this stage the answer was created only in case of exception. + if (answer) { + if (check_only) { + LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_CHECK_FAIL).arg(excuse); + } else { + LOG_ERROR(d2_logger, DHCP_DDNS_CONFIG_FAIL).arg(excuse); + } + return (answer); + } + + if (check_only) { + answer = createAnswer(CONTROL_RESULT_SUCCESS, + "Configuration check successful"); + } else { + answer = createAnswer(CONTROL_RESULT_SUCCESS, + "Configuration applied successfully."); + } + + return (answer); +} + +std::list<std::list<std::string>> +D2CfgMgr::jsonPathsToRedact() const { + static std::list<std::list<std::string>> const list({ + {"tsig-keys", "[]"}, + {"hooks-libraries", "[]", "parameters", "*"}, + }); + return list; +} + +} // namespace d2 +} // namespace isc diff --git a/src/lib/d2srv/d2_cfg_mgr.h b/src/lib/d2srv/d2_cfg_mgr.h new file mode 100644 index 0000000000..eade86cbff --- /dev/null +++ b/src/lib/d2srv/d2_cfg_mgr.h @@ -0,0 +1,339 @@ +// Copyright (C) 2014-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D2_CFG_MGR_H +#define D2_CFG_MGR_H + +#include <asiolink/io_service.h> +#include <cc/data.h> +#include <exceptions/exceptions.h> +#include <d2srv/d2_config.h> +#include <hooks/hooks_config.h> +#include <process/d_cfg_mgr.h> + +#include <stdint.h> +#include <string> + +namespace isc { +namespace d2 { + +class D2CfgContext; +/// @brief Pointer to a configuration context. +typedef boost::shared_ptr<D2CfgContext> D2CfgContextPtr; + +/// @brief DHCP-DDNS Configuration Context +/// +/// Implements the storage container for configuration context. +/// It provides a single enclosure for the storage of configuration parameters +/// and any other DHCP-DDNS specific information that needs to be accessible +/// during configuration parsing as well as to the application as a whole. +/// It is derived from the context base class, ConfigBase. +class D2CfgContext : public process::ConfigBase { +public: + /// @brief Constructor + D2CfgContext(); + + /// @brief Destructor + virtual ~D2CfgContext(); + + /// @brief Creates a clone of this context object. + /// + /// @return returns a pointer to the new clone. + virtual process::ConfigPtr clone() { + return (process::ConfigPtr(new D2CfgContext(*this))); + } + + /// @brief Fetches a reference to the D2Params + D2ParamsPtr& getD2Params() { + return (d2_params_); + } + + /// @brief Fetches the forward DNS domain list manager. + /// + /// @return returns a pointer to the forward manager. + DdnsDomainListMgrPtr getForwardMgr() { + return (forward_mgr_); + } + + /// @brief Sets the forward domain list manager + /// @param forward_mgr pointer to the new forward manager + void setForwardMgr(DdnsDomainListMgrPtr forward_mgr) { + forward_mgr_ = forward_mgr; + } + + /// @brief Fetches the reverse DNS domain list manager. + /// + /// @return returns a pointer to the reverse manager. + DdnsDomainListMgrPtr getReverseMgr() { + return (reverse_mgr_); + } + + /// @brief Sets the reverse domain list manager + /// @param reverse_mgr pointer to the new reverse manager + void setReverseMgr(DdnsDomainListMgrPtr reverse_mgr) { + reverse_mgr_ = reverse_mgr; + } + + /// @brief Fetches the map of TSIG keys. + /// + /// @return returns a pointer to the key map. + TSIGKeyInfoMapPtr getKeys() { + return (keys_); + } + + /// @brief Sets the map of TSIG keys + /// + /// @param keys pointer to the new TSIG key map + void setKeys(const TSIGKeyInfoMapPtr& keys) { + keys_ = keys; + } + + /// @brief Returns information about control socket + /// @return pointer to the Element that holds control-socket map + const isc::data::ConstElementPtr getControlSocketInfo() const { + return (control_socket_); + } + + /// @brief Sets information about the control socket + /// @param control_socket Element that holds control-socket map + void setControlSocketInfo(const isc::data::ConstElementPtr& control_socket) { + control_socket_ = control_socket; + } + + /// @brief Returns non-const reference to configured hooks libraries. + /// + /// @return non-const reference to configured hooks libraries. + isc::hooks::HooksConfig& getHooksConfig() { + return (hooks_config_); + } + + /// @brief Returns const reference to configured hooks libraries. + /// + /// @return const reference to configured hooks libraries. + const isc::hooks::HooksConfig& getHooksConfig() const { + return (hooks_config_); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +protected: + /// @brief Copy constructor for use by derivations in clone(). + D2CfgContext(const D2CfgContext& rhs); + +private: + /// @brief Private assignment operator to avoid potential for slicing. + D2CfgContext& operator=(const D2CfgContext& rhs); + + /// @brief Global level parameter storage + D2ParamsPtr d2_params_; + + /// @brief Forward domain list manager. + DdnsDomainListMgrPtr forward_mgr_; + + /// @brief Reverse domain list manager. + DdnsDomainListMgrPtr reverse_mgr_; + + /// @brief Storage for the map of TSIGKeyInfos. + TSIGKeyInfoMapPtr keys_; + + /// @brief Pointer to the control-socket information. + isc::data::ConstElementPtr control_socket_; + + /// @brief Configured hooks libraries. + isc::hooks::HooksConfig hooks_config_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr; + +/// @brief DHCP-DDNS Configuration Manager +/// +/// Provides the mechanisms for managing the DHCP-DDNS application's +/// configuration. This includes services for parsing sets of configuration +/// values, storing the parsed information in its converted form, +/// and retrieving the information on demand. +class D2CfgMgr : public process::DCfgMgrBase { +public: + /// @brief Reverse zone suffix added to IPv4 addresses for reverse lookups + /// @todo This should be configurable. + static const char* IPV4_REV_ZONE_SUFFIX; + + /// @brief Reverse zone suffix added to IPv6 addresses for reverse lookups + /// @todo This should be configurable. + static const char* IPV6_REV_ZONE_SUFFIX; + + /// @brief Constructor + D2CfgMgr(); + + /// @brief Destructor + virtual ~D2CfgMgr(); + + /// @brief Convenience method that returns the D2 configuration context. + /// + /// @return returns a pointer to the configuration context. + D2CfgContextPtr getD2CfgContext() { + return (boost::dynamic_pointer_cast<D2CfgContext>(getContext())); + } + + /// @brief Returns whether or not forward updates are enabled. + /// + /// This method currently uses the presence or absence of Forward DDNS + /// Domains to determine if forward updates are enabled or disabled. + /// @todo This could be expanded to include the check of a configurable + /// boolean value. + /// + /// @return true if forward updates are enabled, false otherwise. + bool forwardUpdatesEnabled(); + + /// @brief Returns whether or not reverse updates are enabled. + /// + /// This method currently uses the presence or absence of Reverse DDNS + /// Domains to determine if reverse updates are enabled or disabled. + /// @todo This could be expanded to include the check of a configurable + /// boolean value. + /// + /// @return true if reverse updates are enabled, false otherwise. + bool reverseUpdatesEnabled(); + + /// @brief Matches a given FQDN to a forward domain. + /// + /// This calls the matchDomain method of the forward domain manager to + /// match the given FQDN to a forward domain. + /// + /// @param fqdn is the name for which to look. + /// @param domain receives the matching domain. Note that it will be reset + /// upon entry and only set if a match is subsequently found. + /// + /// @return returns true if a match is found, false otherwise. + /// @throw throws D2CfgError if given an invalid fqdn. + bool matchForward(const std::string& fqdn, DdnsDomainPtr& domain); + + /// @brief Matches a given IP address to a reverse domain. + /// + /// This calls the matchDomain method of the reverse domain manager to + /// match the given IPv4 or IPv6 address to a reverse domain. + /// + /// @param ip_address is the name for which to look. + /// @param domain receives the matching domain. Note that it will be reset + /// upon entry and only set if a match is subsequently found. + /// + /// @return returns true if a match is found, false otherwise. + /// @throw throws D2CfgError if given an invalid fqdn. + bool matchReverse(const std::string& ip_address, DdnsDomainPtr& domain); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IP address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// @param address string containing a valid IPv4 or IPv6 address. + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if given an invalid address. + static std::string reverseIpAddress(const std::string& address); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IP address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// Example: + /// input: 192.168.1.15 + /// output: 15.1.168.192.in-addr.arpa. + /// + /// @param ioaddr is the IPv4 IOaddress to convert + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if not given an IPv4 address. + static std::string reverseV4Address(const isc::asiolink::IOAddress& ioaddr); + + /// @brief Generate a reverse order string for the given IP address + /// + /// This method creates a string containing the given IPv6 address + /// contents in reverse order. This format is used for matching + /// against reverse DDNS domains in DHCP_DDNS configuration. + /// After reversing the syllables of the address, it appends the + /// appropriate suffix. + /// + /// IPv6 example: + /// input: 2001:db8:302:99:: + /// output: + ///0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.9.9.0.0.2.0.3.0.8.B.D.0.1.0.0.2.ip6.arpa. + /// + /// @param ioaddr string containing a valid IPv6 address. + /// + /// @return a std::string containing the reverse order address. + /// + /// @throw D2CfgError if not given an IPv6 address. + static std::string reverseV6Address(const isc::asiolink::IOAddress& ioaddr); + + /// @brief Convenience method fetches the D2Params from context + /// @return reference to const D2ParamsPtr + const D2ParamsPtr& getD2Params(); + + /// @brief Convenience method fetches information about control socket + /// from context + /// @return pointer to the Element that holds control-socket map + const isc::data::ConstElementPtr getControlSocketInfo(); + + /// @brief Returns configuration summary in the textual format. + /// + /// @param selection Bitfield which describes the parts of the configuration + /// to be returned. This parameter is ignored for the D2. + /// + /// @return Summary of the configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t selection) override; + + std::list<std::list<std::string>> jsonPathsToRedact() const final override; + +protected: + /// @brief Parses configuration of the D2. + /// + /// @param config Pointer to a configuration specified for D2. + /// @param check_only Boolean flag indicating if this method should + /// only verify correctness of the provided configuration. + /// @return Pointer to a result of configuration parsing. + virtual isc::data::ConstElementPtr + parse(isc::data::ConstElementPtr config, bool check_only) override; + + /// @brief Adds default values to the given config + /// + /// Adds the D2 default values to the configuration Element map. This + /// method is invoked by @c DCfgMgrBase::parseConfig(). + /// + /// @param mutable_config - configuration to which defaults should be added + virtual void setCfgDefaults(isc::data::ElementPtr mutable_config) override; + + /// @brief Creates an new, blank D2CfgContext context + /// + /// This method is used at the beginning of configuration process to + /// create a fresh, empty copy of a D2CfgContext. This new context will + /// be populated during the configuration process and will replace the + /// existing context provided the configuration process completes without + /// error. + /// + /// @return Returns a ConfigPtr to the new context instance. + virtual process::ConfigPtr createNewContext() override; +}; + +/// @brief Defines a shared pointer to D2CfgMgr. +typedef boost::shared_ptr<D2CfgMgr> D2CfgMgrPtr; + +} // end of isc::d2 namespace +} // end of isc namespace + +#endif // D2_CFG_MGR_H diff --git a/src/lib/d2srv/d2_config.cc b/src/lib/d2srv/d2_config.cc new file mode 100644 index 0000000000..68fd48e5af --- /dev/null +++ b/src/lib/d2srv/d2_config.cc @@ -0,0 +1,644 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2srv/d2_log.h> +#include <d2srv/d2_cfg_mgr.h> +#include <dhcpsrv/parsers/dhcp_parsers.h> +#include <exceptions/exceptions.h> +#include <asiolink/io_error.h> + +#include <boost/foreach.hpp> +#include <boost/scoped_ptr.hpp> +#include <boost/algorithm/string/predicate.hpp> + +#include <sstream> +#include <string> + +using namespace isc::process; +using namespace isc::data; + +namespace isc { +namespace d2 { + +// *********************** D2Params ************************* + +D2Params::D2Params(const isc::asiolink::IOAddress& ip_address, + const size_t port, + const size_t dns_server_timeout, + const dhcp_ddns::NameChangeProtocol& ncr_protocol, + const dhcp_ddns::NameChangeFormat& ncr_format) + : ip_address_(ip_address), + port_(port), + dns_server_timeout_(dns_server_timeout), + ncr_protocol_(ncr_protocol), + ncr_format_(ncr_format) { + validateContents(); +} + +D2Params::D2Params() + : ip_address_(isc::asiolink::IOAddress("127.0.0.1")), + port_(53001), dns_server_timeout_(100), + ncr_protocol_(dhcp_ddns::NCR_UDP), + ncr_format_(dhcp_ddns::FMT_JSON) { + validateContents(); +} + +D2Params::~D2Params(){}; + +void +D2Params::validateContents() { + if ((ip_address_.toText() == "0.0.0.0") || (ip_address_.toText() == "::")) { + isc_throw(D2CfgError, + "D2Params: IP address cannot be \"" << ip_address_ << "\""); + } + + if (port_ == 0) { + isc_throw(D2CfgError, "D2Params: port cannot be 0"); + } + + if (dns_server_timeout_ < 1) { + isc_throw(D2CfgError, + "D2Params: DNS server timeout must be larger than 0"); + } + + if (ncr_format_ != dhcp_ddns::FMT_JSON) { + isc_throw(D2CfgError, "D2Params: NCR Format:" + << dhcp_ddns::ncrFormatToString(ncr_format_) + << " is not yet supported"); + } + + if (ncr_protocol_ != dhcp_ddns::NCR_UDP) { + isc_throw(D2CfgError, "D2Params: NCR Protocol:" + << dhcp_ddns::ncrProtocolToString(ncr_protocol_) + << " is not yet supported"); + } +} + +std::string +D2Params::getConfigSummary() const { + std::ostringstream s; + s << "listening on " << getIpAddress() << ", port " << getPort() + << ", using " << ncrProtocolToString(ncr_protocol_); + return (s.str()); +} + +bool +D2Params::operator == (const D2Params& other) const { + return ((ip_address_ == other.ip_address_) && + (port_ == other.port_) && + (dns_server_timeout_ == other.dns_server_timeout_) && + (ncr_protocol_ == other.ncr_protocol_) && + (ncr_format_ == other.ncr_format_)); +} + +bool +D2Params::operator != (const D2Params& other) const { + return (!(*this == other)); +} + +std::string +D2Params::toText() const { + std::ostringstream stream; + + stream << ", ip-address: " << ip_address_.toText() + << ", port: " << port_ + << ", dns-server-timeout_: " << dns_server_timeout_ + << ", ncr-protocol: " + << dhcp_ddns::ncrProtocolToString(ncr_protocol_) + << ", ncr-format: " << ncr_format_ + << dhcp_ddns::ncrFormatToString(ncr_format_); + + return (stream.str()); +} + +std::ostream& +operator<<(std::ostream& os, const D2Params& config) { + os << config.toText(); + return (os); +} + +// *********************** TSIGKeyInfo ************************* +// Note these values match corresponding values for Bind9's +// dnssec-keygen +const char* TSIGKeyInfo::HMAC_MD5_STR = "HMAC-MD5"; +const char* TSIGKeyInfo::HMAC_SHA1_STR = "HMAC-SHA1"; +const char* TSIGKeyInfo::HMAC_SHA224_STR = "HMAC-SHA224"; +const char* TSIGKeyInfo::HMAC_SHA256_STR = "HMAC-SHA256"; +const char* TSIGKeyInfo::HMAC_SHA384_STR = "HMAC-SHA384"; +const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512"; + +TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm, + const std::string& secret, uint32_t digestbits) + :name_(name), algorithm_(algorithm), secret_(secret), + digestbits_(digestbits), tsig_key_() { + remakeKey(); +} + +TSIGKeyInfo::~TSIGKeyInfo() { +} + +const dns::Name& +TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) { + if (boost::iequals(algorithm_id, HMAC_MD5_STR)) { + return (dns::TSIGKey::HMACMD5_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA1_STR)) { + return (dns::TSIGKey::HMACSHA1_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA224_STR)) { + return (dns::TSIGKey::HMACSHA224_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA256_STR)) { + return (dns::TSIGKey::HMACSHA256_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA384_STR)) { + return (dns::TSIGKey::HMACSHA384_NAME()); + } else if (boost::iequals(algorithm_id, HMAC_SHA512_STR)) { + return (dns::TSIGKey::HMACSHA512_NAME()); + } + + isc_throw(BadValue, "Unknown TSIG Key algorithm: " << algorithm_id); +} + +void +TSIGKeyInfo::remakeKey() { + try { + // Since our secret value is base64 encoded already, we need to + // build the input string for the appropriate TSIGKey constructor. + // If secret isn't a valid base64 value, the constructor will throw. + std::ostringstream stream; + stream << dns::Name(name_).toText() << ":" + << secret_ << ":" + << stringToAlgorithmName(algorithm_); + if (digestbits_ > 0) { + stream << ":" << digestbits_; + } + + tsig_key_.reset(new dns::TSIGKey(stream.str())); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "Cannot make TSIGKey: " << ex.what()); + } +} + +ElementPtr +TSIGKeyInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set name + result->set("name", Element::create(name_)); + // Set algorithm + result->set("algorithm", Element::create(algorithm_)); + // Set secret + result->set("secret", Element::create(secret_)); + // Set digest-bits + result->set("digest-bits", + Element::create(static_cast<int64_t>(digestbits_))); + + return (result); +} + +// *********************** DnsServerInfo ************************* +DnsServerInfo::DnsServerInfo(const std::string& hostname, + isc::asiolink::IOAddress ip_address, uint32_t port, + bool enabled) + :hostname_(hostname), ip_address_(ip_address), port_(port), + enabled_(enabled) { +} + +DnsServerInfo::~DnsServerInfo() { +} + +std::string +DnsServerInfo::toText() const { + std::ostringstream stream; + stream << (getIpAddress().toText()) << " port:" << getPort(); + return (stream.str()); +} + +ElementPtr +DnsServerInfo::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set hostname + result->set("hostname", Element::create(hostname_)); + // Set ip-address + result->set("ip-address", Element::create(ip_address_.toText())); + // Set port + result->set("port", Element::create(static_cast<int64_t>(port_))); + + return (result); +} + + +std::ostream& +operator<<(std::ostream& os, const DnsServerInfo& server) { + os << server.toText(); + return (os); +} + +// *********************** DdnsDomain ************************* + +DdnsDomain::DdnsDomain(const std::string& name, + DnsServerInfoStoragePtr servers, + const TSIGKeyInfoPtr& tsig_key_info) + : name_(name), servers_(servers), + tsig_key_info_(tsig_key_info) { +} + +DdnsDomain::~DdnsDomain() { +} + +const std::string +DdnsDomain::getKeyName() const { + if (tsig_key_info_) { + return (tsig_key_info_->getName()); + } + + return (""); +} + +ElementPtr +DdnsDomain::toElement() const { + ElementPtr result = Element::createMap(); + // Set user-context + contextToElement(result); + // Set name + result->set("name", Element::create(name_)); + // Set servers + ElementPtr servers = Element::createList(); + for (DnsServerInfoStorage::const_iterator server = servers_->begin(); + server != servers_->end(); ++server) { + ElementPtr dns_server = (*server)->toElement(); + servers->add(dns_server); + } + // the dns server list may not be empty + if (!servers->empty()) { + result->set("dns-servers", servers); + } + // Set key-name + if (tsig_key_info_) { + result->set("key-name", Element::create(tsig_key_info_->getName())); + } + + return (result); +} + +// *********************** DdnsDomainLstMgr ************************* + +const char* DdnsDomainListMgr::wildcard_domain_name_ = "*"; + +DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name), + domains_(new DdnsDomainMap()) { +} + + +DdnsDomainListMgr::~DdnsDomainListMgr () { +} + +void +DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) { + if (!domains) { + isc_throw(D2CfgError, + "DdnsDomainListMgr::setDomains: Domain list may not be null"); + } + + domains_ = domains; + + // Look for the wild card domain. If present, set the member variable + // to remember it. This saves us from having to look for it every time + // we attempt a match. + DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_); + if (gotit != domains_->end()) { + wildcard_domain_ = gotit->second; + } +} + +bool +DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) { + // First check the case of one domain to rule them all. + if ((size() == 1) && (wildcard_domain_)) { + domain = wildcard_domain_; + return (true); + } + + // Iterate over the domain map looking for the domain which matches + // the longest portion of the given fqdn. + + size_t req_len = fqdn.size(); + size_t match_len = 0; + DdnsDomainMapPair map_pair; + DdnsDomainPtr best_match; + BOOST_FOREACH (map_pair, *domains_) { + std::string domain_name = map_pair.first; + size_t dom_len = domain_name.size(); + + // If the domain name is longer than the fqdn, then it cant be match. + if (req_len < dom_len) { + continue; + } + + // If the lengths are identical and the names match we're done. + if (req_len == dom_len) { + if (boost::iequals(fqdn, domain_name)) { + // exact match, done + domain = map_pair.second; + return (true); + } + } else { + // The fqdn is longer than the domain name. Adjust the start + // point of comparison by the excess in length. Only do the + // comparison if the adjustment lands on a boundary. This + // prevents "onetwo.net" from matching "two.net". + size_t offset = req_len - dom_len; + if ((fqdn[offset - 1] == '.') && + (boost::iequals(fqdn.substr(offset), domain_name))) { + // Fqdn contains domain name, keep it if its better than + // any we have matched so far. + if (dom_len > match_len) { + match_len = dom_len; + best_match = map_pair.second; + } + } + } + } + + if (!best_match) { + // There's no match. If they specified a wild card domain use it + // otherwise there's no domain for this entry. + if (wildcard_domain_) { + domain = wildcard_domain_; + return (true); + } + + LOG_WARN(dhcp_to_d2_logger, DHCP_DDNS_NO_MATCH).arg(fqdn); + return (false); + } + + domain = best_match; + return (true); +} + +ElementPtr +DdnsDomainListMgr::toElement() const { + ElementPtr result = Element::createList(); + // Iterate on ddns domains + for (DdnsDomainMap::const_iterator domain = domains_->begin(); + domain != domains_->end(); ++domain) { + ElementPtr ddns_domain = domain->second->toElement(); + result->add(ddns_domain); + } + + return (result); +} + +// *************************** PARSERS *********************************** + +// *********************** TSIGKeyInfoParser ************************* + +TSIGKeyInfoPtr +TSIGKeyInfoParser::parse(ConstElementPtr key_config) { + std::string name = getString(key_config, "name"); + std::string algorithm = getString(key_config, "algorithm"); + uint32_t digestbits = getInteger(key_config, "digest-bits"); + std::string secret = getString(key_config, "secret"); + ConstElementPtr user_context = key_config->get("user-context"); + + // Algorithm must be valid. + try { + TSIGKeyInfo::stringToAlgorithmName(algorithm); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "tsig-key : " << ex.what() + << " (" << getPosition("algorithm", key_config) << ")"); + } + + // Non-zero digest-bits must be an integral number of octets, greater + // than 80 and at least half of the algorithm key length. It defaults + // to zero and JSON parsing ensures it's a multiple of 8. + if ((digestbits > 0) && + ((digestbits < 80) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR) + && (digestbits < 112)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR) + && (digestbits < 128)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR) + && (digestbits < 192)) || + (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR) + && (digestbits < 256)))) { + isc_throw(D2CfgError, "tsig-key: digest-bits too small : (" + << getPosition("digest-bits", key_config) + << ")"); + } + + // Everything should be valid, so create the key instance. + // It is possible for the asiodns::dns::TSIGKey create to fail such as + // with an invalid secret content. + TSIGKeyInfoPtr key_info; + try { + key_info.reset(new TSIGKeyInfo(name, algorithm, secret, digestbits)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, ex.what() << " (" + << key_config->getPosition() << ")"); + } + + // Add user-context + if (user_context) { + key_info->setContext(user_context); + } + + return (key_info); +} + +// *********************** TSIGKeyInfoListParser ************************* + +TSIGKeyInfoMapPtr +TSIGKeyInfoListParser::parse(ConstElementPtr key_list) { + TSIGKeyInfoMapPtr keys(new TSIGKeyInfoMap()); + ConstElementPtr key_config; + TSIGKeyInfoParser key_parser; + BOOST_FOREACH(key_config, key_list->listValue()) { + TSIGKeyInfoPtr key = key_parser.parse(key_config); + + // Duplicates are not allowed and should be flagged as an error. + if (keys->find(key->getName()) != keys->end()) { + isc_throw(D2CfgError, "Duplicate TSIG key name specified : " + << key->getName() + << " (" << getPosition("name", key_config) << ")"); + } + + (*keys)[key->getName()] = key; + } + + return (keys); +} + +// *********************** DnsServerInfoParser ************************* + +DnsServerInfoPtr +DnsServerInfoParser::parse(ConstElementPtr server_config) { + std::string hostname = getString(server_config, "hostname"); + std::string ip_address = getString(server_config, "ip-address"); + uint32_t port = getInteger(server_config, "port"); + ConstElementPtr user_context = server_config->get("user-context"); + + // The configuration must specify one or the other. + if (hostname.empty() == ip_address.empty()) { + isc_throw(D2CfgError, "Dns Server must specify one or the other" + " of hostname or IP address" + << " (" << server_config->getPosition() << ")"); + } + + DnsServerInfoPtr server_info; + if (!hostname.empty()) { + /// @todo when resolvable hostname is supported we create the entry + /// as follows: + /// + /// @code + /// // When hostname is specified, create a valid, blank IOAddress + /// // and then create the DnsServerInfo. + /// serverInfo.reset(new DnsServerInfo(hostname, io_addr, port)); + /// + /// @endcode + /// + /// Resolution will be done prior to connection during transaction + /// processing. + /// Until then we'll throw unsupported. + isc_throw(D2CfgError, "Dns Server : hostname is not yet supported" + << " (" << getPosition("hostname", server_config) << ")"); + } else { + try { + // Create an IOAddress from the IP address string given and then + // create the DnsServerInfo. + isc::asiolink::IOAddress io_addr(ip_address); + server_info.reset(new DnsServerInfo(hostname, io_addr, port)); + } catch (const isc::asiolink::IOError& ex) { + isc_throw(D2CfgError, "Dns Server : invalid IP address : " + << ip_address + << " (" << getPosition("ip-address", server_config) << ")"); + } + } + + // Add user-context + if (user_context) { + server_info->setContext(user_context); + } + + return (server_info); +} + +// *********************** DnsServerInfoListParser ************************* + +DnsServerInfoStoragePtr +DnsServerInfoListParser::parse(ConstElementPtr server_list) { + DnsServerInfoStoragePtr servers(new DnsServerInfoStorage()); + ConstElementPtr server_config; + DnsServerInfoParser parser; + BOOST_FOREACH(server_config, server_list->listValue()) { + DnsServerInfoPtr server = parser.parse(server_config); + servers->push_back(server); + } + + return (servers); +} + +// *********************** DdnsDomainParser ************************* + +DdnsDomainPtr DdnsDomainParser::parse(ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys) { + std::string name = getString(domain_config, "name"); + std::string key_name = getString(domain_config, "key-name"); + ConstElementPtr user_context = domain_config->get("user-context"); + + // Key name is optional. If it is not blank, then find the key in the + // list of defined keys. + TSIGKeyInfoPtr tsig_key_info; + if (!key_name.empty()) { + if (keys) { + TSIGKeyInfoMap::iterator kit = keys->find(key_name); + if (kit != keys->end()) { + tsig_key_info = kit->second; + } + } + + if (!tsig_key_info) { + isc_throw(D2CfgError, "DdnsDomain : " << name + << " specifies an undefined key: " << key_name + << " (" << getPosition("key-name", domain_config) << ")"); + } + } + + // Parse the list of DNS servers + ConstElementPtr servers_config; + try { + servers_config = domain_config->get("dns-servers"); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, "DdnsDomain : missing dns-server list" + << " (" << servers_config->getPosition() << ")"); + } + + DnsServerInfoListParser server_parser; + DnsServerInfoStoragePtr servers = server_parser.parse(servers_config); + if (servers->size() == 0) { + isc_throw(D2CfgError, "DNS server list cannot be empty" + << servers_config->getPosition()); + } + + // Instantiate the new domain and add it to domain storage. + DdnsDomainPtr domain(new DdnsDomain(name, servers, tsig_key_info)); + + // Add user-context + if (user_context) { + domain->setContext(user_context); + } + + return (domain); +} + +// *********************** DdnsDomainListParser ************************* + +DdnsDomainMapPtr DdnsDomainListParser::parse(ConstElementPtr domain_list, + const TSIGKeyInfoMapPtr keys) { + DdnsDomainMapPtr domains(new DdnsDomainMap()); + DdnsDomainParser parser; + ConstElementPtr domain_config; + BOOST_FOREACH(domain_config, domain_list->listValue()) { + DdnsDomainPtr domain = parser.parse(domain_config, keys); + + // Duplicates are not allowed + if (domains->find(domain->getName()) != domains->end()) { + isc_throw(D2CfgError, "Duplicate domain specified:" + << domain->getName() + << " (" << getPosition("name", domain_config) << ")"); + } + + (*domains)[domain->getName()] = domain; + } + + return (domains); +} + +// *********************** DdnsDomainListMgrParser ************************* + +DdnsDomainListMgrPtr +DdnsDomainListMgrParser::parse(ConstElementPtr mgr_config, + const std::string& mgr_name, + const TSIGKeyInfoMapPtr keys) { + DdnsDomainListMgrPtr mgr(new DdnsDomainListMgr(mgr_name)); + + // Parse the list of domains + ConstElementPtr domains_config = mgr_config->get("ddns-domains"); + if (domains_config) { + DdnsDomainListParser domain_parser; + DdnsDomainMapPtr domains = domain_parser.parse(domains_config, keys); + + // Add the new domain to the domain storage. + mgr->setDomains(domains); + } + + return(mgr); +} + +}; // end of isc::dhcp namespace +}; // end of isc namespace diff --git a/src/lib/d2srv/d2_config.h b/src/lib/d2srv/d2_config.h new file mode 100644 index 0000000000..2d7b5e10e2 --- /dev/null +++ b/src/lib/d2srv/d2_config.h @@ -0,0 +1,898 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D2_CONFIG_H +#define D2_CONFIG_H + +#include <asiolink/io_service.h> +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <cc/cfg_to_element.h> +#include <cc/user_context.h> +#include <dhcpsrv/parsers/dhcp_parsers.h> +#include <dns/tsig.h> +#include <exceptions/exceptions.h> +#include <process/d_cfg_mgr.h> + +#include <boost/foreach.hpp> + +#include <stdint.h> +#include <string> + +namespace isc { +namespace d2 { + +/// @file d2_config.h +/// @brief A collection of classes for housing and parsing the application +/// configuration necessary for the DHCP-DDNS application (aka D2). +/// +/// This file contains the class declarations for the class hierarchy created +/// from the D2 configuration and the parser classes used to create it. +/// The application configuration consists of a set of scalar parameters, +/// a list of TSIG keys, and two managed lists of domains: one list for +/// forward domains and one list for reverse domains. +/// +/// The key list consists of one or more TSIG keys, each entry described by +/// a name, the algorithm method name, optionally the minimum truncated +/// length, and its secret key component. +/// +/// Each managed domain list consists of a list one or more domains and is +/// represented by the class DdnsDomainListMgr. +/// +/// Each domain consists of a set of scalars parameters and a list of DNS +/// servers which support that domain. Among its scalars, is key_name, which +/// is the name of the TSIG Key to use for with this domain. This value should +/// map to one of the TSIG Keys in the key list. Domains are represented by +/// the class, DdnsDomain. +/// +/// Each server consists of a set of scalars used to describe the server such +/// that the application can carry out DNS update exchanges with it. Servers +/// are represented by the class, DnsServerInfo. +/// +/// The parsing class hierarchy reflects this same scheme. Working top down: +/// +/// A DdnsDomainListMgrParser parses a managed domain list entry. It handles +/// any scalars which belong to the manager as well as creating and invoking a +/// DdnsDomainListParser to parse its list of domain entries. +/// +/// A DdnsDomainListParser creates and invokes a DdnsDomainParser for each +/// domain entry in its list. +/// +/// A DdnsDomainParser handles the scalars which belong to the domain as well as +/// creating and invoking a DnsSeverInfoListParser to parse its list of server +/// entries. +/// +/// A DnsServerInfoListParser creates and invokes a DnsServerInfoParser for +/// each server entry in its list. +/// +/// A DdnsServerInfoParser handles the scalars which belong to the server. +/// The following is sample configuration in JSON form with extra spacing +/// for clarity: +/// +/// @code +/// { +/// "interface" : "eth1" , +/// "ip-address" : "192.168.1.33" , +/// "port" : 88 , +/// "control-socket": +/// { +/// "socket-type": "unix" , +/// "socket-name": "/tmp/kea-ddns-ctrl-socket" +//// }, +/// "tsig-keys": +//// [ +/// { +/// "name": "d2_key.tmark.org" , +/// "algorithm": "md5" , +/// "secret": "0123456989" +/// } +/// ], +/// "forward-ddns" : +/// { +/// "ddns-domains": +/// [ +/// { +/// "name": "tmark.org." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "hostname": "fserver.tmark.org" }, +/// { "hostname": "f2server.tmark.org" } +/// ] +/// }, +/// { +/// "name": "pub.tmark.org." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "hostname": "f3server.tmark.org" } +/// ] +/// } +/// ] +/// }, +/// "reverse-ddns" : +/// { +/// "ddns-domains": +/// [ +/// { +/// "name": " 0.168.192.in.addr.arpa." , +/// "key-name": "d2_key.tmark.org" , +/// "dns-servers" : +/// [ +/// { "ip-address": "127.0.0.101" , "port": 100 } +/// ] +/// } +/// ] +/// } +/// } +/// @endcode + +/// @brief Exception thrown when the error during configuration handling +/// occurs. +class D2CfgError : public isc::Exception { +public: + D2CfgError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { }; +}; + +/// @brief Acts as a storage vault for D2 global scalar parameters +class D2Params { +public: + /// @brief Constructor + /// + /// @param ip_address IP address at which D2 should listen for NCRs + /// @param port port on which D2 should listen NCRs + /// @param dns_server_timeout maximum amount of time in milliseconds to + /// wait for a response to a single DNS update request. + /// @param ncr_protocol socket protocol D2 should use to receive NCRS + /// @param ncr_format packet format of the inbound NCRs + /// + /// @throw D2CfgError if: + /// -# ip_address is 0.0.0.0 or :: + /// -# port is 0 + /// -# dns_server_timeout is < 1 + /// -# ncr_protocol is invalid, currently only NCR_UDP is supported + /// -# ncr_format is invalid, currently only FMT_JSON is supported + D2Params(const isc::asiolink::IOAddress& ip_address, + const size_t port, + const size_t dns_server_timeout, + const dhcp_ddns::NameChangeProtocol& ncr_protocol, + const dhcp_ddns::NameChangeFormat& ncr_format); + + /// @brief Default constructor + /// The default constructor creates an instance that has updates disabled. + D2Params(); + + /// @brief Destructor + virtual ~D2Params(); + + /// @brief Return the IP address D2 listens on. + const isc::asiolink::IOAddress& getIpAddress() const { + return(ip_address_); + } + + /// @brief Return the TCP/UPD port D2 listens on. + size_t getPort() const { + return(port_); + } + + /// @brief Return the DNS server timeout value. + size_t getDnsServerTimeout() const { + return(dns_server_timeout_); + } + + /// @brief Return the socket protocol in use. + const dhcp_ddns::NameChangeProtocol& getNcrProtocol() const { + return(ncr_protocol_); + } + + /// @brief Return the expected format of inbound requests (NCRs). + const dhcp_ddns::NameChangeFormat& getNcrFormat() const { + return(ncr_format_); + } + + /// @brief Return summary of the configuration used by D2. + /// + /// The returned summary of the configuration is meant to be appended to + /// the log message informing about the successful completion of the + /// D2 configuration. + /// + /// @return Configuration summary in the textual format. + std::string getConfigSummary() const; + + /// @brief Compares two D2Params's for equality + bool operator == (const D2Params& other) const; + + /// @brief Compares two D2Params's for inequality + bool operator != (const D2Params& other) const; + + /// @brief Generates a string representation of the class contents. + std::string toText() const; + +protected: + /// @brief Validates member values. + /// + /// Method is used by the constructor to validate member contents. + /// Currently checks: + /// -# ip_address is not 0.0.0.0 or :: + /// -# port is not 0 + /// -# dns_server_timeout is 0 + /// -# ncr_protocol is UDP + /// -# ncr_format is JSON + /// + /// @throw D2CfgError if contents are invalid + virtual void validateContents(); + +private: + /// @brief IP address D2 listens on. + isc::asiolink::IOAddress ip_address_; + + /// @brief IP port D2 listens on. + size_t port_; + + /// @brief Timeout for a single DNS packet exchange in milliseconds. + size_t dns_server_timeout_; + + /// @brief The socket protocol to use. + /// Currently only UDP is supported. + dhcp_ddns::NameChangeProtocol ncr_protocol_; + + /// @brief Format of the inbound requests (NCRs). + /// Currently only JSON format is supported. + dhcp_ddns::NameChangeFormat ncr_format_; +}; + +/// @brief Dumps the contents of a D2Params as text to an output stream +/// +/// @param os output stream to which text should be sent +/// @param config D2Param instance to dump +std::ostream& +operator<<(std::ostream& os, const D2Params& config); + +/// @brief Defines a pointer for D2Params instances. +typedef boost::shared_ptr<D2Params> D2ParamsPtr; + +/// @brief Represents a TSIG Key. +/// +/// Acts as both a storage class containing the basic attributes which +/// describe a TSIG Key, as well as owning and providing access to an +/// instance of the actual key (@ref isc::dns::TSIGKey) that can be used +/// by the IO layer for signing and verifying messages. +/// +class TSIGKeyInfo : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief Defines string values for the supported TSIG algorithms + //@{ + static const char* HMAC_MD5_STR; + static const char* HMAC_SHA1_STR; + static const char* HMAC_SHA256_STR; + static const char* HMAC_SHA224_STR; + static const char* HMAC_SHA384_STR; + static const char* HMAC_SHA512_STR; + //}@ + + /// @brief Constructor + /// + /// @param name the unique label used to identify this key + /// @param algorithm the id of the encryption algorithm this key uses. + /// Currently supported values are (case insensitive): + /// -# "HMAC-MD5" + /// -# "HMAC-SHA1" + /// -# "HMAC-SHA224" + /// -# "HMAC-SHA256" + /// -# "HMAC-SHA384" + /// -# "HMAC-SHA512" + /// + /// @param secret The base-64 encoded secret component for this key. + /// (A suitable string for use here could be obtained by running the + /// BIND 9 dnssec-keygen program; the contents of resulting key file + /// will look similar to: + /// @code + /// Private-key-format: v1.3 + /// Algorithm: 157 (HMAC_MD5) + /// Key: LSWXnfkKZjdPJI5QxlpnfQ== + /// Bits: AAA= + /// Created: 20140515143700 + /// Publish: 20140515143700 + /// Activate: 20140515143700 + /// @endcode + /// where the value the "Key:" entry is the secret component of the key.) + /// @param digestbits the minimum truncated length in bits + /// + /// @throw D2CfgError if values supplied are invalid: + /// name cannot be blank, algorithm must be a supported value, + /// secret must be a non-blank, base64 encoded string. + TSIGKeyInfo(const std::string& name, const std::string& algorithm, + const std::string& secret, uint32_t digestbits = 0); + + /// @brief Destructor + virtual ~TSIGKeyInfo(); + + /// @brief Getter which returns the key's name. + /// + /// @return returns the name as a std::string. + const std::string getName() const { + return (name_); + } + + /// @brief Getter which returns the key's algorithm string ID + /// + /// @return returns the algorithm as a std::string. + const std::string getAlgorithm() const { + return (algorithm_); + } + + /// @brief Getter which returns the key's minimum truncated length + /// + /// @return returns the minimum truncated length or 0 as an uint32_t + uint32_t getDigestbits() const { + return (digestbits_); + } + + /// @brief Getter which returns the key's secret. + /// + /// @return returns the secret as a std::string. + const std::string getSecret() const { + return (secret_); + } + + /// @brief Getter which returns the TSIG key used to sign and verify + /// messages + /// + /// @return const pointer reference to dns::TSIGKey. + const dns::TSIGKeyPtr& getTSIGKey() const { + return (tsig_key_); + } + + /// @brief Converts algorithm id to dns::TSIGKey algorithm dns::Name + /// + /// @param algorithm_id string value to translate into an algorithm name. + /// Currently supported values are (case insensitive): + /// -# "HMAC-MD5" + /// -# "HMAC-SHA1" + /// -# "HMAC-SHA224" + /// -# "HMAC-SHA256" + /// -# "HMAC-SHA384" + /// -# "HMAC-SHA512" + /// + /// @return const reference to a dns::Name containing the algorithm name + /// @throw BadValue if ID isn't recognized. + static const dns::Name& stringToAlgorithmName(const std::string& + algorithm_id); + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief Creates the actual TSIG key instance member + /// + /// Replaces this tsig_key member with a key newly created using the key + /// name, algorithm id, digest bits, and secret. + /// This method is currently only called by the constructor, however it + /// could be called post-construction should keys ever support expiration. + /// + /// @throw D2CfgError with an explanation if the key could not be created. + void remakeKey(); + + /// @brief The name of the key. + /// + /// This value is the unique identifier that domains use to + /// to specify which TSIG key they need. + std::string name_; + + /// @brief The string ID of the algorithm that should be used for this key. + std::string algorithm_; + + /// @brief The base64 encoded string secret value component of this key. + std::string secret_; + + /// @brief The minimum truncated length in bits + /// (0 means no truncation is allowed and is the default) + uint32_t digestbits_; + + /// @brief The actual TSIG key. + dns::TSIGKeyPtr tsig_key_; +}; + +/// @brief Defines a pointer for TSIGKeyInfo instances. +typedef boost::shared_ptr<TSIGKeyInfo> TSIGKeyInfoPtr; + +/// @brief Defines a map of TSIGKeyInfos, keyed by the name. +typedef std::map<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMap; + +/// @brief Defines a iterator pairing of name and TSIGKeyInfo +typedef std::pair<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMapPair; + +/// @brief Defines a pointer to map of TSIGkeyInfos +typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr; + + +/// @brief Represents a specific DNS Server. +/// It provides information about the server's network identity and typically +/// belongs to a list of servers supporting DNS for a given domain. It will +/// be used to establish communications with the server to carry out DNS +/// updates. +class DnsServerInfo : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief defines DNS standard port value + static const uint32_t STANDARD_DNS_PORT = 53; + + /// @brief Constructor + /// + /// @param hostname is the resolvable name of the server. If not blank, + /// then the server address should be resolved at runtime. + /// @param ip_address is the static IP address of the server. If hostname + /// is blank, then this address should be used to connect to the server. + /// @param port is the port number on which the server listens. + /// primarily meant for testing purposes. Normally, DNS traffic is on + /// is port 53. (NOTE the constructing code is responsible for setting + /// the default.) + /// @param enabled is a flag that indicates whether this server is + /// enabled for use. It defaults to true. + DnsServerInfo(const std::string& hostname, + isc::asiolink::IOAddress ip_address, + uint32_t port = STANDARD_DNS_PORT, + bool enabled=true); + + /// @brief Destructor + virtual ~DnsServerInfo(); + + /// @brief Getter which returns the server's hostname. + /// + /// @return returns the hostname as as std::string. + const std::string getHostname() const { + return (hostname_); + } + + /// @brief Getter which returns the server's port number. + /// + /// @return returns the port number as a unsigned integer. + uint32_t getPort() const { + return (port_); + } + + /// @brief Getter which returns the server's ip_address. + /// + /// @return returns the address as an IOAddress reference. + const isc::asiolink::IOAddress& getIpAddress() const { + return (ip_address_); + } + + /// @brief Convenience method which returns whether or not the + /// server is enabled. + /// + /// @return returns true if the server is enabled, false otherwise. + bool isEnabled() const { + return (enabled_); + } + + /// @brief Sets the server's enabled flag to true. + void enable() { + enabled_ = true; + } + + /// @brief Sets the server's enabled flag to false. + void disable() { + enabled_ = false; + } + + /// @brief Returns a text representation for the server. + std::string toText() const; + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + + +private: + /// @brief The resolvable name of the server. If not blank, then the + /// server's IP address should be dynamically resolved at runtime. + std::string hostname_; + + /// @brief The static IP address of the server. When hostname is blank, + /// then this address should be used to connect to the server. + isc::asiolink::IOAddress ip_address_; + + /// @brief The port number on which the server listens for DNS traffic. + uint32_t port_; + + /// @param enabled is a flag that indicates whether this server is + /// enabled for use. It defaults to true. + bool enabled_; +}; + +std::ostream& +operator<<(std::ostream& os, const DnsServerInfo& server); + +/// @brief Defines a pointer for DnsServerInfo instances. +typedef boost::shared_ptr<DnsServerInfo> DnsServerInfoPtr; + +/// @brief Defines a storage container for DnsServerInfo pointers. +typedef std::vector<DnsServerInfoPtr> DnsServerInfoStorage; + +/// @brief Defines a pointer to DnsServerInfo storage containers. +typedef boost::shared_ptr<DnsServerInfoStorage> DnsServerInfoStoragePtr; + + +/// @brief Represents a DNS domain that is may be updated dynamically. +/// This class specifies a DNS domain and the list of DNS servers that support +/// it. Its primary use is to map a domain to the DNS server(s) responsible +/// for it. +/// @todo Currently the name entry for a domain is just an std::string. It +/// may be worthwhile to change this to a dns::Name for purposes of better +/// validation and matching capabilities. +class DdnsDomain : public isc::data::UserContext, public isc::data::CfgToElement { +public: + /// @brief Constructor + /// + /// @param name is the domain name of the domain. + /// @param servers is the list of server(s) supporting this domain. + /// @param tsig_key_info pointer to the TSIGKeyInfo for the domain's key + /// It defaults to an empty pointer, signifying the domain has no key. + DdnsDomain(const std::string& name, + DnsServerInfoStoragePtr servers, + const TSIGKeyInfoPtr& tsig_key_info = TSIGKeyInfoPtr()); + + /// @brief Destructor + virtual ~DdnsDomain(); + + /// @brief Getter which returns the domain's name. + /// + /// @return returns the name in an std::string. + const std::string getName() const { + return (name_); + } + + /// @brief Convenience method which returns the domain's TSIG key name. + /// + /// @return returns the key name in an std::string. If domain has no + /// TSIG key, the string will empty. + const std::string getKeyName() const; + + /// @brief Getter which returns the domain's list of servers. + /// + /// @return returns the pointer to the server storage. + const DnsServerInfoStoragePtr& getServers() { + return (servers_); + } + + /// @brief Getter which returns the domain's TSIGKey info + /// + /// @return returns the pointer to the server storage. If the domain + /// is not configured to use TSIG the pointer will be empty. + const TSIGKeyInfoPtr& getTSIGKeyInfo() { + return (tsig_key_info_); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief The domain name of the domain. + std::string name_; + + /// @brief The list of server(s) supporting this domain. + DnsServerInfoStoragePtr servers_; + + /// @brief Pointer to domain's the TSIGKeyInfo. + /// Value is empty if the domain is not configured for TSIG. + TSIGKeyInfoPtr tsig_key_info_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr<DdnsDomain> DdnsDomainPtr; + +/// @brief Defines a map of DdnsDomains, keyed by the domain name. +typedef std::map<std::string, DdnsDomainPtr> DdnsDomainMap; + +/// @brief Defines a iterator pairing domain name and DdnsDomain +typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainMapPair; + +/// @brief Defines a pointer to DdnsDomain storage containers. +typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr; + +/// @brief Provides storage for and management of a list of DNS domains. +/// In addition to housing the domain list storage, it provides domain matching +/// services. These services are used to match a FQDN to a domain. Currently +/// it supports a single matching service, which will return the matching +/// domain or a wild card domain if one is specified. The wild card domain is +/// specified as a domain whose name is "*". The wild card domain will match +/// any entry and is provided for flexibility in FQDNs If for instance, all +/// forward requests are handled by the same servers, the configuration could +/// specify the wild card domain as the only forward domain. All forward DNS +/// updates would be sent to that one list of servers, regardless of the FQDN. +/// As matching capabilities evolve this class is expected to expand. +class DdnsDomainListMgr : public isc::data::CfgToElement { +public: + /// @brief defines the domain name for denoting the wildcard domain. + static const char* wildcard_domain_name_; + + /// @brief Constructor + /// + /// @param name is an arbitrary label assigned to this manager. + DdnsDomainListMgr(const std::string& name); + + /// @brief Destructor + virtual ~DdnsDomainListMgr (); + + /// @brief Matches a given name to a domain based on a longest match + /// scheme. + /// + /// Given a FQDN, search the list of domains, successively removing a + /// sub-domain from the FQDN until a match is found. If no match is found + /// and the wild card domain is present in the list, then return it as the + /// match. If the wild card domain is the only domain in the list, then + /// it will be returned immediately for any FQDN. + /// + /// @param fqdn is the name for which to look. + /// @param domain receives the matching domain. If no match is found its + /// contents will be unchanged. + /// + /// @return returns true if a match is found, false otherwise. + /// @todo This is a very basic match method, which expects valid FQDNs + /// both as input and for the DdnsDomain::getName(). Currently both are + /// simple strings and there is no normalization (i.e. added trailing dots + /// if missing). + virtual bool matchDomain(const std::string& fqdn, DdnsDomainPtr& domain); + + /// @brief Fetches the manager's name. + /// + /// @return returns a std::string containing the name of the manager. + const std::string getName() const { + return (name_); + } + + /// @brief Returns the number of domains in the domain list. + /// + /// @brief returns an unsigned int containing the domain count. + uint32_t size() const { + return (domains_->size()); + } + + /// @brief Fetches the wild card domain. + /// + /// @return returns a pointer reference to the domain. The pointer will + /// empty if the wild card domain is not present. + const DdnsDomainPtr& getWildcardDomain() { + return (wildcard_domain_); + } + + /// @brief Fetches the domain list. + /// + /// @return returns a pointer reference to the list of domains. + const DdnsDomainMapPtr &getDomains() { + return (domains_); + } + + /// @brief Sets the manger's domain list to the given list of domains. + /// This method will scan the inbound list for the wild card domain and + /// set the internal wild card domain pointer accordingly. + void setDomains(DdnsDomainMapPtr domains); + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const; + +private: + /// @brief An arbitrary label assigned to this manager. + std::string name_; + + /// @brief Map of the domains, keyed by name. + DdnsDomainMapPtr domains_; + + /// @brief Pointer to the wild card domain. + DdnsDomainPtr wildcard_domain_; +}; + +/// @brief Defines a pointer for DdnsDomain instances. +typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr; + +/// @brief Storage container for scalar configuration parameters. +/// +/// This class is useful for implementing parsers for more complex configuration +/// elements (e.g. those of item type "map"). It provides a convenient way to +/// add storage to the parser for an arbitrary number and variety of scalar +/// configuration items (e.g. ints, bools, strings...) without explicitly adding +/// storage for each individual type needed by the parser. +/// +/// This class implements a concrete version of the base class by supplying a +/// "clone" method. +class DScalarContext : public process::ConfigBase { +public: + + /// @brief Constructor + DScalarContext() { + }; + + /// @brief Destructor + virtual ~DScalarContext() { + } + + /// @brief Creates a clone of a DStubContext. + /// + /// @return returns a pointer to the new clone. + virtual process::ConfigPtr clone() { + return (process::ConfigPtr(new DScalarContext(*this))); + } + + /// @brief Unparse a configuration object + /// + /// @return a pointer to a configuration + virtual isc::data::ElementPtr toElement() const { + isc_throw(isc::NotImplemented, "DScalarContext::ElementPtr"); + } + +protected: + /// @brief Copy constructor + DScalarContext(const DScalarContext& rhs) : ConfigBase(rhs) { + } + +private: + /// @brief Private assignment operator, not implemented. + DScalarContext& operator=(const DScalarContext& rhs); +}; + +/// @brief Defines a pointer for DScalarContext instances. +typedef boost::shared_ptr<DScalarContext> DScalarContextPtr; + +/// @brief Parser for TSIGKeyInfo +/// +/// This class parses the configuration element "tsig-key" +/// and creates an instance of a TSIGKeyInfo. +class TSIGKeyInfoParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "tsig-key" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// TSIGKeyInfo, validates those entries, creates a TSIGKeyInfo instance + /// + /// @param key_config is the "tsig-key" configuration to parse + /// + /// @return pointer to the new TSIGKeyInfo instance + TSIGKeyInfoPtr parse(data::ConstElementPtr key_config); + +}; + +/// @brief Parser for a list of TSIGKeyInfos +/// +/// This class parses a list of "tsig-key" configuration elements. +/// The TSIGKeyInfo instances are added to the given storage upon commit. +class TSIGKeyInfoListParser : public data::SimpleParser { +public: + /// @brief Performs the parsing of the given list "tsig-key" elements. + /// + /// Creates an empty TSIGKeyInfoMap + /// + /// Instantiates a TSIGKeyInfoParser + /// It iterates over each key entry in the list: + /// 2. Pass the element configuration to the parser's parse method + /// 3. Add the new TSIGKeyInfo instance to the key map + /// + /// @param key_list_config is the list of "tsig_key" elements to parse. + /// + /// @return a map containing the TSIGKeyInfo instances + TSIGKeyInfoMapPtr parse(data::ConstElementPtr key_list_config); +}; + +/// @brief Parser for DnsServerInfo +/// +/// This class parses the configuration element "dns-server" +/// and creates an instance of a DnsServerInfo. +class DnsServerInfoParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "dns-server" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DnsServerInfo, validates those entries, creates a DnsServerInfo instance + /// and returns it. + /// + /// @param server_config is the "dns-server" configuration to parse + /// + /// @return a pointer to the newly created server instance + /// + /// @throw D2CfgError if: + /// -# hostname is not blank, hostname is not yet supported + /// -# ip_address is invalid + /// -# port is 0 + DnsServerInfoPtr parse(data::ConstElementPtr server_config); +}; + +/// @brief Parser for a list of DnsServerInfos +/// +/// This class parses a list of "dns-server" configuration elements. +/// The DnsServerInfo instances are added +/// to the given storage upon commit. +class DnsServerInfoListParser : public data::SimpleParser{ +public: + /// @brief Performs the actual parsing of the given list "dns-server" + /// elements. + /// + /// Creates an empty server list + /// It iterates over each server entry in the list: + /// 1. Creates a server instance by passing the entry to @c + /// DnsSeverInfoParser::parse() + /// 2. Adds the server to the server list + /// + /// @param server_list_config is the list of "dns-server" elements to parse. + /// @return A pointer to the new, populated server list + DnsServerInfoStoragePtr parse(data::ConstElementPtr server_list_config); +}; + +/// @brief Parser for DdnsDomain +/// +/// This class parses the configuration element "ddns-domain" +/// and creates an instance of a DdnsDomain. +class DdnsDomainParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given "ddns-domain" element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DdnsDomain, validates those entries, and creates a DdnsDomain instance. + /// + /// @param domain_config is the "ddns-domain" configuration to parse + /// @param keys map of defined TSIG keys + /// + /// @return a pointer to the new domain instance + DdnsDomainPtr parse(data::ConstElementPtr domain_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for a list of DdnsDomains +/// +/// This class parses a list of "ddns-domain" configuration elements +/// into a map of DdnsDomains. +class DdnsDomainListParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given list "ddns-domain" + /// elements. + /// Creates a new DdnsDomain map + /// It iterates over each domain entry in the list: + /// 1. Creates a DdnsDomain instance by passing the entry into @c + /// DdnsDomainParser::parser() + /// 2. Adds the DdnsDomain instance to the domain map + /// + /// @param domain_list_config is the list of "ddns-domain" elements to + /// parse. + /// @param keys map of defined TSIG keys + /// @return a pointer to the newly populated domain map + DdnsDomainMapPtr parse(data::ConstElementPtr domain_list_config, + const TSIGKeyInfoMapPtr keys); +}; + +/// @brief Parser for DdnsDomainListMgr +/// +/// This class parses the configuration elements "forward-ddns" and +/// "reverse-ddns". It populates the given DdnsDomainListMgr with parsed +/// information. +class DdnsDomainListMgrParser : public data::SimpleParser { +public: + /// @brief Performs the actual parsing of the given manager element. + /// + /// Parses a configuration for the elements needed to instantiate a + /// DdnsDomainListMgr, validates those entries, then creates a + /// DdnsDomainListMgr. + /// + /// @param mgr_config manager configuration to parse + /// @param mgr_name convenience label for the manager instance + /// @param keys map of defined TSIG keys + /// + /// @return a pointer to the new manager instance + DdnsDomainListMgrPtr parse(data::ConstElementPtr mgr_config, + const std::string& mgr_name, + const TSIGKeyInfoMapPtr keys); +}; + + +}; // end of isc::d2 namespace +}; // end of isc namespace + +#endif // D2_CONFIG_H diff --git a/src/lib/d2srv/d2_log.cc b/src/lib/d2srv/d2_log.cc new file mode 100644 index 0000000000..a56640b879 --- /dev/null +++ b/src/lib/d2srv/d2_log.cc @@ -0,0 +1,23 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Defines the logger used by the top-level component of kea-dhcp-ddns. + +#include <config.h> + +#include <d2srv/d2_log.h> + +namespace isc { +namespace d2 { + +/// @brief Defines the logger used within D2. +isc::log::Logger d2_logger("dhcpddns"); +isc::log::Logger dhcp_to_d2_logger("dhcp-to-d2"); +isc::log::Logger d2_to_dns_logger("d2-to-dns"); + +} // namespace d2 +} // namespace isc + diff --git a/src/lib/d2srv/d2_log.h b/src/lib/d2srv/d2_log.h new file mode 100644 index 0000000000..2b408e07a7 --- /dev/null +++ b/src/lib/d2srv/d2_log.h @@ -0,0 +1,25 @@ +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D2_LOG_H +#define D2_LOG_H + +#include <log/logger_support.h> +#include <log/macros.h> +#include <d2srv/d2_messages.h> + +namespace isc { +namespace d2 { + +/// Define the loggers for the "d2" logging. +extern isc::log::Logger d2_logger; +extern isc::log::Logger dhcp_to_d2_logger; +extern isc::log::Logger d2_to_dns_logger; + +} // namespace d2 +} // namespace isc + +#endif // D2_LOG_H diff --git a/src/lib/d2srv/d2_messages.cc b/src/lib/d2srv/d2_messages.cc new file mode 100644 index 0000000000..117ca0adf1 --- /dev/null +++ b/src/lib/d2srv/d2_messages.cc @@ -0,0 +1,171 @@ +// File created from ../../../src/bin/d2/d2_messages.mes + +#include <cstddef> +#include <log/message_types.h> +#include <log/message_initializer.h> + +namespace isc { +namespace d2 { + +extern const isc::log::MessageID DHCP_DDNS_ADD_FAILED = "DHCP_DDNS_ADD_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_ADD_SUCCEEDED = "DHCP_DDNS_ADD_SUCCEEDED"; +extern const isc::log::MessageID DHCP_DDNS_ALREADY_RUNNING = "DHCP_DDNS_ALREADY_RUNNING"; +extern const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS = "DHCP_DDNS_AT_MAX_TRANSACTIONS"; +extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN = "DHCP_DDNS_CLEARED_FOR_SHUTDOWN"; +extern const isc::log::MessageID DHCP_DDNS_COMMAND = "DHCP_DDNS_COMMAND"; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURE = "DHCP_DDNS_CONFIGURE"; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL = "DHCP_DDNS_CONFIG_CHECK_FAIL"; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL = "DHCP_DDNS_CONFIG_FAIL"; +extern const isc::log::MessageID DHCP_DDNS_FAILED = "DHCP_DDNS_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE = "DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_IO_ERROR = "DHCP_DDNS_FORWARD_ADD_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_REJECTED = "DHCP_DDNS_FORWARD_ADD_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT = "DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR = "DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED = "DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE = "DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_IO_ERROR = "DHCP_DDNS_FORWARD_REPLACE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_REJECTED = "DHCP_DDNS_FORWARD_REPLACE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT = "DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED = "DHCP_DDNS_FWD_REQUEST_IGNORED"; +extern const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE = "DHCP_DDNS_INVALID_RESPONSE"; +extern const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK = "DHCP_DDNS_NOT_ON_LOOPBACK"; +extern const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS = "DHCP_DDNS_NO_ELIGIBLE_JOBS"; +extern const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR = "DHCP_DDNS_NO_FWD_MATCH_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_NO_MATCH = "DHCP_DDNS_NO_MATCH"; +extern const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR = "DHCP_DDNS_NO_REV_MATCH_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_PROCESS_INIT = "DHCP_DDNS_PROCESS_INIT"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL = "DHCP_DDNS_QUEUE_MGR_QUEUE_FULL"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE = "DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING = "DHCP_DDNS_QUEUE_MGR_RECONFIGURING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING = "DHCP_DDNS_QUEUE_MGR_RECOVERING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR = "DHCP_DDNS_QUEUE_MGR_RECV_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR = "DHCP_DDNS_QUEUE_MGR_RESUME_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUMING = "DHCP_DDNS_QUEUE_MGR_RESUMING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED = "DHCP_DDNS_QUEUE_MGR_STARTED"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR = "DHCP_DDNS_QUEUE_MGR_START_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED = "DHCP_DDNS_QUEUE_MGR_STOPPED"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING = "DHCP_DDNS_QUEUE_MGR_STOPPING"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR = "DHCP_DDNS_QUEUE_MGR_STOP_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR = "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP = "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP"; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_FAILED = "DHCP_DDNS_REMOVE_FAILED"; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_SUCCEEDED = "DHCP_DDNS_REMOVE_SUCCEEDED"; +extern const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED = "DHCP_DDNS_REQUEST_DROPPED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE = "DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_IO_ERROR = "DHCP_DDNS_REVERSE_REMOVE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_REJECTED = "DHCP_DDNS_REVERSE_REMOVE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT = "DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS = "DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE = "DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_IO_ERROR = "DHCP_DDNS_REVERSE_REPLACE_IO_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_REJECTED = "DHCP_DDNS_REVERSE_REPLACE_REJECTED"; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT = "DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT"; +extern const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED = "DHCP_DDNS_REV_REQUEST_IGNORED"; +extern const isc::log::MessageID DHCP_DDNS_RUN_EXIT = "DHCP_DDNS_RUN_EXIT"; +extern const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND = "DHCP_DDNS_SHUTDOWN_COMMAND"; +extern const isc::log::MessageID DHCP_DDNS_STARTED = "DHCP_DDNS_STARTED"; +extern const isc::log::MessageID DHCP_DDNS_STARTING_TRANSACTION = "DHCP_DDNS_STARTING_TRANSACTION"; +extern const isc::log::MessageID DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR = "DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_TRANS_SEND_ERROR = "DHCP_DDNS_TRANS_SEND_ERROR"; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_REQUEST_SENT = "DHCP_DDNS_UPDATE_REQUEST_SENT"; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_RESPONSE_RECEIVED = "DHCP_DDNS_UPDATE_RESPONSE_RECEIVED"; + +} // namespace d2 +} // namespace isc + +namespace { + +const char* values[] = { + "DHCP_DDNS_ADD_FAILED", "DHCP_DDNS Request ID %1: Transaction outcome %2", + "DHCP_DDNS_ADD_SUCCEEDED", "DHCP_DDNS Request ID %1: successfully added the DNS mapping addition for this request: %2", + "DHCP_DDNS_ALREADY_RUNNING", "%1 already running? %2", + "DHCP_DDNS_AT_MAX_TRANSACTIONS", "application has %1 queued requests but has reached maximum number of %2 concurrent transactions", + "DHCP_DDNS_CLEARED_FOR_SHUTDOWN", "application has met shutdown criteria for shutdown type: %1", + "DHCP_DDNS_COMMAND", "command directive received, command: %1 - args: %2", + "DHCP_DDNS_CONFIGURE", "configuration %1 received: %2", + "DHCP_DDNS_CONFIG_CHECK_FAIL", "DHCP-DDNS server configuration check failed: %1", + "DHCP_DDNS_CONFIG_FAIL", "DHCP-DDNS server configuration failed: %1", + "DHCP_DDNS_FAILED", "application experienced a fatal error: %1", + "DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while adding a forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE", "DNS Request ID %1: update message to add a forward DNS entry could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_ADD_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping add for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_ADD_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to add the address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while adding forward address mapping for FQDN, %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing a forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE", "DNS Request ID %1: update message to remove a forward DNS Address entry could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping address removal for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove the forward address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward address mapping for FQDN, %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing forward RRs for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE", "DNS Request ID %1: update message to remove forward DNS RR entries could not be constructed for this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward RR removal for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove forward RR entries for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward RRs for FQDN, %3", + "DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing forward address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE", "DNS Request ID %1: update message to replace a forward DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_FORWARD_REPLACE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping replace for FQDN %2 to DNS server %3", + "DHCP_DDNS_FORWARD_REPLACE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to replace the address mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing forward address mapping for FQDN, %3", + "DHCP_DDNS_FWD_REQUEST_IGNORED", "Request ID %1: Forward updates are disabled, the forward portion of request will be ignored: %2", + "DHCP_DDNS_INVALID_RESPONSE", "received response to DNS Update message is malformed: %1", + "DHCP_DDNS_NOT_ON_LOOPBACK", "the DHCP-DDNS server has been configured to listen on %1 which is not the local loopback. This is an insecure configuration supported for testing purposes only", + "DHCP_DDNS_NO_ELIGIBLE_JOBS", "although there are queued requests, there are pending transactions for each, Queue count: %1 Transaction count: %2", + "DHCP_DDNS_NO_FWD_MATCH_ERROR", "Request ID %1: the configured list of forward DDNS domains does not contain a match for: %2 The request has been discarded.", + "DHCP_DDNS_NO_MATCH", "No DNS servers match FQDN %1", + "DHCP_DDNS_NO_REV_MATCH_ERROR", "Request ID %1: the configured list of reverse DDNS domains does not contain a match for: %2 The request has been discarded.", + "DHCP_DDNS_PROCESS_INIT", "application init invoked", + "DHCP_DDNS_QUEUE_MGR_QUEUE_FULL", "application request queue has reached maximum number of entries %1", + "DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE", "Request ID %1: received and queued a request.", + "DHCP_DDNS_QUEUE_MGR_RECONFIGURING", "application is reconfiguring the queue manager", + "DHCP_DDNS_QUEUE_MGR_RECOVERING", "application is attempting to recover from a queue manager IO error", + "DHCP_DDNS_QUEUE_MGR_RECV_ERROR", "application's queue manager was notified of a request receive error by its listener.", + "DHCP_DDNS_QUEUE_MGR_RESUME_ERROR", "application could not restart the queue manager, reason: %1", + "DHCP_DDNS_QUEUE_MGR_RESUMING", "application is resuming listening for requests now that the request queue size has reached %1 of a maximum %2 allowed", + "DHCP_DDNS_QUEUE_MGR_STARTED", "application's queue manager has begun listening for requests.", + "DHCP_DDNS_QUEUE_MGR_START_ERROR", "application could not start the queue manager, reason: %1", + "DHCP_DDNS_QUEUE_MGR_STOPPED", "application's queue manager has stopped listening for requests.", + "DHCP_DDNS_QUEUE_MGR_STOPPING", "application is stopping the queue manager for %1", + "DHCP_DDNS_QUEUE_MGR_STOP_ERROR", "application encountered an error stopping the queue manager: %1", + "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR", "application's queue manager request receive handler experienced an unexpected exception %1:", + "DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP", "application's queue manager receive was", + "DHCP_DDNS_REMOVE_FAILED", "DHCP_DDNS Request ID %1: Transaction outcome: %2", + "DHCP_DDNS_REMOVE_SUCCEEDED", "DHCP_DDNS Request ID %1: successfully removed the DNS mapping addition for this request: %2", + "DHCP_DDNS_REQUEST_DROPPED", "Request ID %1: Request contains no enabled update requests and will be dropped: %2", + "DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing reverse address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE", "DNS Request ID %1: update message to remove a reverse DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_REVERSE_REMOVE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping remove for FQDN %2 to DNS server %3", + "DHCP_DDNS_REVERSE_REMOVE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to remove the reverse mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing reverse address mapping for FQDN, %3", + "DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS", "DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing reverse address mapping for FQDN %3 to DNS server %4", + "DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE", "DNS Request ID %1: update message to replace a reverse DNS entry could not be constructed from this request: %2, reason: %3", + "DHCP_DDNS_REVERSE_REPLACE_IO_ERROR", "DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping replacement for FQDN %2 to DNS server %3", + "DHCP_DDNS_REVERSE_REPLACE_REJECTED", "DNS Request ID %1: Server, %2, rejected a DNS update request to replace the reverse mapping for FQDN, %3, with an RCODE: %4", + "DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT", "DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing reverse address mapping for FQDN, %3", + "DHCP_DDNS_REV_REQUEST_IGNORED", "Request ID %1: Reverse updates are disabled, the reverse portion of request will be ignored: %2", + "DHCP_DDNS_RUN_EXIT", "application is exiting the event loop", + "DHCP_DDNS_SHUTDOWN_COMMAND", "application received shutdown command with args: %1", + "DHCP_DDNS_STARTED", "Kea DHCP-DDNS server version %1 started", + "DHCP_DDNS_STARTING_TRANSACTION", "Request ID %1:", + "DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR", "Request ID %1: application encountered an unexpected error while carrying out a NameChangeRequest: %2", + "DHCP_DDNS_TRANS_SEND_ERROR", "Request ID %1: application encountered an unexpected error while attempting to send a DNS update: %2", + "DHCP_DDNS_UPDATE_REQUEST_SENT", "Request ID %1: %2 to server: %3", + "DHCP_DDNS_UPDATE_RESPONSE_RECEIVED", "Request ID %1: to server: %2 status: %3", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/lib/d2srv/d2_messages.h b/src/lib/d2srv/d2_messages.h new file mode 100644 index 0000000000..5b76287c04 --- /dev/null +++ b/src/lib/d2srv/d2_messages.h @@ -0,0 +1,89 @@ +// File created from ../../../src/bin/d2/d2_messages.mes + +#ifndef D2_MESSAGES_H +#define D2_MESSAGES_H + +#include <log/message_types.h> + +namespace isc { +namespace d2 { + +extern const isc::log::MessageID DHCP_DDNS_ADD_FAILED; +extern const isc::log::MessageID DHCP_DDNS_ADD_SUCCEEDED; +extern const isc::log::MessageID DHCP_DDNS_ALREADY_RUNNING; +extern const isc::log::MessageID DHCP_DDNS_AT_MAX_TRANSACTIONS; +extern const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN; +extern const isc::log::MessageID DHCP_DDNS_COMMAND; +extern const isc::log::MessageID DHCP_DDNS_CONFIGURE; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_CHECK_FAIL; +extern const isc::log::MessageID DHCP_DDNS_CONFIG_FAIL; +extern const isc::log::MessageID DHCP_DDNS_FAILED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_FWD_REQUEST_IGNORED; +extern const isc::log::MessageID DHCP_DDNS_INVALID_RESPONSE; +extern const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK; +extern const isc::log::MessageID DHCP_DDNS_NO_ELIGIBLE_JOBS; +extern const isc::log::MessageID DHCP_DDNS_NO_FWD_MATCH_ERROR; +extern const isc::log::MessageID DHCP_DDNS_NO_MATCH; +extern const isc::log::MessageID DHCP_DDNS_NO_REV_MATCH_ERROR; +extern const isc::log::MessageID DHCP_DDNS_PROCESS_INIT; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_FULL; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECV_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUMING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STARTED; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPED; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR; +extern const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_FAILED; +extern const isc::log::MessageID DHCP_DDNS_REMOVE_SUCCEEDED; +extern const isc::log::MessageID DHCP_DDNS_REQUEST_DROPPED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_IO_ERROR; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_REJECTED; +extern const isc::log::MessageID DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT; +extern const isc::log::MessageID DHCP_DDNS_REV_REQUEST_IGNORED; +extern const isc::log::MessageID DHCP_DDNS_RUN_EXIT; +extern const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND; +extern const isc::log::MessageID DHCP_DDNS_STARTED; +extern const isc::log::MessageID DHCP_DDNS_STARTING_TRANSACTION; +extern const isc::log::MessageID DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR; +extern const isc::log::MessageID DHCP_DDNS_TRANS_SEND_ERROR; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_REQUEST_SENT; +extern const isc::log::MessageID DHCP_DDNS_UPDATE_RESPONSE_RECEIVED; + +} // namespace d2 +} // namespace isc + +#endif // D2_MESSAGES_H diff --git a/src/lib/d2srv/d2_messages.mes b/src/lib/d2srv/d2_messages.mes new file mode 100644 index 0000000000..4b3d47dd0a --- /dev/null +++ b/src/lib/d2srv/d2_messages.mes @@ -0,0 +1,396 @@ +# Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$NAMESPACE isc::d2 + +% DHCP_DDNS_ADD_FAILED DHCP_DDNS Request ID %1: Transaction outcome %2 +This is an error message issued after DHCP_DDNS attempts to submit DNS mapping +entry additions have failed. The precise reason for the failure should be +documented in preceding log entries. + +% DHCP_DDNS_ADD_SUCCEEDED DHCP_DDNS Request ID %1: successfully added the DNS mapping addition for this request: %2 +This is an informational message issued after DHCP_DDNS has submitted DNS +mapping additions which were received and accepted by an appropriate DNS server. + +% DHCP_DDNS_ALREADY_RUNNING %1 already running? %2 +This is an error message that occurs when DHCP_DDNS encounters a pre-existing +PID file which contains the PID of a running process. This most likely +indicates an attempt to start a second instance of DHCP_DDNS using the +same configuration file. It is possible, though unlikely, that the PID file +is a remnant left behind by a server crash or power failure and the PID +it contains refers to a process other than DHCP_DDNS. In such an event, +it would be necessary to manually remove the PID file. The first argument is +the DHCP_DDNS process name, the second contains the PID and PID file. + +% DHCP_DDNS_AT_MAX_TRANSACTIONS application has %1 queued requests but has reached maximum number of %2 concurrent transactions +This is a debug message that indicates that the application has DHCP_DDNS +requests in the queue but is working as many concurrent requests as allowed. + +% DHCP_DDNS_CLEARED_FOR_SHUTDOWN application has met shutdown criteria for shutdown type: %1 +This is a debug message issued when the application has been instructed +to shutdown and has met the required criteria to exit. + +% DHCP_DDNS_COMMAND command directive received, command: %1 - args: %2 +This is a debug message issued when the DHCP-DDNS application command method +has been invoked. + +% DHCP_DDNS_CONFIGURE configuration %1 received: %2 +This is a debug message issued when the DHCP-DDNS application configure method +has been invoked. + +% DHCP_DDNS_CONFIG_CHECK_FAIL DHCP-DDNS server configuration check failed: %1 +This error message indicates that the DHCP-DDNS had failed configuration +check. Details are provided. Additional details may be available +in earlier log entries, possibly on lower levels. + +% DHCP_DDNS_CONFIG_FAIL DHCP-DDNS server configuration failed: %1 +This error message indicates that the DHCP-DDNS had failed configuration +attempt. Details are provided. Additional details may be available +in earlier log entries, possibly on lower levels. + +% DHCP_DDNS_FAILED application experienced a fatal error: %1 +This is a debug message issued when the DHCP-DDNS application encounters an +unrecoverable error from within the event loop. + +% DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while adding a forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was adding a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE DNS Request ID %1: update message to add a forward DNS entry could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address addition. This is due +to invalid data contained in the NameChangeRequest. The request will be aborted. +This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_ADD_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping add for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_ADD_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to add the address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while adding forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to add a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing a forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_BUILD_FAILURE DNS Request ID %1: update message to remove a forward DNS Address entry could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address (A or AAAA) removal. This +is due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. +/*sar*/ + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping address removal for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address remove. The application will retry +against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove the forward address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REMOVE_ADDRS_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing forward RRs for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing forward RRs. The request will be aborted. This is +most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_BUILD_FAILURE DNS Request ID %1: update message to remove forward DNS RR entries could not be constructed for this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting forward RR (DHCID RR) removal. This is due +to invalid data contained in the NameChangeRequest. The request will be aborted. +This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward RR removal for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward RR remove. The application will retry +against the same server. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove forward RR entries for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REMOVE_RRS_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing forward RRs for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove forward RRs mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. +/*sar*/ + +% DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing forward address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was replacing a forward address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE DNS Request ID %1: update message to replace a forward DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a forward address replacement. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_FORWARD_REPLACE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a forward mapping replace for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a forward address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_FORWARD_REPLACE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to replace the address mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing forward address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to replace a forward address mapping, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_FWD_REQUEST_IGNORED Request ID %1: Forward updates are disabled, the forward portion of request will be ignored: %2 +This is a debug message issued when forward DNS updates are disabled and +DHCP_DDNS receives an update request containing a forward DNS update. The +forward update will not performed. + +% DHCP_DDNS_INVALID_RESPONSE received response to DNS Update message is malformed: %1 +This is a debug message issued when the DHCP-DDNS application encountered an +error while decoding a response to DNS Update message. Typically, this error +will be encountered when a response message is malformed. + +% DHCP_DDNS_NOT_ON_LOOPBACK the DHCP-DDNS server has been configured to listen on %1 which is not the local loopback. This is an insecure configuration supported for testing purposes only +This is a warning message issued when the DHCP-DDNS server is configured to +listen at an address other than the loopback address (127.0.0.1 or ::1). It is +possible for a malicious attacker to send bogus NameChangeRequests to it and +change entries in the DNS. For this reason, addresses other than the IPv4 or +IPv6 loopback addresses should only be used for testing purposes. A future +version of Kea will implement authentication to guard against such attacks. + +% DHCP_DDNS_NO_ELIGIBLE_JOBS although there are queued requests, there are pending transactions for each, Queue count: %1 Transaction count: %2 +This is a debug message issued when all of the queued requests represent clients +for which there is an update already in progress. This may occur under +normal operations but should be temporary situation. + +% DHCP_DDNS_NO_FWD_MATCH_ERROR Request ID %1: the configured list of forward DDNS domains does not contain a match for: %2 The request has been discarded. +This is an error message that indicates that DHCP_DDNS received a request to +update the forward DNS information for the given FQDN but for which there are +no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS +configuration needs to be updated or the source of the FQDN itself should be +investigated. + +% DHCP_DDNS_NO_MATCH No DNS servers match FQDN %1 +This is warning message issued when there are no domains in the configuration +which match the cited fully qualified domain name (FQDN). The DNS Update +request for the FQDN cannot be processed. + +% DHCP_DDNS_NO_REV_MATCH_ERROR Request ID %1: the configured list of reverse DDNS domains does not contain a match for: %2 The request has been discarded. +This is an error message that indicates that DHCP_DDNS received a request to +update the reverse DNS information for the given FQDN but for which there are +no configured DDNS domains in the DHCP_DDNS configuration. Either the DHCP_DDNS +configuration needs to be updated or the source of the FQDN itself should be +investigated. + +% DHCP_DDNS_PROCESS_INIT application init invoked +This is a debug message issued when the DHCP-DDNS application enters +its initialization method. + +% DHCP_DDNS_QUEUE_MGR_QUEUE_FULL application request queue has reached maximum number of entries %1 +This an error message indicating that DHCP-DDNS is receiving DNS update +requests faster than they can be processed. This may mean the maximum queue +needs to be increased, the DHCP-DDNS clients are simply generating too many +requests too quickly, or perhaps upstream DNS servers are experiencing +load issues. + +% DHCP_DDNS_QUEUE_MGR_QUEUE_RECEIVE Request ID %1: received and queued a request. +This is an informational message indicating that the NameChangeRequest listener used +by DHCP-DDNS to receive a request has received a request and queued it for further +processing. + +% DHCP_DDNS_QUEUE_MGR_RECONFIGURING application is reconfiguring the queue manager +This is an informational message indicating that DHCP_DDNS is reconfiguring the queue manager as part of normal startup or in response to a new configuration. + +% DHCP_DDNS_QUEUE_MGR_RECOVERING application is attempting to recover from a queue manager IO error +This is an informational message indicating that DHCP_DDNS is attempting to +restart the queue manager after it suffered an IO error while receiving +requests. + +% DHCP_DDNS_QUEUE_MGR_RECV_ERROR application's queue manager was notified of a request receive error by its listener. +This is an error message indicating that the NameChangeRequest listener used by +DHCP-DDNS to receive requests encountered an IO error. There should be +corresponding log messages from the listener layer with more details. This may +indicate a network connectivity or system resource issue. + +% DHCP_DDNS_QUEUE_MGR_RESUME_ERROR application could not restart the queue manager, reason: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager could not +be restarted after stopping due to a full receive queue. This means that +the application cannot receive requests. This is most likely due to DHCP_DDNS +configuration parameters referring to resources such as an IP address or port, +that is no longer unavailable. DHCP_DDNS will attempt to restart the queue +manager if given a new configuration. + +% DHCP_DDNS_QUEUE_MGR_RESUMING application is resuming listening for requests now that the request queue size has reached %1 of a maximum %2 allowed +This is an informational message indicating that DHCP_DDNS, which had stopped +accepting new requests, has processed enough entries from the receive queue to +resume accepting requests. + +% DHCP_DDNS_QUEUE_MGR_STARTED application's queue manager has begun listening for requests. +This is a debug message indicating that DHCP_DDNS's Queue Manager has +successfully started and is now listening for NameChangeRequests. + +% DHCP_DDNS_QUEUE_MGR_START_ERROR application could not start the queue manager, reason: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager could not +be started. This means that the application cannot receive requests. This is +most likely due to DHCP_DDNS configuration parameters referring to resources +such as an IP address or port, that are unavailable. DHCP_DDNS will attempt to +restart the queue manager if given a new configuration. + +% DHCP_DDNS_QUEUE_MGR_STOPPED application's queue manager has stopped listening for requests. +This is a debug message indicating that DHCP_DDNS's Queue Manager has +stopped listening for NameChangeRequests. This may be because of normal event +such as reconfiguration or as a result of an error. There should be log +messages preceding this one to indicate why it has stopped. + +% DHCP_DDNS_QUEUE_MGR_STOPPING application is stopping the queue manager for %1 +This is an informational message indicating that DHCP_DDNS is stopping the +queue manager either to reconfigure it or as part of application shutdown. + +% DHCP_DDNS_QUEUE_MGR_STOP_ERROR application encountered an error stopping the queue manager: %1 +This is an error message indicating that DHCP_DDNS encountered an error while +trying to stop the queue manager. This error is unlikely to occur or to +impair the application's ability to function but it should be reported for +analysis. + +% DHCP_DDNS_QUEUE_MGR_UNEXPECTED_HANDLER_ERROR application's queue manager request receive handler experienced an unexpected exception %1: +This is an error message indicating that an unexpected error occurred within the +DHCP_DDNS's Queue Manager request receive completion handler. This is most +likely a programmatic issue that should be reported. The application may +recover on its own. + +% DHCP_DDNS_QUEUE_MGR_UNEXPECTED_STOP application's queue manager receive was +aborted unexpectedly while queue manager state is: %1 +This is an error message indicating that DHCP_DDNS's Queue Manager request +receive was unexpected interrupted. Normally, the read is receive is only +interrupted as a normal part of stopping the queue manager. This is most +likely a programmatic issue that should be reported. + +% DHCP_DDNS_REMOVE_FAILED DHCP_DDNS Request ID %1: Transaction outcome: %2 +This is an error message issued after DHCP_DDNS attempts to submit DNS mapping +entry removals have failed. The precise reason for the failure should be +documented in preceding log entries. + +% DHCP_DDNS_REMOVE_SUCCEEDED DHCP_DDNS Request ID %1: successfully removed the DNS mapping addition for this request: %2 +This is an informational message issued after DHCP_DDNS has submitted DNS +mapping removals which were received and accepted by an appropriate DNS server. + +% DHCP_DDNS_REQUEST_DROPPED Request ID %1: Request contains no enabled update requests and will be dropped: %2 +This is a debug message issued when DHCP_DDNS receives a request which does not +contain updates in a direction that is enabled. In other words, if only forward +updates are enabled and request is received that asks only for reverse updates +then the request is dropped. + +% DHCP_DDNS_REVERSE_REMOVE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while removing reverse address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was removing a reverse address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_REVERSE_REMOVE_BUILD_FAILURE DNS Request ID %1: update message to remove a reverse DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a reverse PTR removal. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_REVERSE_REMOVE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping remove for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a reverse address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REMOVE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to remove the reverse mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_REVERSE_REMOVE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while removing reverse address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to remove a reverse address, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS DHCP_DDNS Request ID %1: received an unknown DNSClient status: %2, while replacing reverse address mapping for FQDN %3 to DNS server %4 +This is an error message issued when DNSClient returns an unrecognized status +while DHCP_DDNS was replacing a reverse address mapping. The request will be +aborted. This is most likely a programmatic issue and should be reported. + +% DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE DNS Request ID %1: update message to replace a reverse DNS entry could not be constructed from this request: %2, reason: %3 +This is an error message issued when an error occurs attempting to construct +the server bound packet requesting a reverse PTR replacement. This is +due to invalid data contained in the NameChangeRequest. The request will be +aborted. This is most likely a configuration issue. + +% DHCP_DDNS_REVERSE_REPLACE_IO_ERROR DHCP_DDNS Request ID %1: encountered an IO error sending a reverse mapping replacement for FQDN %2 to DNS server %3 +This is an error message issued when a communication error occurs while +DHCP_DDNS is carrying out a reverse address update. The application will +retry against the same server or others as appropriate. + +% DHCP_DDNS_REVERSE_REPLACE_REJECTED DNS Request ID %1: Server, %2, rejected a DNS update request to replace the reverse mapping for FQDN, %3, with an RCODE: %4 +This is an error message issued when an update was rejected by the DNS server +it was sent to for the reason given by the RCODE. The rcode values are defined +in RFC 2136. + +% DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT DHCP_DDNS Request ID %1: received a corrupt response from the DNS server, %2, while replacing reverse address mapping for FQDN, %3 +This is an error message issued when the response received by DHCP_DDNS, to a +update request to replace a reverse address, is mangled or malformed. +The application will retry against the same server or others as appropriate. + +% DHCP_DDNS_REV_REQUEST_IGNORED Request ID %1: Reverse updates are disabled, the reverse portion of request will be ignored: %2 +This is a debug message issued when reverse DNS updates are disabled and +DHCP_DDNS receives an update request containing a reverse DNS update. The +reverse update will not performed. + +% DHCP_DDNS_RUN_EXIT application is exiting the event loop +This is a debug message issued when the DHCP-DDNS server exits its +event lo + +% DHCP_DDNS_SHUTDOWN_COMMAND application received shutdown command with args: %1 +This is a debug message issued when the application has been instructed +to shut down by the controller. + +% DHCP_DDNS_STARTED Kea DHCP-DDNS server version %1 started +This informational message indicates that the DHCP-DDNS server has +processed all configuration information and is ready to begin processing. +The version is also printed. + +% DHCP_DDNS_STARTING_TRANSACTION Request ID %1: +This is a debug message issued when DHCP-DDNS has begun a transaction for +a given request. + +% DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR Request ID %1: application encountered an unexpected error while carrying out a NameChangeRequest: %2 +This is error message issued when the application fails to process a +NameChangeRequest correctly. Some or all of the DNS updates requested as part +of this update did not succeed. This is a programmatic error and should be +reported. + +% DHCP_DDNS_TRANS_SEND_ERROR Request ID %1: application encountered an unexpected error while attempting to send a DNS update: %2 +This is error message issued when the application is able to construct an update +message but the attempt to send it suffered an unexpected error. This is most +likely a programmatic error, rather than a communications issue. Some or all +of the DNS updates requested as part of this request did not succeed. + +% DHCP_DDNS_UPDATE_REQUEST_SENT Request ID %1: %2 to server: %3 +This is a debug message issued when DHCP_DDNS sends a DNS request to a DNS +server. + +% DHCP_DDNS_UPDATE_RESPONSE_RECEIVED Request ID %1: to server: %2 status: %3 +This is a debug message issued when DHCP_DDNS receives sends a DNS update +response from a DNS server. diff --git a/src/lib/d2srv/d2_simple_parser.cc b/src/lib/d2srv/d2_simple_parser.cc new file mode 100644 index 0000000000..15cfd67ab2 --- /dev/null +++ b/src/lib/d2srv/d2_simple_parser.cc @@ -0,0 +1,309 @@ +// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <d2srv/d2_config.h> +#include <d2srv/d2_simple_parser.h> +#include <cc/data.h> +#include <hooks/hooks_manager.h> +#include <hooks/hooks_parser.h> +#include <boost/foreach.hpp> + +using namespace isc::data; +using namespace isc::d2; +using namespace isc; + +namespace { + +dhcp_ddns::NameChangeProtocol +getProtocol(ConstElementPtr map, const std::string& name) { + ConstElementPtr value = map->get(name); + if (!value) { + isc_throw(D2CfgError, "Mandatory parameter " << name + << " not found (" << map->getPosition() << ")"); + } + std::string str = value->stringValue(); + try { + return (dhcp_ddns::stringToNcrProtocol(str)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, + "invalid NameChangeRequest protocol (" << str + << ") specified for parameter '" << name + << "' (" << value->getPosition() << ")"); + } +} + +dhcp_ddns::NameChangeFormat +getFormat(ConstElementPtr map, const std::string& name) { + ConstElementPtr value = map->get(name); + if (!value) { + isc_throw(D2CfgError, "Mandatory parameter " << name + << " not found (" << map->getPosition() << ")"); + } + std::string str = value->stringValue(); + try { + return (dhcp_ddns::stringToNcrFormat(str)); + } catch (const std::exception& ex) { + isc_throw(D2CfgError, + "invalid NameChangeRequest format (" << str + << ") specified for parameter '" << name + << "' (" << value->getPosition() << ")"); + } +} + +} // anon + +namespace isc { +namespace d2 { +/// @brief This sets of arrays define the default values and +/// values inherited (derived) between various scopes. +/// +/// Each of those is documented in @file d2_simple_parser.cc. This +/// is different than most other comments in Kea code. The reason +/// for placing those in .cc rather than .h file is that it +/// is expected to be one centralized place to look at for +/// the default values. This is expected to be looked at also by +/// people who are not skilled in C or C++, so they may be +/// confused with the differences between declaration and definition. +/// As such, there's one file to look at that hopefully is readable +/// without any C or C++ skills. +/// +/// @{ + +/// @brief This table defines default global values for D2 +/// +/// Some of the global parameters defined in the global scope (i.e. directly +/// in DhcpDdns) are optional. If not defined, the following values will be +/// used. +const SimpleDefaults D2SimpleParser::D2_GLOBAL_DEFAULTS = { + { "ip-address", Element::string, "127.0.0.1" }, + { "port", Element::integer, "53001" }, + { "dns-server-timeout", Element::integer, "100" }, // in seconds + { "ncr-protocol", Element::string, "UDP" }, + { "ncr-format", Element::string, "JSON" } +}; + +/// Supplies defaults for ddns-domains list elements (i.e. DdnsDomains) +const SimpleDefaults D2SimpleParser::TSIG_KEY_DEFAULTS = { + { "digest-bits", Element::integer, "0" } +}; + +/// Supplies defaults for optional values in DDNS domain managers +/// (e.g. "forward-ddns" and "reverse-ddns"). +/// @note While there are none yet defined, it is highly likely +/// there will be domain manager defaults added in the future. +/// This code to set defaults already uses this list, so supporting +/// values will simply require adding them to this list. +const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_MGR_DEFAULTS = { +}; + +/// Supplies defaults for ddns-domains list elements (i.e. DdnsDomains) +const SimpleDefaults D2SimpleParser::DDNS_DOMAIN_DEFAULTS = { + { "key-name", Element::string, "" } +}; + +/// Supplies defaults for optional values DdnsDomain entries. +const SimpleDefaults D2SimpleParser::DNS_SERVER_DEFAULTS = { + { "hostname", Element::string, "" }, + { "port", Element::integer, "53" }, +}; + +/// @} + +/// --------------------------------------------------------------------------- +/// --- end of default values ------------------------------------------------- +/// --------------------------------------------------------------------------- + +size_t +D2SimpleParser::setAllDefaults(isc::data::ElementPtr global) { + size_t cnt = 0; + // Set global defaults first. + cnt = setDefaults(global, D2_GLOBAL_DEFAULTS); + + // If the key list is present, set its members' defaults + if (global->find("tsig-keys")) { + ConstElementPtr keys = global->get("tsig-keys"); + cnt += setListDefaults(keys, TSIG_KEY_DEFAULTS); + } else { + // Not present, so add an empty list. + ConstElementPtr list(new ListElement()); + global->set("tsig-keys", list); + cnt++; + } + + // Set the forward domain manager defaults. + cnt += setManagerDefaults(global, "forward-ddns", DDNS_DOMAIN_MGR_DEFAULTS); + + // Set the reverse domain manager defaults. + cnt += setManagerDefaults(global, "reverse-ddns", DDNS_DOMAIN_MGR_DEFAULTS); + return (cnt); +} + +size_t +D2SimpleParser::setDdnsDomainDefaults(ElementPtr domain, + const SimpleDefaults& domain_defaults) { + size_t cnt = 0; + + // Set the domain's scalar defaults + cnt += setDefaults(domain, domain_defaults); + if (domain->find("dns-servers")) { + // Now add the defaults to its server list. + ConstElementPtr servers = domain->get("dns-servers"); + cnt += setListDefaults(servers, DNS_SERVER_DEFAULTS); + } + + return (cnt); +} + + +size_t +D2SimpleParser::setManagerDefaults(ElementPtr global, + const std::string& mgr_name, + const SimpleDefaults& mgr_defaults) { + size_t cnt = 0; + + if (!global->find(mgr_name)) { + // If it's not present, then default is an empty map + ConstElementPtr map(new MapElement()); + global->set(mgr_name, map); + ++cnt; + } else { + // Get a writable copy of the manager element map + ElementPtr mgr = + boost::const_pointer_cast<Element>(global->get(mgr_name)); + + // Set the manager's scalar defaults first + cnt += setDefaults(mgr, mgr_defaults); + + // Get the domain list and set defaults for them. + // The domain list may not be present ddns for this + // manager is disabled. + if (mgr->find("ddns-domains")) { + ConstElementPtr domains = mgr->get("ddns-domains"); + BOOST_FOREACH(ElementPtr domain, domains->listValue()) { + // Set the domain's defaults. We can't use setListDefaults() + // as this does not handle sub-lists or maps, like server list. + cnt += setDdnsDomainDefaults(domain, DDNS_DOMAIN_DEFAULTS); + } + } + + } + + return (cnt); +} + +void D2SimpleParser::parse(const D2CfgContextPtr& ctx, + const isc::data::ConstElementPtr& config, + bool check_only) { + // TSIG keys need to parse before the Domains, so we can catch Domains + // that specify undefined keys. Create the necessary parsing order now. + // addToParseOrder("tsig-keys"); + // addToParseOrder("forward-ddns"); + // addToParseOrder("reverse-ddns"); + + ConstElementPtr keys = config->get("tsig-keys"); + if (keys) { + TSIGKeyInfoListParser parser; + ctx->setKeys(parser.parse(keys)); + } + + ConstElementPtr fwd = config->get("forward-ddns"); + if (fwd) { + DdnsDomainListMgrParser parser; + DdnsDomainListMgrPtr mgr = parser.parse(fwd, "forward-ddns", + ctx->getKeys()); + ctx->setForwardMgr(mgr); + } + + ConstElementPtr rev = config->get("reverse-ddns"); + if (rev) { + DdnsDomainListMgrParser parser; + DdnsDomainListMgrPtr mgr = parser.parse(rev, "reverse-ddns", + ctx->getKeys()); + ctx->setReverseMgr(mgr); + } + + // Fetch the parameters in the config, performing any logical + // validation required. + asiolink::IOAddress ip_address(0); + uint32_t port = 0; + uint32_t dns_server_timeout = 0; + dhcp_ddns::NameChangeProtocol ncr_protocol = dhcp_ddns::NCR_UDP; + dhcp_ddns::NameChangeFormat ncr_format = dhcp_ddns::FMT_JSON; + + ip_address = SimpleParser::getAddress(config, "ip-address"); + + if ((ip_address.toText() == "0.0.0.0") || + (ip_address.toText() == "::")) { + isc_throw(D2CfgError, "IP address cannot be \"" + << ip_address << "\"" + << " (" << config->get("ip-address")->getPosition() << ")"); + } + + port = SimpleParser::getUint32(config, "port"); + + dns_server_timeout = SimpleParser::getUint32(config, "dns-server-timeout"); + + ncr_protocol = getProtocol(config, "ncr-protocol"); + if (ncr_protocol != dhcp_ddns::NCR_UDP) { + isc_throw(D2CfgError, "ncr-protocol : " + << dhcp_ddns::ncrProtocolToString(ncr_protocol) + << " is not yet supported (" + << config->get("ncr-protocol")->getPosition() << ")"); + } + + ncr_format = getFormat(config, "ncr-format"); + if (ncr_format != dhcp_ddns::FMT_JSON) { + isc_throw(D2CfgError, "NCR Format:" + << dhcp_ddns::ncrFormatToString(ncr_format) + << " is not yet supported" + << " (" << config->get("ncr-format")->getPosition() << ")"); + } + + ConstElementPtr user = config->get("user-context"); + if (user) { + ctx->setContext(user); + } + + ConstElementPtr socket = config->get("control-socket"); + if (socket) { + if (socket->getType() != Element::map) { + isc_throw(D2CfgError, "Specified control-socket is expected to be a map" + ", i.e. a structure defined within { }"); + } + ctx->setControlSocketInfo(socket); + } + + // Finally, let's get the hook libs! + using namespace isc::hooks; + HooksConfig& libraries = ctx->getHooksConfig(); + ConstElementPtr hooks = config->get("hooks-libraries"); + if (hooks) { + HooksLibrariesParser hooks_parser; + hooks_parser.parse(libraries, hooks); + libraries.verifyLibraries(hooks->getPosition()); + } + + // Attempt to create the new client config. This ought to fly as + // we already validated everything. + D2ParamsPtr params(new D2Params(ip_address, port, dns_server_timeout, + ncr_protocol, ncr_format)); + + ctx->getD2Params() = params; + + if (!check_only) { + // This occurs last as if it succeeds, there is no easy way + // revert it. As a result, the failure to commit a subsequent + // change causes problems when trying to roll back. + HooksManager::prepareUnloadLibraries(); + static_cast<void>(HooksManager::unloadLibraries()); + libraries.loadLibraries(); + } +} + +} +} diff --git a/src/lib/d2srv/d2_simple_parser.h b/src/lib/d2srv/d2_simple_parser.h new file mode 100644 index 0000000000..49f0d8a4ba --- /dev/null +++ b/src/lib/d2srv/d2_simple_parser.h @@ -0,0 +1,96 @@ +// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef D2_SIMPLE_PARSER_H +#define D2_SIMPLE_PARSER_H + +#include <cc/simple_parser.h> +#include <d2srv/d2_cfg_mgr.h> + +namespace isc { +namespace d2 { + +/// @brief SimpleParser specialized for D2 +/// +/// This class is a @ref isc::data::SimpleParser dedicated to D2. +/// In particular, it contains all the default values and names of the +/// parameters that are to be derived (inherited) between scopes. +/// For the actual values, see @file d2_simple_parser.cc +class D2SimpleParser : public data::SimpleParser { +public: + + /// @brief Sets all defaults for D2 configuration + /// + /// This method sets global and element defaults. + /// + /// @param global scope to be filled in with defaults. + /// @return number of default values added + static size_t setAllDefaults(data::ElementPtr global); + + // see d2_simple_parser.cc for comments for those parameters + static const data::SimpleDefaults D2_GLOBAL_DEFAULTS; + + // Defaults for tsig-keys list elements, TSIGKeyInfos + static const data::SimpleDefaults TSIG_KEY_DEFAULTS; + + // Defaults for <forward|reverse>-ddns elements, DdnsDomainListMgrs + static const data::SimpleDefaults DDNS_DOMAIN_MGR_DEFAULTS; + + // Defaults for ddns-domains list elements, DdnsDomains + static const data::SimpleDefaults DDNS_DOMAIN_DEFAULTS; + + // Defaults for dns-servers list elements, DnsServerInfos + static const data::SimpleDefaults DNS_SERVER_DEFAULTS; + + /// @brief Adds default values to a DDNS Domain element + /// + /// Adds the scalar default values to the given DDNS domain + /// element, and then adds the DNS Server defaults to the domain's + /// server list, "dns-servers". + /// + /// @param domain DDNS domain element to which defaults should be added + /// @param domain_defaults list of default values from which to add + /// @return returns the number of default values added + static size_t setDdnsDomainDefaults(data::ElementPtr domain, + const data::SimpleDefaults& + domain_defaults); + + /// @brief Adds default values to a DDNS Domain List Manager + /// + /// This function looks for the named DDNS domain manager element within + /// the given element tree. If it is found, it adds the scalar default + /// values to the manager element and then adds the DDNS Domain defaults + /// to its domain list, "ddns-domains". If the manager element is not + /// found, then an empty map entry is added for it, thus defaulting the + /// manager to "disabled". + /// + /// @param global element tree containing the DDNS domain manager element + /// to which defaults should be + /// added + /// @param mgr_name name of the manager element within the element tree + /// (e.g. "forward-ddns", "reverse-ddns") + /// @param mgr_defaults list of default values from which to add + /// @return returns the number of default values added + static size_t setManagerDefaults(data::ElementPtr global, + const std::string& mgr_name, + const data::SimpleDefaults& mgr_defaults); + + /// @brief Parses the whole D2 configuration + /// + /// @param ctx - parsed information will be stored here + /// @param config - Element tree structure that holds configuration + /// @param check_only - if true the configuration is verified only, not applied + /// + /// @throw ConfigError if any issues are encountered. + void parse(const D2CfgContextPtr& ctx, + const isc::data::ConstElementPtr& config, + bool check_only); +}; + +}; +}; + +#endif diff --git a/src/lib/d2srv/tests/.gitignore b/src/lib/d2srv/tests/.gitignore new file mode 100644 index 0000000000..3ab5a5578a --- /dev/null +++ b/src/lib/d2srv/tests/.gitignore @@ -0,0 +1 @@ +/libd2srv_unittests diff --git a/src/lib/d2srv/tests/Makefile.am b/src/lib/d2srv/tests/Makefile.am new file mode 100644 index 0000000000..864c4d8cba --- /dev/null +++ b/src/lib/d2srv/tests/Makefile.am @@ -0,0 +1,36 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libd2srv_unittests + +libd2srv_unittests_SOURCES = run_unittests.cc +#libd2srv_unittests_SOURCES += + +libd2srv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libd2srv_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) + +libd2srv_unittests_LDADD = $(top_builddir)/src/lib/process/libkea-process.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libd2srv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libd2srv_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) + +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/d2srv/tests/run_unittests.cc b/src/lib/d2srv/tests/run_unittests.cc new file mode 100644 index 0000000000..768319e3fa --- /dev/null +++ b/src/lib/d2srv/tests/run_unittests.cc @@ -0,0 +1,20 @@ +// Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <log/logger_support.h> + +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + + int result = RUN_ALL_TESTS(); + + return (result); +} |