BIND 10 trac2957, updated. be6122f520871344324be1ceb9f118195d508bba [2957] This is the second of a two part commit for D2CfgMgr. It Adds a collection of classes for housing and parsing the application configuration necessary for the DHCP-DDNS application. This rounds out the D2CfgMgr initial implementation.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Jun 18 18:54:07 UTC 2013


The branch, trac2957 has been updated
       via  be6122f520871344324be1ceb9f118195d508bba (commit)
      from  dac0b87d5b0466e3aaf0f49ec7b2362606c03415 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit be6122f520871344324be1ceb9f118195d508bba
Author: Thomas Markwalder <tmark at isc.org>
Date:   Tue Jun 18 14:51:28 2013 -0400

    [2957] This is the second of a two part commit for D2CfgMgr. It  Adds a
    collection of classes for housing and parsing the application configuration
    necessary for the DHCP-DDNS application. This rounds out the D2CfgMgr initial
    implementation.
    
    New files:
       src/bin/d2/d2_config.cc
       src/bin/d2/d2_config.h
       src/bin/d2/tests/d2_cfg_mgr_unittests.cc

-----------------------------------------------------------------------

Summary of changes:
 src/bin/d2/Makefile.am                      |    1 +
 src/bin/d2/d2_cfg_mgr.cc                    |   77 ++-
 src/bin/d2/d2_cfg_mgr.h                     |   72 ++-
 src/bin/d2/d2_config.cc                     |  465 ++++++++++++++
 src/bin/d2/d2_config.h                      |  661 ++++++++++++++++++++
 src/bin/d2/d2_messages.mes                  |    7 +-
 src/bin/d2/d_cfg_mgr.cc                     |   46 +-
 src/bin/d2/d_cfg_mgr.h                      |   33 +-
 src/bin/d2/dhcp-ddns.spec                   |  178 +++++-
 src/bin/d2/tests/Makefile.am                |    2 +
 src/bin/d2/tests/d2_cfg_mgr_unittests.cc    |  891 +++++++++++++++++++++++++++
 src/bin/d2/tests/d2_controller_unittests.cc |   28 +-
 src/bin/d2/tests/d2_process_unittests.cc    |   17 +-
 src/bin/d2/tests/d_cfg_mgr_unittests.cc     |   50 +-
 src/bin/d2/tests/d_test_stubs.cc            |   20 +
 src/bin/d2/tests/d_test_stubs.h             |   70 +++
 16 files changed, 2510 insertions(+), 108 deletions(-)
 create mode 100644 src/bin/d2/d2_config.cc
 create mode 100644 src/bin/d2/d2_config.h
 create mode 100644 src/bin/d2/tests/d2_cfg_mgr_unittests.cc

-----------------------------------------------------------------------
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index 5e876d6..fe1f2a8 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -53,6 +53,7 @@ b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
 b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
 b10_dhcp_ddns_SOURCES += d_cfg_mgr.cc d_cfg_mgr.h
+b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
 b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
diff --git a/src/bin/d2/d2_cfg_mgr.cc b/src/bin/d2/d2_cfg_mgr.cc
index 873008f..0b94c6e 100644
--- a/src/bin/d2/d2_cfg_mgr.cc
+++ b/src/bin/d2/d2_cfg_mgr.cc
@@ -15,6 +15,8 @@
 #include <d2/d2_log.h>
 #include <d2/d2_cfg_mgr.h>
 
+#include <boost/foreach.hpp>
+
 using namespace std;
 using namespace isc;
 using namespace isc::dhcp;
