summaryrefslogtreecommitdiffstats
path: root/src/lib/d2srv
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/d2srv')
-rw-r--r--src/lib/d2srv/.gitattributes2
-rw-r--r--src/lib/d2srv/Makefile.am83
-rw-r--r--src/lib/d2srv/d2_cfg_mgr.cc326
-rw-r--r--src/lib/d2srv/d2_cfg_mgr.h339
-rw-r--r--src/lib/d2srv/d2_config.cc644
-rw-r--r--src/lib/d2srv/d2_config.h898
-rw-r--r--src/lib/d2srv/d2_log.cc23
-rw-r--r--src/lib/d2srv/d2_log.h25
-rw-r--r--src/lib/d2srv/d2_messages.cc171
-rw-r--r--src/lib/d2srv/d2_messages.h89
-rw-r--r--src/lib/d2srv/d2_messages.mes396
-rw-r--r--src/lib/d2srv/d2_simple_parser.cc309
-rw-r--r--src/lib/d2srv/d2_simple_parser.h96
-rw-r--r--src/lib/d2srv/tests/.gitignore1
-rw-r--r--src/lib/d2srv/tests/Makefile.am36
-rw-r--r--src/lib/d2srv/tests/run_unittests.cc20
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);
+}