@@ -26,12 +28,21 @@ namespace d2 {
 
 // *********************** D2CfgContext  *************************
 
-D2CfgContext::D2CfgContext() {
-    // @TODO - initialize D2 specific storage
+D2CfgContext::D2CfgContext()
+    : forward_mgr_(new DdnsDomainListMgr("forward_mgr")),
+      reverse_mgr_(new DdnsDomainListMgr("reverse_mgr")) {
 }
 
-D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs)
-    /* @TODO copy D2 specific storage  */ {
+D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
+    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());
+    }
 }
 
 D2CfgContext::~D2CfgContext() {
@@ -45,12 +56,60 @@ D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
 D2CfgMgr::~D2CfgMgr() {
 }
 
+bool
+D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
+    if (fqdn == "") {
+        // 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& fqdn, DdnsDomainPtr& domain) {
+    if (fqdn == "") {
+        // This is a programmatic error and should not happen.
+        isc_throw (D2CfgError, "matchReverse passed a null or empty fqdn");
+    }
+
+    // Fetch the reverse manager from the D2 context.
+    DdnsDomainListMgrPtr& mgr = getD2CfgContext()->getReverseMgr();
+
+    // Call the manager's match method and return the result.
+    return (mgr->matchDomain(fqdn, domain));
+}
+
+
 isc::dhcp::ParserPtr
-D2CfgMgr::createConfigParser(const std::string& element_id) {
-    // @TODO This is only enough implementation for integration.
-    // This will expand to support the top level D2 elements.
-    // For now we will simply return a debug parser for everything.
-    return (isc::dhcp::ParserPtr(new isc::dhcp::DebugParser(element_id)));
+D2CfgMgr::createConfigParser(const std::string& config_id) {
+    // Get D2 specific context.
+    D2CfgContextPtr context = getD2CfgContext();
+
+    // Create parser instance based on element_id.
+    DhcpConfigParser* parser = NULL;
+    if ((config_id == "interface")  ||
+        (config_id == "ip_address")) {
+        parser = new StringParser(config_id, context->getStringStorage());
+    } else if (config_id == "port") {
+        parser = new Uint32Parser(config_id, context->getUint32Storage());
+    } else if (config_id ==  "forward_ddns") {
+        parser = new DdnsDomainListMgrParser("forward_mgr",
+                                             context->getForwardMgr());
+    } else if (config_id ==  "reverse_ddns") {
+        parser = new DdnsDomainListMgrParser("reverse_mgr",
+                                             context->getReverseMgr());
+    } else {
+        isc_throw(NotImplemented,
+                  "parser error: D2CfgMgr parameter not supported: "
+                  << config_id);
+    }
+
+    return (isc::dhcp::ParserPtr(parser));
 }
 
 }; // end of isc::dhcp namespace
diff --git a/src/bin/d2/d2_cfg_mgr.h b/src/bin/d2/d2_cfg_mgr.h
index 0260e5c..54692ce 100644
--- a/src/bin/d2/d2_cfg_mgr.h
+++ b/src/bin/d2/d2_cfg_mgr.h
@@ -12,9 +12,11 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <asiolink/io_address.h>
 #include <cc/data.h>
 #include <exceptions/exceptions.h>
 #include <d2/d_cfg_mgr.h>
+#include <d2/d2_config.h>
 
 #include <stdint.h>
 #include <string>
@@ -28,11 +30,9 @@ namespace d2 {
 /// @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 context specific information that needs to be accessible
+/// and any other DHCP-DDNS specific information that needs to be accessible
 /// during configuration parsing as well as to the application as a whole.
-/// @TODO - add in storage of D2 specific storage like domain-to-dns_server
-/// mapping.  This is the initial implementation necessary to integrate
-/// configuration management into D2.
+/// It is derived from the context base class, DCfgContextBase.
 class D2CfgContext : public DCfgContextBase {
 public:
     /// @brief Constructor
@@ -42,11 +42,26 @@ public:
     virtual ~D2CfgContext();
 
     /// @brief Creates a clone of this context object.
+    ///
     /// @return returns a raw pointer to the new clone.
     virtual D2CfgContext* clone() {
             return (new D2CfgContext(*this));
     }
 
+    /// @brief Fetches the forward DNS domain list manager.
+    ///
+    /// @return returns a pointer reference to the forward manager.
+    DdnsDomainListMgrPtr& getForwardMgr() {
+        return (forward_mgr_);
+    }
+
+    /// @brief Fetches the reverse DNS domain list manager.
+    ///
+    /// @return returns a pointer reference to the reverse manager.
+    DdnsDomainListMgrPtr& getReverseMgr() {
+        return (reverse_mgr_);
+    }
+
 protected:
     /// @brief Copy constructor for use by derivations in clone().
     D2CfgContext(const D2CfgContext& rhs);
@@ -55,9 +70,17 @@ private:
     /// @brief Private assignment operator to avoid potential for slicing.
     D2CfgContext& operator=(const D2CfgContext& rhs);
 
-    /// @TODO storage for DNS domain-server mapping will be added here
+    /// @brief Forward domain list manager.
+    DdnsDomainListMgrPtr forward_mgr_;
+
+    /// @brief Reverse domain list manager.
+    DdnsDomainListMgrPtr reverse_mgr_;
 };
 
+/// @brief Defines a pointer for DdnsDomain instances.
+typedef boost::shared_ptr<DdnsDomainListMgr> DdnsDomainListMgrPtr;
+
+
 /// @brief Pointer to a configuration context.
 typedef boost::shared_ptr<D2CfgContext> D2CfgContextPtr;
 
@@ -67,7 +90,6 @@ typedef boost::shared_ptr<D2CfgContext> D2CfgContextPtr;
 /// configuration.  This includes services for parsing sets of configuration
 /// values, storing the parsed information in its converted form,
 /// and retrieving the information on demand.
-/// @TODO add in D2 specific parsing
 class D2CfgMgr : public DCfgMgrBase {
 public:
     /// @brief Constructor
@@ -85,13 +107,43 @@ public:
         return (boost::dynamic_pointer_cast<D2CfgContext>(getContext()));
     }
 
+    /// @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& fdqn, DdnsDomainPtr &domain);
+
+    /// @brief Matches a given FQDN to a reverse domain.
+    ///
+    /// This calls the matchDomain method of the reverse 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 matchReverse(const std::string& fdqn, DdnsDomainPtr &domain);
+
 protected:
     /// @brief Given an element_id returns an instance of the appropriate
     /// parser.
-    /// @TODO The initial implementation simply returns a DebugParser for any
-    /// element_id value.  This is sufficient to integrate configuration
-    /// management into D2. Specific parsers will be added as the DHCP-DDNS
-    /// specific configuration is constructed.
+    ///
+    /// It is responsible for top-level or outermost DHCP-DDNS configuration
+    /// elements (see dhcp-ddns.spec):
+    ///     1. interface
+    ///     2. ip_address
+    ///     3. port
+    ///     4. forward_ddns
+    ///     5. reverse_ddns
     ///
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
diff --git a/src/bin/d2/d2_config.cc b/src/bin/d2/d2_config.cc
new file mode 100644
index 0000000..49448ac
--- /dev/null
+++ b/src/bin/d2/d2_config.cc
@@ -0,0 +1,465 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <d2/d2_log.h>
+#include <d2/d2_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_error.h>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace isc {
+namespace d2 {
+
+// *********************** DnsServerInfo  *************************
+
+const uint32_t DnsServerInfo::standard_dns_port = 53;
+
+const char* DnsServerInfo::empty_ip_str = "0.0.0.0";
+
+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() {
+}
+
+// *********************** DdnsDomain  *************************
+
+DdnsDomain::DdnsDomain(const std::string& name, const std::string& key_name,
+                       DnsServerInfoStoragePtr servers)
+    : name_(name), key_name_(key_name), servers_(servers) {
+}
+
+DdnsDomain::~DdnsDomain() {
+}
+
+// *********************** DdnsDomainLstMgr  *************************
+
+DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
+    domains_(new DdnsDomainStorage()) {
+}
+
+
+DdnsDomainListMgr::~DdnsDomainListMgr () {
+}
+
+void 
+DdnsDomainListMgr::setDomains(DdnsDomainStoragePtr domains) {
+    if (!domains) {
+        isc_throw(D2CfgError, 
+                  "DdnsDomainListMgr::setDomains: Domain list may not be null");
+    }
+
+    domains_ = domains;
+
+    // Iterate over the domain list lookfing 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 match. 
+    DdnsDomainPtrPair domain_pair;
+    BOOST_FOREACH(domain_pair, (*domains_)) {
+        DdnsDomainPtr domain = domain_pair.second;
+        const std::string& domain_name = domain->getName();
+
+        if (domain_name == "*") {
+            wildcard_domain_ = domain;
+            break;
+        }
+    }
+}
+
+bool
+DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
+    // Clear the return parameter.
+    domain.reset();
+
+    // First check the case of one domain to rule them all.
+    if ((size() == 1) && (wildcard_domain_)) {
+        domain = wildcard_domain_;
+        return (true);
+    }
+
+    // Start with the longest version of the fqdn and search the list.
+    // Continue looking for shorter versions of fqdn so long as no match is 
+    // found.
+    // @TODO This can surely be optimized, time permitting.  
+    std::string match_name = fqdn;
+    std::size_t start_pos = 0;
+    while (start_pos != std::string::npos) {
+        match_name = match_name.substr(start_pos, std::string::npos);
+        DdnsDomainStorage::iterator gotit = domains_->find(match_name);
+        if (gotit != domains_->end()) {
+            domain = gotit->second;
+            break;
+        }
+
+        start_pos = match_name.find_first_of(".");
+        if (start_pos != std::string::npos) {
+            start_pos++;
+        }
+    }
+
+    if (!domain) {
+        // 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_;
+        } else {
+            LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
+            return (false);
+        }
+    } 
+
+    return (true);
+}
+
+// *************************** PARSERS ***********************************
+
+// *********************** DnsServerInfoParser  *************************
+
+DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
+    DnsServerInfoStoragePtr servers) 
+    : entry_name_(entry_name), servers_(servers), local_scalars_() {
+    if (!servers_) {
+        isc_throw(D2CfgError, "DdnsServerInfoParser ctor:"
+                  " server storage cannot be null");
+    }
+}
+
+DnsServerInfoParser::~DnsServerInfoParser() {
+}
+
+void 
+DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
+    isc::dhcp::ConfigPair config_pair;
+    // For each element in the server configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's 
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    BOOST_FOREACH (config_pair, server_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+        
+}
+
+isc::dhcp::ParserPtr 
+DnsServerInfoParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    // Based on the configuration id of the element, create the appropriate
+    // parser. Scalars are set to use the parser's local scalar storage. 
+    if ((config_id == "hostname")  ||
+        (config_id == "ip_address")) {
+        parser = new isc::dhcp::StringParser(config_id, 
+                                             local_scalars_.getStringStorage());
+    } else if (config_id == "port") { 
+        parser = new isc::dhcp::Uint32Parser(config_id, 
+                                             local_scalars_.getUint32Storage());
+    } else {
+        isc_throw(NotImplemented, 
+                  "parser error: DnsServerInfo parameter not supported: " 
+                  << config_id);
+    }
+
+    // Return the new parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void 
+DnsServerInfoParser::commit() {
+    std::string hostname;
+    std::string ip_address;
+    uint32_t port = DnsServerInfo::standard_dns_port; 
+
+    // Fetch the server configuration's paresed scalar values from parser's 
+    // local storage. 
+    local_scalars_.getParam("hostname", hostname, DCfgContextBase::optional_);
+    local_scalars_.getParam("ip_address", ip_address, 
+                            DCfgContextBase::optional_);
+    local_scalars_.getParam("port", port, DCfgContextBase::optional_);
+
+    // The configuration cannot specify both hostname and ip_address.
+    if ((hostname != "") && (ip_address != "")) {
+        isc_throw(D2CfgError,
+          "Dns Server cannot specify both hostname and static IP address");
+    } 
+
+    // The configuration must specify one or the other.
+    if ((hostname == "") && (ip_address == "")) {
+        isc_throw(D2CfgError,
+          "Dns Server must specify either hostname or static IP address");
+    }
+
+    DnsServerInfo* serverInfo = NULL;
+    if (hostname != "") {
+        // When  hostname is specified, create a valid, blank IOAddress and
+        // then create the DnsServerInfo.
+        isc::asiolink::IOAddress io_addr(DnsServerInfo::empty_ip_str);
+        serverInfo = new DnsServerInfo(hostname, io_addr, port);
+    } else {
+        try {
+            // Create an IOAddress from the IP address string given and then
+            // create the DnsServerInfo.
+            isc::asiolink::IOAddress io_addr(ip_address);
+            serverInfo = new DnsServerInfo(hostname, io_addr, port);
+        } catch (const isc::asiolink::IOError& ex) {
+            isc_throw(D2CfgError, "Invalid IP address:" << ip_address);
+        }
+    }
+
+    // Add the new DnsServerInfo to the server storage.
+    servers_->push_back(DnsServerInfoPtr(serverInfo));
+}
+
+// *********************** DnsServerInfoListParser  *************************
+
+DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
+                                       DnsServerInfoStoragePtr servers)
+    :list_name_(list_name), servers_(servers), parsers_() {
+    if (!servers_) {
+        isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
+                  " server storage cannot be null");
+    }
+}
+
+DnsServerInfoListParser::~DnsServerInfoListParser(){
+}
+
+void 
+DnsServerInfoListParser::
+build(isc::data::ConstElementPtr server_list){
+    int i = 0;
+    isc::data::ConstElementPtr server_config;
+    // For each server element in the server list: 
+    // 1. Create a parser for the server element.
+    // 2. Invoke the parser's build method passing in the server's 
+    // configuration.
+    // 3. Add the parser to a local collection of parsers.
+    BOOST_FOREACH(server_config, server_list->listValue()) {
+        // Create a name for the parser based on its position in the list.
+        std::string entry_name = boost::lexical_cast<std::string>(i++);
+        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name, 
+                                                            servers_));
+        parser->build(server_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void 
+DnsServerInfoListParser::commit() {
+    // Domains must have at least one server.
+    if (parsers_.size() == 0) {
+        isc_throw (D2CfgError, "Server List must contain at least one server");
+    }
+
+    // Invoke commit on each server parser. This will cause each one to
+    // create it's server instance and commit it to storage. 
+    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+}
+
+// *********************** DdnsDomainParser  *************************
+
+DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
+                                   DdnsDomainStoragePtr domains) 
+    : entry_name_(entry_name), domains_(domains), 
+    local_servers_(new DnsServerInfoStorage()), local_scalars_() {
+    if (!domains_) {
+        isc_throw(D2CfgError, 
+                  "DdnsDomainParser ctor, domain storage cannot be null");
+    }
+}
+
+
+DdnsDomainParser::~DdnsDomainParser() {
+}
+
+void 
+DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
+    // For each element in the domain configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's 
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    isc::dhcp::ConfigPair config_pair;
+    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr 
+DdnsDomainParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    // Based on the configuration id of the element, create the appropriate
+    // parser. Scalars are set to use the parser's local scalar storage. 
+    if ((config_id == "name")  ||
+        (config_id == "key_name")) {  
+        parser = new isc::dhcp::StringParser(config_id, 
+                                             local_scalars_.getStringStorage());
+    } else if (config_id == "dns_servers") {
+       // Server list parser is given in our local server storage. It will pass
+       // this down to its server parsers and is where they will write their
+       // server instances upon commit.
+       parser = new DnsServerInfoListParser(config_id, local_servers_);
+    } else {
+       isc_throw(NotImplemented,
+                "parser error: DdnsDomain parameter not supported: " 
+                << config_id);
+    }
+
+    // Return the new domain parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void 
+DdnsDomainParser::commit() {
+    std::string name;
+    std::string key_name;
+
+    // Domain name is not optional. The get will throw if its not there.
+    local_scalars_.getParam("name", name); 
+
+    // Blank domain names are not allowed.
+    if (name == "") {
+        isc_throw(D2CfgError, "Domain name cannot be blank");
+    }
+
+    // Currently, the premise is that domain storage is always empty
+    // prior to parsing so always adding domains never replacing them.
+    // Duplicates are not allowed and should be flagged as a configuration
+    // error.
+    if (domains_->find(name) != domains_->end()) {
+        isc_throw(D2CfgError, "Duplicate domain specified:" << name); 
+    }
+
+    // Key name is optional and for now, unused.  It is intended to be
+    // used as the name of the TSIG key this domain should use.
+    local_scalars_.getParam("key_name", key_name, DCfgContextBase::optional_);
+
+    // Instantiate the new domain and add it to domain storage.
+    DdnsDomainPtr domain(new DdnsDomain(name, key_name, local_servers_));
+
+    // Add the new domain to the domain storage.
+    (*domains_)[name]=domain;
+}
+
+// *********************** DdnsDomainListParser  *************************
+
+DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
+                                       DdnsDomainStoragePtr domains)
+    :list_name_(list_name), domains_(domains), parsers_() {
+    if (!domains_) {
+        isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
+                  " domain storage cannot be null");
+    }
+}
+
+DdnsDomainListParser::~DdnsDomainListParser(){
+}
+
+void 
+DdnsDomainListParser::
+build(isc::data::ConstElementPtr domain_list){
+    // For each domain element in the domain list: 
+    // 1. Create a parser for the domain element.
+    // 2. Invoke the parser's build method passing in the domain's 
+    // configuration.
+    // 3. Add the parser to the local collection of parsers.
+    int i = 0;
+    isc::data::ConstElementPtr domain_config;
+    BOOST_FOREACH(domain_config, domain_list->listValue()) {
+        std::string entry_name = boost::lexical_cast<std::string>(i++);
+        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name, domains_));
+        parser->build(domain_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void 
+DdnsDomainListParser::commit() {
+    // Invoke commit on each server parser. This will cause each one to
+    // create it's server instance and commit it to storage. 
+    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
+        parser->commit();
+    }
+}
+
+
+// *********************** DdnsDomainListMgrParser  *************************
+
+DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
+                                                 DdnsDomainListMgrPtr& mgr) 
+    : entry_name_(entry_name), mgr_(mgr), 
+    local_domains_(new DdnsDomainStorage()), local_scalars_() {
+}
+
+
+DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
+}
+
+void 
+DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
+    // For each element in the domain manager configuration:
+    // 1. Create a parser for the element.
+    // 2. Invoke the parser's build method passing in the element's 
+    // configuration.
+    // 3. Invoke the parser's commit method to store the element's parsed
+    // data to the parser's local storage.
+    isc::dhcp::ConfigPair config_pair;
+    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr 
+DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
+    DhcpConfigParser* parser = NULL;
+    if (config_id == "ddns_domains") {
+       // Domain list parser is given our local domain storage. It will pass
+       // this down to its domain parsers and is where they will write their
+       // domain instances upon commit.
+       parser = new DdnsDomainListParser(config_id, local_domains_);
+    } else {
+       isc_throw(NotImplemented, "parser error: "
+                 "DdnsDomainListMgr parameter not supported: " << config_id);
+    }
+
+    // Return the new domain parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void 
+DdnsDomainListMgrParser::commit() {
+    // Add the new domain to the domain storage.
+    mgr_->setDomains(local_domains_);
+}
+
+
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/d2/d2_config.h b/src/bin/d2/d2_config.h
new file mode 100644
index 0000000..deb8b52
--- /dev/null
+++ b/src/bin/d2/d2_config.h
@@ -0,0 +1,661 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <d2/d_cfg_mgr.h>
+#include <dhcpsrv/dhcp_parsers.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/foreach.hpp>
+
+#include <stdint.h>
+#include <string>
+
+#ifndef D2_CONFIG_H
+#define D2_CONFIG_H
+
+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 and
+/// two managed lists of domains: one list for forward domains and one list for
+/// reverse domains.
+///
+/// Each managed 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.  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 configuration specification for use with BIND10 is detailed in the file
+/// dhcp-ddns.spec.
+///
+/// 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 DdnsDomainLiatParser creates and invokes DdnsDomainListParser 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 th server.
+
+/// @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 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:
+
+    /// @brief defines DNS standard port value
+    static const uint32_t standard_dns_port;
+
+    /// @brief defines an "empty" string version of an ip address.
+    static const char* empty_ip_str;
+
+    /// @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,
+                  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;
+    }
+
+
+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_;
+};
+
+/// @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.  It's primary use is to map a domain to the DNS server(s) responsible
+/// for it.
+class DdnsDomain {
+public:
+    /// @brief Constructor
+    ///
+    /// @param name is the domain name of the domain.
+    /// @param key_name is the TSIG key name for use with this domain.
+    /// (@TODO TSIG is not yet functional).
+    /// @param servers is the list of server(s) supporting this domain.
+    DdnsDomain(const std::string& name, const std::string& key_name,
+               DnsServerInfoStoragePtr servers);
+
+    /// @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 Getter which returns the domain's TSIG key name.
+    ///
+    /// @return returns the key name in an std::string.
+    const std::string getKeyName() const {
+        return (key_name_);
+    }
+
+    /// @brief Getter which returns the domain's list of servers.
+    ///
+    /// @return returns the pointer to the server storage.
+    const DnsServerInfoStoragePtr& getServers() {
+        return (servers_);
+    }
+
+private:
+    /// @brief The domain name of the domain.
+    std::string name_;
+
+    /// @brief The name of the TSIG key for use with this domain.
+    /// @TODO TSIG is not yet functional).
+    std::string key_name_;
+
+    /// @brief The list of server(s) supporting this domain.
+    DnsServerInfoStoragePtr servers_;
+};
+
+/// @brief Defines a pointer for DdnsDomain instances.
+typedef boost::shared_ptr<DdnsDomain> DdnsDomainPtr;
+
+/// @brief Defines a storage container for DdnsDomain pointers.
+typedef std::map<std::string, DdnsDomainPtr> DdnsDomainStorage;
+
+/// @brief Defines a pointer to DdnsDomain storage containers.
+typedef boost::shared_ptr<DdnsDomainStorage> DdnsDomainStoragePtr;
+
+/// @brief Defines a domain and domain key pair for iterating.
+typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainPtrPair;
+
+
+/// @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 "*".
+/// As matching capabilities evolve this class is expected to expand.
+class DdnsDomainListMgr {
+public:
+    /// @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
+    /// the it will be returned immediately for any FQDN.
+    ///
+    /// @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.
+    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 DdnsDomainStoragePtr &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(DdnsDomainStoragePtr domains);
+
+private:
+    /// @brief An arbitrary label assigned to this manager.
+    std::string name_;
+
+    /// @brief Storage for the list of domains.
+    DdnsDomainStoragePtr 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 simple
+/// configuration items (e.g. ints, bools, strings...) without explicitly adding
+/// storage for each individual type needed by the parser.
+class DScalarContext : public DCfgContextBase {
+public:
+
+    /// @brief Constructor
+    DScalarContext() {
+    };
+
+    /// @brief Destructor
+    virtual ~DScalarContext() {
+    }
+
+    /// @brief Creates a clone of a DStubContext.
+    ///
+    /// @return returns a raw pointer to the new clone.
+    virtual DScalarContext* clone() {
+        return (new DScalarContext(*this));
+    }
+
+protected:
+    /// @brief Copy constructor
+    DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs){
+    }
+
+private:
+    /// @brief Private assignment operator, not implemented.
+    DScalarContext& operator=(const DScalarContext& rhs);
+};
+
+/// @brief Parser for  DnsServerInfo
+///
+/// This class parses the configuration element "dns_server" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a DnsServerInfo.
+class DnsServerInfoParser : public  isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition. Since servers are specified in a list this value is likely
+    /// be something akin to "server:0", set during parsing.
+    /// @param servers is a pointer to the storage area to which the parser
+    /// should commit the newly created DnsServerInfo instance.
+    DnsServerInfoParser(const std::string& entry_name,
+                        DnsServerInfoStoragePtr servers);
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoParser();
+
+    /// @brief Performs the actual parsing of the given  "dns_server" element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param server_config is the "dns_server" configuration to parse
+    virtual void build(isc::data::ConstElementPtr server_config);
+
+    /// @brief Creates a parser for the given "dns_server" member element id.
+    ///
+    /// The server elements currently supported are(see dhcp-ddns.spec):
+    ///   1. hostname
+    ///   2. ip_address
+    ///   3. port 
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "dns_server" specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Instantiates a DnsServerInfo from internal data values
+    /// saves it to the storage area pointed to by servers_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    /// Since servers are specified in a list this value is likely be something
+    /// akin to "server:0", set during parsing.  Primarily here for diagnostics.
+    std::string entry_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the newly created DnsServerInfo instance. This is given to us as a
+    /// constructor argument by an upper level.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    DScalarContext local_scalars_;
+};
+
+/// @brief Parser for a list of DnsServerInfos
+///
+/// This class parses a list of "dns_server" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The DnsServerInfo instances are added
+/// to the given storage upon commit.
+class DnsServerInfoListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param servers is a pointer to the storage area to which the parser
+    /// should commit the newly created DnsServerInfo instance.
+    DnsServerInfoListParser(const std::string& list_name,
+                            DnsServerInfoStoragePtr servers_);
+
+    /// @brief Destructor
+    virtual ~DnsServerInfoListParser();
+
+    /// @brief Performs the actual parsing of the given list "dns_server"
+    /// elements.
+    /// It iterates over each server entry in the list:
+    ///   1. Instantiate a DnsServerInfoParser for the entry
+    ///   2. Pass the element configuration to the parser's build method
+    ///   3. Add the parser instance to local storage
+    ///
+    /// The net effect is to parse all of the server entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param server_list_config is the list of "dns_server" elements to parse.
+    virtual void build(isc::data::ConstElementPtr server_list_config);
+
+    /// @brief Iterates over the internal list of DnsServerInfoParsers,
+    /// invoking commit on each.  This causes each parser to instantiate a
+    /// DnsServerInfo from its internal data values and add that that server
+    /// instance to the storage area, servers_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string list_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the list of newly created DnsServerInfo instances. This is given to us
+    /// as a constructor argument by an upper level.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Local storage of DnsServerInfoParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
+/// @brief Parser for  DdnsDomain
+///
+/// This class parses the configuration element "ddns_domain" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a DdnsDomain.
+class DdnsDomainParser : public isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition. Since domains are specified in a list this value is likely
+    /// be something akin to "forward_ddns:0", set during parsing.
+    /// @param domains is a pointer to the storage area to which the parser
+    /// should commit the newly created DdnsDomain instance.
+    DdnsDomainParser(const std::string& entry_name,
+                     DdnsDomainStoragePtr domains);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainParser();
+
+    /// @brief Performs the actual parsing of the given  "ddns_domain" element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param server_config is the "ddns_domain" configuration to parse
+    virtual void build(isc::data::ConstElementPtr domain_config);
+
+    /// @brief Creates a parser for the given "ddns_domain" member element id.
+    ///
+    /// The domain elements currently supported are(see dhcp-ddns.spec):
+    ///   1. name
+    ///   2. key_name
+    ///   3. dns_servers
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "ddns_domain" specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Instantiates a DdnsDomain from internal data values
+    /// saves it to the storage area pointed to by domains_.
+    virtual void commit();
+
+private:
+
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string entry_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the newly created DdnsDomain instance. This is given to us as a
+    /// constructor argument by an upper level.
+    DdnsDomainStoragePtr domains_;
+
+    /// @brief Local storage for DnsServerInfo instances. This is passed into
+    /// DnsServerInfoListParser(s), which in turn passes it into each
+    /// DnsServerInfoParser.  When the DnsServerInfoParsers "commit" they add
+    /// their server instance to this storage.
+    DnsServerInfoStoragePtr local_servers_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    DScalarContext local_scalars_;
+};
+
+/// @brief Parser for a list of DdnsDomains
+///
+/// This class parses a list of "ddns_domain" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The DdnsDomain instances are added
+/// to the given storage upon commit.
+class DdnsDomainListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param domains is a pointer to the storage area to which the parser
+    /// should commit the newly created DdnsDomain instance.
+    DdnsDomainListParser(const std::string& list_name,
+                            DdnsDomainStoragePtr domains_);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListParser();
+
+    /// @brief Performs the actual parsing of the given list "ddns_domain"
+    /// elements.
+    /// It iterates over each server entry in the list:
+    ///   1. Instantiate a DdnsDomainParser for the entry
+    ///   2. Pass the element configuration to the parser's build method
+    ///   3. Add the parser instance to local storage
+    ///
+    /// The net effect is to parse all of the domain entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param domain_list_config is the list of "ddns_domain" elements to
+    /// parse.
+    virtual void build(isc::data::ConstElementPtr domain_list_config);
+
+    /// @brief Iterates over the internal list of DdnsDomainParsers, invoking
+    /// commit on each.  This causes each parser to instantiate a DdnsDomain
+    /// from its internal data values and add that domain instance to the
+    /// storage area, domains_.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string list_name_;
+
+    /// @brief Pointer to the storage area to which the parser should commit
+    /// the list of newly created DdnsDomain instances. This is given to us
+    /// as a constructor argument by an upper level.
+    DdnsDomainStoragePtr domains_;
+
+    /// @brief Local storage of DdnsDomainParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
+/// @brief Parser for DdnsDomainListMgr
+///
+/// This class parses the configuration elements "forward_ddns" and
+/// "reverse_ddns" as defined in src/bin/d2/dhcp-ddns.spec.  It populates the
+/// given DdnsDomainListMgr with parsed information upon commit.  Note that
+/// unlike other parsers, this parser does NOT instantiate the final object
+/// during the commit phase, it populates it.  It must pre-exist.
+class DdnsDomainListMgrParser : public isc::dhcp::DhcpConfigParser {
+public:
+    /// @brief Constructor
+    ///
+    /// @param entry_name is an arbitrary label assigned to this configuration
+    /// definition.
+    /// @param mgr is a pointer to the DdnsDomainListMgr to populate.
+    /// @throw throws D2CfgError if mgr pointer is empty.
+    DdnsDomainListMgrParser(const std::string& entry_name,
+                     DdnsDomainListMgrPtr& mgr);
+
+    /// @brief Destructor
+    virtual ~DdnsDomainListMgrParser();
+
+    /// @brief Performs the actual parsing of the given manager element.
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param mgr_config is the manager configuration to parse
+    virtual void build(isc::data::ConstElementPtr mgr_config);
+
+    /// @brief Creates a parser for the given manager member element id.
+    ///
+    /// 
+    /// The manager elements currently supported are (see dhcp-ddns.spec):
+    ///     1. ddns_domains 
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the manager specification.
+    ///
+    /// @return returns a pointer to newly created parser.
+    virtual isc::dhcp::ParserPtr createConfigParser(const std::string&
+                                                    config_id);
+
+    /// @brief Populates the DdnsDomainListMgr from internal data values
+    /// set during parsing.
+    virtual void commit();
+
+private:
+    /// @brief Arbitrary label assigned to this parser instance.
+    std::string entry_name_;
+
+    /// @brief Pointer to manager instance to which the parser should commit
+    /// the parsed data. This is given to us as a constructor argument by an
+    /// upper level.
+    DdnsDomainListMgrPtr mgr_;
+
+    /// @brief Local storage for DdnsDomain instances. This is passed into a
+    /// DdnsDomainListParser(s), which in turn passes it into each
+    /// DdnsDomainParser.  When the DdnsDomainParsers "commit" they add their
+    /// domain instance to this storage.
+    DdnsDomainStoragePtr local_domains_;
+
+    /// @brief Local storage area for scalar parameter values. Use to hold
+    /// data until time to commit.
+    /// @TODO Currently, the manager has no scalars but this is likely to
+    /// change as matching capabilities expand.
+    DScalarContext local_scalars_;
+};
+
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif // D2_CONFIG_H
diff --git a/src/bin/d2/d2_messages.mes b/src/bin/d2/d2_messages.mes
index 2bbc394..ab12bfd 100644
--- a/src/bin/d2/d2_messages.mes
+++ b/src/bin/d2/d2_messages.mes
@@ -66,7 +66,7 @@ application and will exit.
 
 % DCTL_NOT_RUNNING %1 application instance is not running
 A warning message is issued when an attempt is made to shut down the
-the application when it is not running.
+application when it is not running.
 
 % DCTL_ORDER_ERROR Configuration contains more elements than the parsing order
 A debug message which indicates that configuration being parsed includes
@@ -119,6 +119,11 @@ has been invoked.
 This is a debug message issued when the Dhcp-Ddns application encounters an
 unrecoverable error from within the event loop.
 
+% 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 FQDN.  The DNS Update request for the FQDN cannot be
+processed.
+
 % DHCP_DDNS_PROCESS_INIT application init invoked
 This is a debug message issued when the Dhcp-Ddns application enters
 its init method.
diff --git a/src/bin/d2/d_cfg_mgr.cc b/src/bin/d2/d_cfg_mgr.cc
index e789a43..365eab1 100644
--- a/src/bin/d2/d_cfg_mgr.cc
+++ b/src/bin/d2/d_cfg_mgr.cc
@@ -39,6 +39,9 @@ namespace isc {
 namespace d2 {
 
 // *********************** DCfgContextBase  *************************
+
+const bool DCfgContextBase::optional_ = true;
+
 DCfgContextBase::DCfgContextBase():
         boolean_values_(new BooleanStorage()),
         uint32_values_(new Uint32Storage()),
@@ -51,6 +54,45 @@ DCfgContextBase::DCfgContextBase(const DCfgContextBase& rhs):
         string_values_(new StringStorage(*(rhs.string_values_))) {
 }
 
+void
+DCfgContextBase::getParam(const std::string& name, bool& value, bool optional) {
+    try {
+        value = boolean_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
+
+void
+DCfgContextBase::getParam(const std::string& name, uint32_t& value,
+                          bool optional) {
+    try {
+        value = uint32_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
+void
+DCfgContextBase::getParam(const std::string& name, std::string& value,
+                          bool optional) {
+    try {
+        value = string_values_->getParam(name);
+    } catch (DhcpConfigError& ex) {
+        // If the parameter is not optional, re-throw the exception.
+        if (!optional) {
+            throw;
+        }
+    }
+}
+
 DCfgContextBase::~DCfgContextBase() {
 }
 
@@ -140,7 +182,7 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
         LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg("");
         answer = isc::config::createAnswer(0, "Configuration committed.");
 
-    } catch (const DCfgMgrBaseError& ex) {
+    } catch (const isc::Exception& ex) {
         LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(element_id).arg(ex.what());
         answer = isc::config::createAnswer(1,
                      string("Configuration parsing failed:") + ex.what() +
@@ -148,6 +190,7 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
 
         // An error occurred, so make sure that we restore original context.
         context_ = original_context;
+        return (answer);
     }
 
     return (answer);
@@ -157,7 +200,6 @@ void DCfgMgrBase::buildAndCommit(std::string& element_id,
                                  isc::data::ConstElementPtr value) {
     // Call derivation's implementation to create the appropriate parser
     // based on the element id.
-    // ParserPtr parser(createConfigParser(element_id));
     ParserPtr parser = createConfigParser(element_id);
     if (!parser) {
         isc_throw(DCfgMgrBaseError, std::string("Could not create parser"));
diff --git a/src/bin/d2/d_cfg_mgr.h b/src/bin/d2/d_cfg_mgr.h
index d375dd7..b5e4769 100644
--- a/src/bin/d2/d_cfg_mgr.h
+++ b/src/bin/d2/d_cfg_mgr.h
@@ -54,6 +54,9 @@ public:
 ///
 class DCfgContextBase {
 public:
+    /// @brief Indicator that a configuration parameter is optional.
+    static const bool optional_;
+
     /// @brief Constructor
     DCfgContextBase();
 
@@ -66,11 +69,13 @@ public:
     /// @param name is the name of the parameter to retrieve.
     /// @param value is an output parameter in which to return the retrieved
     /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
+    /// It defaults to false if not specified.
     /// @throw throws DhcpConfigError if the context does not contain the
-    /// parameter.
-    void getParam(const std::string& name, bool& value) {
-        value = boolean_values_->getParam(name);
-    }
+    /// parameter and optional is false.
+    void getParam(const std::string& name, bool& value, bool optional=false);
 
     /// @brief Fetches the value for a given uint32_t configuration parameter
     /// from the context.
@@ -78,11 +83,13 @@ public:
     /// @param name is the name of the parameter to retrieve.
     /// @param value is an output parameter in which to return the retrieved
     /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
     /// @throw throws DhcpConfigError if the context does not contain the
-    /// parameter.
-    void getParam(const std::string& name, uint32_t& value) {
-        value = uint32_values_->getParam(name);
-    }
+    /// parameter and optional is false.
+    void getParam(const std::string& name, uint32_t& value,
+                 bool optional=false);
 
     /// @brief Fetches the value for a given string configuration parameter
     /// from the context.
@@ -90,11 +97,13 @@ public:
     /// @param name is the name of the parameter to retrieve.
     /// @param value is an output parameter in which to return the retrieved
     /// value.
+    /// @param optional if true, the parameter is optional and the method
+    /// will not throw if the parameter is not found in the context. The
+    /// contents of the output parameter, value, will not be altered.
     /// @throw throws DhcpConfigError if the context does not contain the
-    /// parameter.
-    void getParam(const std::string& name, std::string& value) {
-        value = string_values_->getParam(name);
-    }
+    /// parameter and optional is false.
+    void getParam(const std::string& name, std::string& value,
+                  bool optional=false);
 
     /// @brief Fetches the Boolean Storage. Typically used for passing
     /// into parsers.
diff --git a/src/bin/d2/dhcp-ddns.spec b/src/bin/d2/dhcp-ddns.spec
index 1098ada..1ea67f1 100644
--- a/src/bin/d2/dhcp-ddns.spec
+++ b/src/bin/d2/dhcp-ddns.spec
@@ -1,21 +1,173 @@
+{ 
+"module_spec": 
 {
-  "module_spec": {
     "module_name": "DhcpDdns",
     "module_description": "DHPC-DDNS Service",
     "config_data": [
-    ],
+    { 
+        "item_name": "interface",
+        "item_type": "string",
+        "item_optional": true,
+        "item_default": "eth0"
+    },
+
+    { 
+        "item_name": "ip_address",
+        "item_type": "string",
+        "item_optional": false,
+        "item_default": "127.0.0.1" 
+    },
+
+    { 
+        "item_name": "port",
+        "item_type": "integer",
+        "item_optional": true,
+        "item_default": 51771 
+    },
+
+    {
+        "item_name": "foward_ddns",
+        "item_type": "map",
+        "item_optional": false,
+         "item_default": {},
+         "map_item_spec": [ 
+         {
+            "item_name": "ddns_domains",
+            "item_type": "list",
+            "item_optional": false, 
+            "item_default": [],
+            "list_item_spec":
+            {
+                "item_name": "ddns_domain",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {},
+                "map_item_spec": [ 
+                { 
+                    "item_name": "name",
+                    "item_type": "string",
+                    "item_optional": false,
+                    "item_default": ""
+                },
+
+                { 
+                    "item_name": "key_name",
+                    "item_type": "string",
+                    "item_optional": true,
+                    "item_default": "" 
+                },
+    
+                {
+                    "item_name": "dns_servers",
+                    "item_type": "list",
+                    "item_optional": false, 
+                    "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "dns_server",
+                        "item_type": "map",
+                        "item_optional": false, 
+                        "item_default": {},
+                        "map_item_spec": [ 
+                        { 
+                            "item_name": "hostname",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "ip_address",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "port",
+                            "item_type": "integer",
+                            "item_optional": true,
+                            "item_default": 53 
+                        }]
+                    }
+                }]
+            }
+        }]
+    },
+
+    {
+        "item_name": "reverse_ddns",
+        "item_type": "map",
+        "item_optional": false,
+         "item_default": {},
+         "map_item_spec": [ 
+         { 
+            "item_name": "ddns_domains",
+            "item_type": "list",
+            "item_optional": false, 
+            "item_default": [],
+            "list_item_spec":
+            {
+                "item_name": "ddns_domain",
+                "item_type": "map",
+                "item_optional": false,
+                "item_default": {},
+                "map_item_spec": [ 
+                { 
+                    "item_name": "name",
+                    "item_type": "string",
+                    "item_optional": false,
+                    "item_default": ""
+                },
+
+                { 
+                    "item_name": "key_name",
+                    "item_type": "string",
+                    "item_optional": true,
+                    "item_default": "" 
+                },
+    
+                {
+                    "item_name": "dns_servers",
+                    "item_type": "list",
+                    "item_optional": false, 
+                    "item_default": [],
+                    "list_item_spec":
+                    {
+                        "item_name": "dns_server",
+                        "item_type": "map",
+                        "item_optional": false, 
+                        "item_default": {},
+                        "map_item_spec": [ 
+                        { 
+                            "item_name": "hostname",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "ip_address",
+                            "item_type": "string",
+                            "item_optional": true,
+                            "item_default": ""
+                        },
+                        { 
+                            "item_name": "port",
+                            "item_type": "integer",
+                            "item_optional": true,
+                            "item_default": 53 
+                        }]
+                    }
+                }]
+            }
+        }]
+    }],
+
     "commands": [
-      {
-        "command_name": "shutdown",
-        "command_description": "Shut down the DHCP-DDNS service",
-        "command_args": [
-          {
-            "item_name": "pid",
-            "item_type": "integer",
-            "item_optional": true
-          }
-        ]
-      }
+        {
+            "command_name": "shutdown",
+            "command_description": "Shuts down DHCPv6 server.",
+            "command_args": [
+            ]
+        }
     ]
   }
 }
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index 501fe96..593292b 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -57,6 +57,7 @@ d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
 d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d_cfg_mgr.cc ../d_cfg_mgr.h
+d2_unittests_SOURCES += ../d2_config.cc ../d2_config.h
 d2_unittests_SOURCES += ../d2_cfg_mgr.cc ../d2_cfg_mgr.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
@@ -64,6 +65,7 @@ d2_unittests_SOURCES += d2_process_unittests.cc
 d2_unittests_SOURCES += d_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
 d2_unittests_SOURCES += d_cfg_mgr_unittests.cc
+d2_unittests_SOURCES += d2_cfg_mgr_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
diff --git a/src/bin/d2/tests/d2_cfg_mgr_unittests.cc b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc
new file mode 100644
index 0000000..77c14bb
--- /dev/null
+++ b/src/bin/d2/tests/d2_cfg_mgr_unittests.cc
@@ -0,0 +1,891 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config/module_spec.h>
+#include <d2/d2_config.h>
+#include <d2/d2_cfg_mgr.h>
+#include <d_test_stubs.h>
+
+#include <boost/foreach.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+namespace {
+
+/// @brief Test fixture class for testing D2CfgMgr class.
+/// It maintains an member instance of D2CfgMgr and provides methods for
+/// converting JSON strings to configuration element sets, checking parse
+/// results, and accessing the configuration context.
+class D2CfgMgrTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    D2CfgMgrTest():cfg_mgr_(new D2CfgMgr) {
+    }
+
+    /// @brief Destructor
+    ~D2CfgMgrTest() {
+    }
+
+    /// @brief Configuration manager instance.
+    D2CfgMgrPtr cfg_mgr_;
+};
+
+/// @brief Tests that the spec file is valid.
+/// Verifies that the BIND10 DHCP-DDNS configuration specification file
+//  is valid.
+TEST(D2SpecTest, basicSpecTest) {
+    ASSERT_NO_THROW(isc::config::moduleSpecFromFile("../dhcp-ddns.spec"));
+}
+
+/// @brief Convenience function which compares the contents of the given
+/// DnsServerInfo against the given set of values.
+///
+/// It is structured in such a way that each value is checked, and output
+/// is generate for all that do not match.
+///
+/// @param server is a pointer to the server to check against.
+/// @param hostname is the value to compare against server's hostname_.
+/// @param ip_address is the string value to compare against server's
+/// ip_address_.
+/// @param port is the value to compare against server's port.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkServer(DnsServerInfoPtr server, const char* hostname,
+                 const char *ip_address, uint32_t port)
+{
+    // Return value, assume its a match.
+    bool result = true;
+
+    if (!server)
+    {
+        EXPECT_TRUE(server);
+        return false;
+    }
+
+    // Check hostname.
+    if (server->getHostname() != hostname) {
+        EXPECT_EQ(server->getHostname(),hostname);
+        result = false;
+    }
+
+    // Check IP address.
+    if (server->getIpAddress().toText() != ip_address) {
+        EXPECT_EQ(server->getIpAddress().toText(), ip_address);
+        result = false;
+    }
+
+    // Check port.
+    if (server->getPort() !=  port) {
+        EXPECT_EQ (server->getPort(), port);
+        result = false;
+    }
+
+    return (result);
+}
+
+/// @brief Test fixture class for testing DnsServerInfo parsing.
+class DnsServerInfoTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    DnsServerInfoTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~DnsServerInfoTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        servers_.reset(new DnsServerInfoStorage());
+        parser_.reset(new DnsServerInfoParser("test", servers_));
+    }
+
+    /// @brief Storage for "committing" servers.
+    DnsServerInfoStoragePtr servers_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
+/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
+/// It verifies that:
+/// 1. Specifying both a hostname and an ip address is not allowed.
+/// 2. Specifying both blank a hostname and blank ip address is not allowed.
+/// 3. Specifying a negative port number is not allowed.
+TEST_F(DnsServerInfoTest, invalidEntyTests) {
+    // Create a config in which both host and ip address are supplied.
+    // Verify that it builds without throwing but commit fails.
+    std::string config = "{ \"hostname\": \"pegasus.tmark\", "
+                         "  \"ip_address\": \"127.0.0.1\" } ";
+    ASSERT_NO_THROW(fromJSON(config));
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Neither host nor ip address supplied
+    // Verify that it builds without throwing but commit fails.
+    config = "{ \"hostname\": \"\", "
+             "  \"ip_address\": \"\" } ";
+    ASSERT_NO_THROW(fromJSON(config));
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Create a config with a negative port number.
+    // Verify that build fails.
+    config = "{ \"ip_address\": \"192.168.5.6\" ,"
+             "  \"port\": -100 }";
+    ASSERT_NO_THROW(fromJSON(config));
+    EXPECT_THROW (parser_->build(config_set_), isc::BadValue);
+}
+
+/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo
+/// when given a valid combination of entries.
+/// It verifies that:
+/// 1. A DnsServerInfo entry is correctly made, when given only a hostname.
+/// 2. A DnsServerInfo entry is correctly made, when given ip address and port.
+/// 3. A DnsServerInfo entry is correctly made, when given only an ip address.
+TEST_F(DnsServerInfoTest, validEntyTests) {
+    // Valid entries for dynamic host
+    std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    int count =  servers_->size();
+    EXPECT_EQ(count, 1);
+
+    // Verify the server exists and has the correct values.
+    DnsServerInfoPtr server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "pegasus.tmark",
+                            DnsServerInfo::empty_ip_str,
+                            DnsServerInfo::standard_dns_port));
+
+    // Start over for a new test.
+    reset();
+
+    // Valid entries for static ip
+    config = " { \"ip_address\": \"127.0.0.1\" , "
+             "  \"port\": 100 }";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    count =  servers_->size();
+    EXPECT_EQ(count, 1);
+
+    // Verify the server exists and has the correct values.
+    server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    // Start over for a new test.
+    reset();
+
+    // Valid entries for static ip, no port
+    config = " { \"ip_address\": \"192.168.2.5\" }";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that it builds and commits without throwing.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_NO_THROW(parser_->commit());
+
+    // Verify the correct number of servers are present
+    count =  servers_->size();
+    EXPECT_EQ(count, 1);
+
+    // Verify the server exists and has the correct values.
+    server = (*servers_)[0];
+    EXPECT_TRUE(checkServer(server, "", "192.168.2.5",
+                            DnsServerInfo::standard_dns_port));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
+/// entries is detected.
+TEST_F(ConfigParseTest, invalidServerList) {
+    // Construct a list of servers with an invalid server entry.
+    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
+                        "{ \"hostname\": \"\" }, "
+                        "{ \"hostname\": \"three.tmark\" } ]";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create the server storage and list parser.
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
+
+    // Verify that the list builds without errors.
+    ASSERT_NO_THROW(parser->build(config_set_));
+
+    // Verify that the list commit fails.
+    EXPECT_THROW(parser->commit(), D2CfgError);
+}
+
+/// @brief Verifies that a list of DnsServerInfo entries parses correctly given
+/// a valid configuration.
+TEST_F(ConfigParseTest, validServerList) {
+    // Create a valid list of servers.
+    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
+                        "{ \"hostname\": \"two.tmark\" }, "
+                        "{ \"hostname\": \"three.tmark\" } ]";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create the server storage and list parser.
+    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));
+
+    // Verfiy that the list builds and commits without error.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify that the server storage contains the correct number of servers.
+    int count =  servers->size();
+    EXPECT_EQ(count, 3);
+
+    // Verify the first server exists and has the correct values.
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(checkServer(server, "one.tmark", DnsServerInfo::empty_ip_str,
+                            DnsServerInfo::standard_dns_port));
+
+    // Verify the second server exists and has the correct values.
+    server = (*servers)[1];
+    EXPECT_TRUE(checkServer(server, "two.tmark", DnsServerInfo::empty_ip_str,
+                            DnsServerInfo::standard_dns_port));
+
+    // Verify the third server exists and has the correct values.
+    server = (*servers)[2];
+    EXPECT_TRUE(checkServer(server, "three.tmark", DnsServerInfo::empty_ip_str,
+                            DnsServerInfo::standard_dns_port));
+}
+
+/// @brief Tests the enforcement of data validation when parsing DdnsDomains.
+/// It verifies that:
+/// 1. Domain storage cannot be null when constructing a DdnsDomainParser.
+/// 2. The name entry is not optional.
+/// 3. The server list man not be empty.
+/// 4. That a mal-formed server entry is detected.
+TEST_F(ConfigParseTest, invalidDdnsDomainEntry) {
+    // Verify that attempting to construct the parser with null storage fails.
+    DdnsDomainStoragePtr domains;
+    ASSERT_THROW(new DdnsDomainParser("test", domains), D2CfgError);
+
+    // Create domain storage for the parser, and then instantiate the
+    // parser.
+    domains.reset(new DdnsDomainStorage());
+    DdnsDomainParser *parser = NULL;
+    ASSERT_NO_THROW(parser = new DdnsDomainParser("test", domains));
+
+    // Create a domain configuration without a name
+    std::string config = "{  \"key_name\": \"d2_key.tmark.org\" , "
+                         "  \"dns_servers\" : [ "
+                         "  {  \"ip_address\": \"127.0.0.1\" , "
+                         "    \"port\": 100 },"
+                         "  { \"ip_address\": \"127.0.0.2\" , "
+                         "    \"port\": 200 },"
+                         "  {  \"ip_address\": \"127.0.0.3\" , "
+                         "    \"port\": 300 } ] } ";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that the domain configuration builds but commit fails.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_THROW(parser->commit(), isc::dhcp::DhcpConfigError);
+
+    // Create a domain configuration with an empty server list.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "   ] } ";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that the domain configuration build fails.
+    ASSERT_THROW(parser->build(config_set_), D2CfgError);
+
+    // Create a domain configuration with a mal-formed server entry.
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "  {  \"ip_address\": \"127.0.0.3\" , "
+             "    \"port\": -1 } ] } ";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that the domain configuration build fails.
+    ASSERT_THROW(parser->build(config_set_), isc::BadValue);
+}
+
+
+/// @brief Verifies the basics of parsing DdnsDomains.
+/// It verifies that:
+/// 1. Valid construction of DdnsDomainParser functions.
+/// 2. Given a valid, configuration entry, DdnsDomainParser parses
+/// correctly.
+/// (It indirectly verifies the operation of DdnsDomainStorage).
+TEST_F(ConfigParseTest, ddnsDomainParsing) {
+    // Create a valid domain configuration entry containing three valid
+    // servers.
+    std::string config =
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  {  \"ip_address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip_address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  {  \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } ";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create domain storage for the parser, and then instantiate the
+    // parser.  This verifies that valid parser construction.
+    DdnsDomainStoragePtr domains(new DdnsDomainStorage());
+    DdnsDomainParser *parser = NULL;
+    ASSERT_NO_THROW(parser = new DdnsDomainParser("test", domains));
+
+    // Verify that the domain configuration builds and commits without error.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify that the domain storage contains the correct number of domains.
+    int count =  domains->size();
+    EXPECT_EQ(count, 1);
+
+    // Verify that the expected domain exists and can be retrieved from
+    // the storage.
+    DdnsDomainStorage::iterator gotit = domains->find("tmark.org");
+    ASSERT_TRUE(gotit != domains->end());
+    DdnsDomainPtr& domain = gotit->second;
+
+    // Verify the name and key_name values.
+    EXPECT_EQ(domain->getName(), "tmark.org");
+    EXPECT_EQ(domain->getKeyName(), "d2_key.tmark.org");
+
+    // Verify that the server list exists and contains the correct number of
+    // servers.
+    const DnsServerInfoStoragePtr& servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(count, 3);
+
+    // Fetch each server and verify its contents.
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+}
+
+/// @brief Tests the fundamentals of parsing DdnsDomain lists.
+/// This test verifies that given a valid domain list configuration
+/// it will accurately parse and populate each domain in the list.
+TEST_F(ConfigParseTest, DdnsDomainListParsing) {
+    // Create a valid domain list configuration, with two domains
+    // that have three servers each.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" , "
+                        "    \"port\": 100 },"
+                        "  { \"ip_address\": \"127.0.0.2\" , "
+                        "    \"port\": 200 },"
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"billcat.net\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.4\" , "
+                        "    \"port\": 400 },"
+                        "  { \"ip_address\": \"127.0.0.5\" , "
+                        "    \"port\": 500 },"
+                        "  { \"ip_address\": \"127.0.0.6\" , "
+                        "    \"port\": 600 } ] } "
+                        "] ";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create domain storage for the parser, and then instantiate the
+    // parser.
+    DdnsDomainStoragePtr domains(new DdnsDomainStorage());
+    DdnsDomainListParser *parser = NULL;
+    ASSERT_NO_THROW(parser = new DdnsDomainListParser("test", domains));
+
+    // Verify that the domain configuration builds and commits without error.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify that the domain storage contains the correct number of domains.
+    int count =  domains->size();
+    EXPECT_EQ(count, 2);
+
+    // Verify that the first domain exists and can be retrieved.
+    DdnsDomainStorage::iterator gotit = domains->find("tmark.org");
+    ASSERT_TRUE(gotit != domains->end());
+    DdnsDomainPtr& domain = gotit->second;
+
+    // Verify the name and key_name values of the first domain.
+    EXPECT_EQ(domain->getName(), "tmark.org");
+    EXPECT_EQ(domain->getKeyName(), "d2_key.tmark.org");
+
+    // Verify the each of the first domain's servers
+    DnsServerInfoStoragePtr servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(count, 3);
+
+    DnsServerInfoPtr server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
+
+    // Verify second domain
+    gotit = domains->find("billcat.net");
+    ASSERT_TRUE(gotit != domains->end());
+    domain = gotit->second;
+
+    // Verify the name and key_name values of the second domain.
+    EXPECT_EQ(domain->getName(), "billcat.net");
+    EXPECT_EQ(domain->getKeyName(), "d2_key.billcat.net");
+
+    // Verify the each of second domain's servers
+    servers = domain->getServers();
+    EXPECT_TRUE(servers);
+    count =  servers->size();
+    EXPECT_EQ(count, 3);
+
+    server = (*servers)[0];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400));
+
+    server = (*servers)[1];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500));
+
+    server = (*servers)[2];
+    EXPECT_TRUE(server);
+    EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600));
+}
+
+/// @brief Tests that a domain list configuration cannot contain duplicates.
+TEST_F(ConfigParseTest, duplicateDomainTest) {
+    // Create a domain list configuration that contains two domains with
+    // the same name.
+    std::string config =
+                        "[ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        ", "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.3\" , "
+                        "    \"port\": 300 } ] } "
+                        "] ";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create the domain storage pointer and the parser.
+    DdnsDomainStoragePtr domains(new DdnsDomainStorage());
+    DdnsDomainListParser *parser = NULL;
+    ASSERT_NO_THROW(parser = new DdnsDomainListParser("test", domains));
+
+    // Verify that the parse build succeeds but the commit fails.
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_THROW(parser->commit(), D2CfgError);
+}
+
+/// @brief Tests construction of D2CfgMgr
+/// This test verifies that a D2CfgMgr constructs properly.
+TEST(D2CfgMgr, construction) {
+    D2CfgMgr *cfg_mgr = NULL;
+
+    // Verify that configuration manager constructions without error.
+    ASSERT_NO_THROW(cfg_mgr=new D2CfgMgr());
+
+    // Verify that the context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr->getD2CfgContext());
+    EXPECT_TRUE(context);
+
+    // Verify that the forward manager can be retrieved and is not null.
+    EXPECT_TRUE(context->getForwardMgr());
+
+    // Verify that the reverse manager can be retrieved and is not null.
+    EXPECT_TRUE(context->getReverseMgr());
+
+    // Verify that the manager can be destructed without error.
+    EXPECT_NO_THROW(delete cfg_mgr);
+}
+
+/// @brief Tests the parsing of a complete, valid DHCP-DDNS configuration.
+/// This tests passes the configuration into an instance of D2CfgMgr just
+/// as it would be done by d2_process in response to a configuration update
+/// event.
+TEST_F(D2CfgMgrTest, fullConfigTest) {
+    // Create a configuration with all of application level parameters, plus
+    // both the forward and reverse ddns managers.  Both managers have two
+    // domains with three servers per domain.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.tmark\" } , "
+                        "  { \"hostname\": \"two.tmark\" } , "
+                        "  { \"hostname\": \"three.tmark\"} "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"billcat.net\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"four.billcat\" } , "
+                        "  { \"hostname\": \"five.billcat\" } , "
+                        "  { \"hostname\": \"six.billcat\" } "
+                        "  ] } "
+                        "] },"
+                        "\"reverse_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.rev\" } , "
+                        "  { \"hostname\": \"two.rev\" } , "
+                        "  { \"hostname\": \"three.rev\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \" 0.247.106.in.addr.arpa.\" , "
+                        "  \"key_name\": \"d2_key.billcat.net\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"four.rev\" }, "
+                        "  { \"hostname\": \"five.rev\" } , "
+                        "  { \"hostname\": \"six.rev\" } "
+                        "  ] } "
+                        "] } }";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    // Verify that the application level scalars have the proper values.
+    std::string interface;
+    EXPECT_NO_THROW (context->getParam("interface", interface));
+    EXPECT_EQ(interface, "eth1");
+
+    std::string ip_address;
+    EXPECT_NO_THROW (context->getParam("ip_address", ip_address));
+    EXPECT_EQ(ip_address, "192.168.1.33");
+
+    uint32_t port = 0;
+    EXPECT_NO_THROW (context->getParam("port", port));
+    EXPECT_EQ(port, 88);
+
+    // Verify that the forward manager can be retrieved.
+    DdnsDomainListMgrPtr mgr = context->getForwardMgr();
+    ASSERT_TRUE(mgr);
+
+    // Verify that the forward manager has the correct number of domains.
+    DdnsDomainStoragePtr domains = mgr->getDomains();
+    ASSERT_TRUE(domains);
+    int count =  domains->size();
+    EXPECT_EQ(count, 2);
+
+    // Verify that the server count in each of the forward manager domains.
+    // NOTE that since prior tests have validated server parsing, we are are
+    // assuming that the servers did in fact parse correctly if the correct
+    // number of them are there.
+    DdnsDomainPtrPair domain_pair;
+    BOOST_FOREACH(domain_pair, (*domains)) {
+        DdnsDomainPtr domain = domain_pair.second;
+        DnsServerInfoStoragePtr servers = domain->getServers();
+        count = servers->size();
+        EXPECT_TRUE(servers);
+        EXPECT_EQ(count, 3);
+    }
+
+    // Verify that the reverse manager can be retrieved.
+    mgr = context->getReverseMgr();
+    ASSERT_TRUE(mgr);
+
+    // Verify that the reverse manager has the correct number of domains.
+    domains = mgr->getDomains();
+    count =  domains->size();
+    EXPECT_EQ(count, 2);
+
+    // Verify that the server count in each of the reverse manager domains.
+    // NOTE that since prior tests have validated server parsing, we are are
+    // assuming that the servers did in fact parse correctly if the correct
+    // number of them are there.
+    BOOST_FOREACH(domain_pair, (*domains)) {
+        DdnsDomainPtr domain = domain_pair.second;
+        DnsServerInfoStoragePtr servers = domain->getServers();
+        count = servers->size();
+        EXPECT_TRUE(servers);
+        EXPECT_EQ(count, 3);
+    }
+}
+
+/// @brief Tests the basics of the D2CfgMgr FQDN-domain matching
+/// This test uses a valid configuration to exercise the D2CfgMgr
+/// forward FQDN-to-domain matching.
+/// It verifies that:
+/// 1. Given an FQDN which exactly matches a domain's name, that domain is
+/// returned as match.
+/// 2. Given a FQDN for sub-domain in the list, returns the proper match.
+/// 3. Given a FQDN that matches no domain name, returns the wild card domain
+/// as a match.
+TEST_F(D2CfgMgrTest, forwardMatchTest) {
+    // Create  configuration with one domain, one sub domain, and the wild
+    // card.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"one.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.2\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"global.net\" } "
+                        "  ] } "
+                        "] } }";
+
+    ASSERT_NO_THROW(fromJSON(config));
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify that an exact match works.
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    // Verify that an exact match works.
+    EXPECT_TRUE(cfg_mgr_->matchForward("one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that a FQDN for sub-domain matches.
+    EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    // Verify that a FQDN for sub-domain matches.
+    EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that an FQDN with no match, returns the wild card domain.
+    EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN throws.
+    ASSERT_THROW(cfg_mgr_->matchForward("", match), D2CfgError);
+}
+
+/// @brief Tests domain matching when there is no wild card domain.
+/// This test verifies that matches are found only for FQDNs that match
+/// some or all of a domain name.  FQDNs without matches should not return
+/// a match.
+TEST_F(D2CfgMgrTest, matchNoWildcard) {
+    // Create a configuration with one domain, one sub-domain, and NO wild card.
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        ", "
+                        "{ \"name\": \"one.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.2\" } "
+                        "  ] } "
+                        "] } }";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify that full or partial matches, still match.
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match));
+    EXPECT_EQ("tmark.org", match->getName());
+
+    EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match));
+    EXPECT_EQ("one.tmark.org", match->getName());
+
+    // Verify that a FQDN with no match, fails to match.
+    EXPECT_FALSE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+}
+
+/// @brief Tests domain matching when there is ONLY a wild card domain.
+/// This test verifies that any FQDN matches the wild card.
+TEST_F(D2CfgMgrTest, matchAll) {
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        "] } }";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    // Verify that wild card domain is returned for any FQDN.
+    DdnsDomainPtr match;
+    EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match));
+    EXPECT_EQ("*", match->getName());
+    EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN still throws.
+    ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
+
+}
+
+/// @brief Tests the basics of the D2CfgMgr reverse FQDN-domain matching
+/// This test uses a valid configuration to exercise the D2CfgMgr's
+/// reverse FQDN-to-domain matching.
+/// It verifies that:
+/// 1. Given an FQDN which exactly matches a domain's name, that domain is
+/// returned as match.
+/// 2. Given a FQDN for sub-domain in the list, returns the proper match.
+/// 3. Given a FQDN that matches no domain name, returns the wild card domain
+/// as a match.
+TEST_F(D2CfgMgrTest, matchReverse) {
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"reverse_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"100.168.192.in-addr.arpa\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] }, "
+                        "{ \"name\": \"168.192.in-addr.arpa\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] }, "
+                        "{ \"name\": \"*\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"ip_address\": \"127.0.0.1\" } "
+                        "  ] } "
+                        "] } }";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that we can parse the configuration.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    ASSERT_TRUE(checkAnswer(0));
+
+    // Verify that the D2 context can be retrieved and is not null.
+    D2CfgContextPtr context;
+    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());
+
+    DdnsDomainPtr match;
+    // Verify an exact match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("100.168.192.in-addr.arpa", match));
+    EXPECT_EQ("100.168.192.in-addr.arpa", match->getName());
+
+    // Verify a sub-domain match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("27.100.168.192.in-addr.arpa", match));
+    EXPECT_EQ("100.168.192.in-addr.arpa", match->getName());
+
+    // Verify a sub-domain match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("30.133.168.192.in-addr.arpa", match));
+    EXPECT_EQ("168.192.in-addr.arpa", match->getName());
+
+    // Verify a wild card match.
+    EXPECT_TRUE(cfg_mgr_->matchReverse("shouldbe.wildcard", match));
+    EXPECT_EQ("*", match->getName());
+
+    // Verify that an attempt to match an empty FQDN throws.
+    ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
+}
+
+
+} // end of anonymous namespace
diff --git a/src/bin/d2/tests/d2_controller_unittests.cc b/src/bin/d2/tests/d2_controller_unittests.cc
index a2b3374..f4b4d41 100644
--- a/src/bin/d2/tests/d2_controller_unittests.cc
+++ b/src/bin/d2/tests/d2_controller_unittests.cc
@@ -54,7 +54,7 @@ public:
 };
 
 /// @brief Basic Controller instantiation testing.
-/// Verfies that the controller singleton gets created and that the
+/// Verifies that the controller singleton gets created and that the
 /// basic derivation from the base class is intact.
 TEST_F(D2ControllerTest, basicInstanceTesting) {
     // Verify the we can the singleton instance can be fetched and that
@@ -80,12 +80,12 @@ TEST_F(D2ControllerTest, basicInstanceTesting) {
 }
 
 /// @brief Tests basic command line processing.
-/// Verfies that:
+/// Verifies that:
 /// 1. Standard command line options are supported.
 /// 2. Invalid options are detected.
 TEST_F(D2ControllerTest, commandLineArgs) {
-    char* argv[] = { const_cast<char*>("progName"), 
-                     const_cast<char*>("-s"), 
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
                      const_cast<char*>("-v") };
     int argc = 3;
 
@@ -101,7 +101,7 @@ TEST_F(D2ControllerTest, commandLineArgs) {
     EXPECT_TRUE(checkVerbose(true));
 
     // Verify that an unknown option is detected.
-    char* argv2[] = { const_cast<char*>("progName"), 
+    char* argv2[] = { const_cast<char*>("progName"),
                       const_cast<char*>("-x") };
     argc = 2;
     EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage);
@@ -119,8 +119,8 @@ TEST_F(D2ControllerTest, initProcessTesting) {
 /// launches with a valid, stand-alone command line and no simulated errors.
 TEST_F(D2ControllerTest, launchNormalShutdown) {
     // command line to run standalone
-    char* argv[] = { const_cast<char*>("progName"), 
-                     const_cast<char*>("-s"), 
+    char* argv[] = { const_cast<char*>("progName"),
+                     const_cast<char*>("-s"),
                      const_cast<char*>("-v") };
     int argc = 3;
 
@@ -165,10 +165,9 @@ TEST_F(D2ControllerTest, configUpdateTests) {
     ASSERT_NO_THROW(initProcess());
     EXPECT_TRUE(checkProcess());
 
-    // Create a configuration set. Content is arbitrary, just needs to be
-    // valid JSON.
-    std::string config = "{ \"test-value\": 1000 } ";
-    isc::data::ElementPtr config_set = isc::data::Element::fromJSON(config);
+    // Create a configuration set using a small, valid D2 configuration.
+    isc::data::ElementPtr config_set =
+                                isc::data::Element::fromJSON(valid_d2_config);
 
     // We are not stand-alone, so configuration should be rejected as there is
     // no session.  This is a pretty contrived situation that shouldn't be
@@ -182,6 +181,13 @@ TEST_F(D2ControllerTest, configUpdateTests) {
     answer = DControllerBase::configHandler(config_set);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
+
+    // Use an invalid configuration to verify parsing error return.
+    std::string config = "{ \"bogus\": 1000 } ";
+    config_set = isc::data::Element::fromJSON(config);
+    answer = DControllerBase::configHandler(config_set);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
 }
 
 /// @brief Command execution tests.
diff --git a/src/bin/d2/tests/d2_process_unittests.cc b/src/bin/d2/tests/d2_process_unittests.cc
index 40c85f3..c507f2c 100644
--- a/src/bin/d2/tests/d2_process_unittests.cc
+++ b/src/bin/d2/tests/d2_process_unittests.cc
@@ -15,6 +15,7 @@
 
 #include <config/ccsession.h>
 #include <d2/d2_process.h>
+#include <d_test_stubs.h>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
@@ -83,17 +84,23 @@ TEST(D2Process, construction) {
 }
 
 /// @brief Verifies basic configure method behavior.
-/// @TODO This test is simplistic and will need to be augmented as configuration
+///  This test is simplistic and will need to be augmented as configuration
 /// ability is implemented.
 TEST_F(D2ProcessTest, configure) {
-    // Verify that given a configuration "set", configure returns
-    // a successful response.
     int rcode = -1;
-    string config = "{ \"test-value\": 1000 } ";
-    isc::data::ElementPtr json = isc::data::Element::fromJSON(config);
+
+    // Use a small, valid D2 configuration to verify successful parsing.
+    isc::data::ElementPtr json = isc::data::Element::fromJSON(valid_d2_config);
     isc::data::ConstElementPtr answer = process_->configure(json);
     isc::config::parseAnswer(rcode, answer);
     EXPECT_EQ(0, rcode);
+
+    // Use an invalid configuration to verify parsing error return.
+    string config = "{ \"bogus\": 1000 } ";
+    json = isc::data::Element::fromJSON(config);
+    answer = process_->configure(json);
+    isc::config::parseAnswer(rcode, answer);
+    EXPECT_EQ(1, rcode);
 }
 
 /// @brief Verifies basic command method behavior.
diff --git a/src/bin/d2/tests/d_cfg_mgr_unittests.cc b/src/bin/d2/tests/d_cfg_mgr_unittests.cc
index bcea450..779f3ba 100644
--- a/src/bin/d2/tests/d_cfg_mgr_unittests.cc
+++ b/src/bin/d2/tests/d_cfg_mgr_unittests.cc
@@ -53,10 +53,11 @@ public:
 };
 
 /// @brief Test fixture class for testing DCfgMgrBase class.
-/// It maintains an member instance of DStubCfgMgr and provides methods for
-/// converting JSON strings to configuration element sets, checking parse
-/// results, and accessing the configuration context.
-class DStubCfgMgrTest : public ::testing::Test {
+/// It maintains an member instance of DStubCfgMgr and derives from
+/// ConfigParseTest fixture, thus providing methods for converting JSON
+/// strings to configuration element sets, checking parse results, and
+/// accessing the configuration context.
+class DStubCfgMgrTest : public ConfigParseTest {
 public:
 
     /// @brief Constructor
@@ -67,41 +68,6 @@ public:
     ~DStubCfgMgrTest() {
     }
 
-    /// @brief Converts a given JSON string into an Element set and stores the
-    /// result the member variable, config_set_.
-    ///
-    /// @param json_text contains the configuration text in JSON format to
-    /// convert.
-    /// @return returns true if the conversion is successful, false otherwise.
-    bool fromJSON(std::string& json_text) {
-        try  {
-            config_set_ = isc::data::Element::fromJSON(json_text);
-        } catch (...) {
-            // This is so we can diagnose parsing mistakes during test
-            // development.
-            std::cerr << "fromJSON failed to parse text" << json_text
-                      << std::endl;
-            return (false);
-        }
-
-        return (true);
-    }
-
-    /// @brief Compares the status in the  parse result stored in member
-    /// variable answer_ to a given value.
-    ///
-    /// @param should_be is an integer against which to compare the status.
-    ///
-    /// @return returns true if the status value is equal to the given value.
-    bool checkAnswer(int should_be) {
-        int rcode = 0;
-        isc::data::ConstElementPtr comment;
-        comment = isc::config::parseAnswer(rcode, answer_);
-        //std::cout << "checkAnswer rcode:" << rcode << " comment: "
-        //          << *comment_ << std::endl;
-        return (rcode == should_be);
-    }
-
     /// @brief Convenience method which returns a DStubContextPtr to the
     /// configuration context.
     ///
@@ -113,12 +79,6 @@ public:
 
     /// @brief Configuration manager instance.
     DStubCfgMgrPtr cfg_mgr_;
-
-    /// @brief Configuration set being tested.
-    isc::data::ElementPtr config_set_;
-
-    /// @brief Results of most recent elemnt parsing.
-    isc::data::ConstElementPtr answer_;
 };
 
 ///@brief Tests basic construction/destruction of configuration manager.
diff --git a/src/bin/d2/tests/d_test_stubs.cc b/src/bin/d2/tests/d_test_stubs.cc
index 5745116..d07419f 100644
--- a/src/bin/d2/tests/d_test_stubs.cc
+++ b/src/bin/d2/tests/d_test_stubs.cc
@@ -21,6 +21,26 @@ using namespace asio;
 namespace isc {
 namespace d2 {
 
+const char* valid_d2_config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"forward_ddns\" : {"
+                        "\"ddns_domains\": [ "
+                        "{ \"name\": \"tmark.org\" , "
+                        "  \"key_name\": \"d2_key.tmark.org\" , "
+                        "  \"dns_servers\" : [ "
+                        "  { \"hostname\": \"one.tmark\" } "
+                        "     ] } ] }, "
+                        "\"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 } ] } "
+                        "] } }";
+
 // Initialize the static failure flag.
 SimFailure::FailureType SimFailure::failure_type_ = SimFailure::ftNoFailure;
 
diff --git a/src/bin/d2/tests/d_test_stubs.h b/src/bin/d2/tests/d_test_stubs.h
index b9a9d2c..73361b5 100644
--- a/src/bin/d2/tests/d_test_stubs.h
+++ b/src/bin/d2/tests/d_test_stubs.h
@@ -28,6 +28,11 @@
 namespace isc {
 namespace d2 {
 
+/// @brief Provides a valid DHCP-DDNS configuraiton for testing basic
+/// parsing fundamentals.
+extern const char* valid_d2_config;
+
+
 /// @brief Class is used to set a globally accessible value that indicates
 /// a specific type of failure to simulate.  Test derivations of base classes
 /// can exercise error handling code paths by testing for specific SimFailure
@@ -85,10 +90,12 @@ public:
         return (false);
     }
 
+    /// @brief Resets the failure type to none.
     static void clear() {
        failure_type_ = ftNoFailure;
     }
 
+    /// @brief Static value for holding the failure type to simulate.
     static enum FailureType failure_type_;
 };
 
@@ -576,6 +583,69 @@ public:
 /// @brief Defines a pointer to DStubCfgMgr.
 typedef boost::shared_ptr<DStubCfgMgr> DStubCfgMgrPtr;
 
+/// @brief Test fixture base class for any fixtures which test parsing.
+/// It provides methods for converting JSON strings to configuration element
+/// sets and checking parse results
+class ConfigParseTest : public ::testing::Test {
+public:
+
+    /// @brief Constructor
+    ConfigParseTest(){
+    }
+
+    /// @brief Destructor
+    ~ConfigParseTest() {
+    }
+
+    /// @brief Converts a given JSON string into an Element set and stores the
+    /// result the member variable, config_set_.
+    ///
+    /// @param json_text contains the configuration text in JSON format to
+    /// convert.
+    /// @return returns true if the conversion is successful, false otherwise.
+    bool fromJSON(std::string& json_text) {
+        try  {
+            config_set_ = isc::data::Element::fromJSON(json_text);
+        } catch (...) {
+            // This is so we can diagnose parsing mistakes during test
+            // development.
+            std::cerr << "fromJSON failed to parse text" << json_text
+                      << std::endl;
+            return (false);
+        }
+
+        return (true);
+    }
+
+    /// @brief Compares the status in the  parse result stored in member
+    /// variable answer_ to a given value.
+    ///
+    /// @param should_be is an integer against which to compare the status.
+    ///
+    /// @return returns true if the status value is equal to the given value.
+    bool checkAnswer(int should_be) {
+        int rcode = 0;
+        isc::data::ConstElementPtr comment;
+        comment = isc::config::parseAnswer(rcode, answer_);
+        // Handy for diagnostics
+        // if (rcode != 0) {
+        //    std::cout << "checkAnswer rcode:" << rcode << " comment: "
+        //          << *comment << std::endl;
+        //}
+        return (rcode == should_be);
+    }
+
+    /// @brief Configuration set being tested.
+    isc::data::ElementPtr config_set_;
+
+    /// @brief Results of most recent element parsing.
+    isc::data::ConstElementPtr answer_;
+};
+
+/// @brief Defines a small but valid DHCP-DDNS compliant configuration for
+/// testing configuration parsing fundamentals.
+extern const char* valid_d2_config;
+
 }; // namespace isc::d2
 }; // namespace isc
 



More information about the bind10-changes mailing list