Browse Source

[2957] Review comments. Added in the ability to configure TSIG Key
information.

Thomas Markwalder 12 years ago
parent
commit
95d6573794

+ 24 - 9
src/bin/d2/d2_cfg_mgr.cc

@@ -30,7 +30,8 @@ namespace d2 {
 
 D2CfgContext::D2CfgContext()
     : forward_mgr_(new DdnsDomainListMgr("forward_mgr")),
-      reverse_mgr_(new DdnsDomainListMgr("reverse_mgr")) {
+      reverse_mgr_(new DdnsDomainListMgr("reverse_mgr")),
+      keys_(new TSIGKeyInfoMap()) {
 }
 
 D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
@@ -43,6 +44,8 @@ D2CfgContext::D2CfgContext(const D2CfgContext& rhs) : DCfgContextBase(rhs) {
         reverse_mgr_.reset(new DdnsDomainListMgr(rhs.reverse_mgr_->getName()));
         reverse_mgr_->setDomains(rhs.reverse_mgr_->getDomains());
     }
+
+    keys_ = rhs.keys_; 
 }
 
 D2CfgContext::~D2CfgContext() {
@@ -51,6 +54,14 @@ D2CfgContext::~D2CfgContext() {
 // *********************** D2CfgMgr  *************************
 
 D2CfgMgr::D2CfgMgr() : DCfgMgrBase(DCfgContextBasePtr(new D2CfgContext())) {
+    // TSIG keys need to parse before the Domains, so we can catch Domains
+    // that specify undefined keys. Create the necessary parsing order now.
+    addToParseOrder("interface");
+    addToParseOrder("ip_address");
+    addToParseOrder("port");
+    addToParseOrder("tsig_keys");
+    addToParseOrder("forward_ddns");
+    addToParseOrder("reverse_ddns");
 }
 
 D2CfgMgr::~D2CfgMgr() {
@@ -58,13 +69,13 @@ D2CfgMgr::~D2CfgMgr() {
 
 bool
 D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
-    if (fqdn == "") {
+    if (fqdn.empty()) {
         // This is a programmatic error and should not happen.
-        isc_throw (D2CfgError, "matchForward passed an empty fqdn");
+        isc_throw(D2CfgError, "matchForward passed an empty fqdn");
     }
 
     // Fetch the forward manager from the D2 context.
-    DdnsDomainListMgrPtr& mgr = getD2CfgContext()->getForwardMgr();
+    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getForwardMgr();
 
     // Call the manager's match method and return the result.
     return (mgr->matchDomain(fqdn, domain));
@@ -72,13 +83,13 @@ D2CfgMgr::matchForward(const std::string& fqdn, DdnsDomainPtr& domain) {
 
 bool
 D2CfgMgr::matchReverse(const std::string& fqdn, DdnsDomainPtr& domain) {
-    if (fqdn == "") {
+    if (fqdn.empty()) {
         // This is a programmatic error and should not happen.
-        isc_throw (D2CfgError, "matchReverse passed a null or empty fqdn");
+        isc_throw(D2CfgError, "matchReverse passed a null or empty fqdn");
     }
 
     // Fetch the reverse manager from the D2 context.
-    DdnsDomainListMgrPtr& mgr = getD2CfgContext()->getReverseMgr();
+    DdnsDomainListMgrPtr mgr = getD2CfgContext()->getReverseMgr();
 
     // Call the manager's match method and return the result.
     return (mgr->matchDomain(fqdn, domain));
@@ -99,10 +110,14 @@ D2CfgMgr::createConfigParser(const std::string& config_id) {
         parser = new Uint32Parser(config_id, context->getUint32Storage());
     } else if (config_id ==  "forward_ddns") {
         parser = new DdnsDomainListMgrParser("forward_mgr",
-                                             context->getForwardMgr());
+                                             context->getForwardMgr(),
+                                             context->getKeys());
     } else if (config_id ==  "reverse_ddns") {
         parser = new DdnsDomainListMgrParser("reverse_mgr",
-                                             context->getReverseMgr());
+                                             context->getReverseMgr(),
+                                             context->getKeys());
+    } else if (config_id ==  "tsig_keys") {
+        parser = new TSIGKeyInfoListParser("tsig_key_list", context->getKeys());
     } else {
         isc_throw(NotImplemented,
                   "parser error: D2CfgMgr parameter not supported: "

+ 16 - 5
src/bin/d2/d2_cfg_mgr.h

@@ -28,6 +28,7 @@ namespace isc {
 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 DHCP-DDNS specific information that needs to be accessible
@@ -45,23 +46,30 @@ public:
     ///
     /// @return returns a raw pointer to the new clone.
     virtual D2CfgContext* clone() {
-            return (new D2CfgContext(*this));
+        return (new D2CfgContext(*this));
     }
 
     /// @brief Fetches the forward DNS domain list manager.
     ///
-    /// @return returns a pointer reference to the forward manager.
-    DdnsDomainListMgrPtr& getForwardMgr() {
+    /// @return returns a pointer 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 returns a pointer to the reverse manager.
+    DdnsDomainListMgrPtr getReverseMgr() {
         return (reverse_mgr_);
     }
 
+    /// @brief Fetches the map of TSIG keys.
+    ///
+    /// @return returns a pointer to the key map.
+    TSIGKeyInfoMapPtr getKeys() {
+        return (keys_);
+    }
+
 protected:
     /// @brief Copy constructor for use by derivations in clone().
     D2CfgContext(const D2CfgContext& rhs);
@@ -75,6 +83,9 @@ private:
 
     /// @brief Reverse domain list manager.
     DdnsDomainListMgrPtr reverse_mgr_;
+
+    /// @brief Storage for the map of TSIGKeyInfos
+    TSIGKeyInfoMapPtr keys_;
 };
 
 /// @brief Defines a pointer for DdnsDomain instances.

+ 252 - 112
src/bin/d2/d2_config.cc

@@ -24,16 +24,25 @@
 namespace isc {
 namespace d2 {
 
-// *********************** DnsServerInfo  *************************
+// *********************** TSIGKeyInfo  *************************
+
+TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
+                         const std::string& secret)
+    :name_(name), algorithm_(algorithm), secret_(secret) {
+}
+
+TSIGKeyInfo::~TSIGKeyInfo() {
+}
 
-const uint32_t DnsServerInfo::standard_dns_port = 53;
 
-const char* DnsServerInfo::empty_ip_str = "0.0.0.0";
+// *********************** DnsServerInfo  *************************
 
-DnsServerInfo::DnsServerInfo(const std::string& hostname, 
+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), 
+    :hostname_(hostname), ip_address_(ip_address), port_(port),
     enabled_(enabled) {
 }
 
@@ -52,35 +61,31 @@ DdnsDomain::~DdnsDomain() {
 
 // *********************** DdnsDomainLstMgr  *************************
 
+const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";
+
 DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
-    domains_(new DdnsDomainStorage()) {
+    domains_(new DdnsDomainMap()) {
 }
 
 
 DdnsDomainListMgr::~DdnsDomainListMgr () {
 }
 
-void 
-DdnsDomainListMgr::setDomains(DdnsDomainStoragePtr domains) {
+void
+DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
     if (!domains) {
-        isc_throw(D2CfgError, 
+        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;
-        }
+    // Look for the wild card domain. If present, set the member variable
+    // to remember it.  This saves us from having to look for it every time
+    // we attempt a match.
+    DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
+    if (gotit != domains_->end()) {
+            wildcard_domain_ = gotit->second;
     }
 }
 
@@ -96,48 +101,181 @@ DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
     }
 
     // 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 
+    // Continue looking for shorter versions of fqdn so long as no match is
     // found.
-    // @TODO This can surely be optimized, time permitting.  
+    // @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);
+        DdnsDomainMap::iterator gotit = domains_->find(match_name);
         if (gotit != domains_->end()) {
             domain = gotit->second;
-            break;
+            return (true);
         }
 
         start_pos = match_name.find_first_of(".");
         if (start_pos != std::string::npos) {
-            start_pos++;
+            ++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);
-        }
-    } 
+    // There's no match. If they specified a wild card domain use it
+    // otherwise there's no domain for this entry.
+    if (wildcard_domain_) {
+        domain = wildcard_domain_;
+        return (true);
+    }
 
-    return (true);
+    LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
+    return (false);
 }
 
 // *************************** PARSERS ***********************************
 
+// *********************** TSIGKeyInfoParser  *************************
+
+TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
+    TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), keys_(keys), local_scalars_() {
+    if (!keys_) {
+        isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
+                  " key storage cannot be null");
+    }
+}
+
+TSIGKeyInfoParser::~TSIGKeyInfoParser() {
+}
+
+void
+TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
+    isc::dhcp::ConfigPair config_pair;
+    // For each element in the key 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, key_config->mapValue()) {
+        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first));
+        parser->build(config_pair.second);
+        parser->commit();
+    }
+}
+
+isc::dhcp::ParserPtr
+TSIGKeyInfoParser::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 == "algorithm") ||
+        (config_id == "secret")) {
+        parser = new isc::dhcp::StringParser(config_id,
+                                             local_scalars_.getStringStorage());
+    } else {
+        isc_throw(NotImplemented,
+                  "parser error: TSIGKeyInfo parameter not supported: "
+                  << config_id);
+    }
+
+    // Return the new parser instance.
+    return (isc::dhcp::ParserPtr(parser));
+}
+
+void
+TSIGKeyInfoParser::commit() {
+    std::string name;
+    std::string algorithm;
+    std::string secret;
+
+    // Fetch the key configuration's parsed scalar values from parser's
+    // local storage.
+    local_scalars_.getParam("name", name);
+    local_scalars_.getParam("algorithm", algorithm);
+    local_scalars_.getParam("secret", secret);
+
+    // @TODO Validation here is very superficial. This will expand as TSIG
+    // Key use is more fully implemented.
+
+    // Name cannot be blank.
+    if (name.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify name");
+    }
+
+    // Algorithme cannot be blank.
+    if (algorithm.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify algorithm");
+    }
+
+    // Secret cannot be blank.
+    if (secret.empty()) {
+        isc_throw(D2CfgError, "TSIG Key Info must specify secret");
+    }
+
+    // Currently, the premise is that key storage is always empty prior to
+    // parsing so we are always adding keys never replacing them. Duplicates
+    // are not allowed and should be flagged as a configuration error.
+    if (keys_->find(name) != keys_->end()) {
+        isc_throw(D2CfgError, "Duplicate TSIG key specified:" << name);
+    }
+
+    TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
+
+    // Add the new TSIGKeyInfo to the key storage.
+    (*keys_)[name]=key_info;
+}
+
+// *********************** TSIGKeyInfoListParser  *************************
+
+TSIGKeyInfoListParser::TSIGKeyInfoListParser(const std::string& list_name,
+                                       TSIGKeyInfoMapPtr keys)
+    :list_name_(list_name), keys_(keys), parsers_() {
+    if (!keys_) {
+        isc_throw(D2CfgError, "TSIGKeyInfoListParser ctor:"
+                  " key storage cannot be null");
+    }
+}
+
+TSIGKeyInfoListParser::~TSIGKeyInfoListParser(){
+}
+
+void
+TSIGKeyInfoListParser::
+build(isc::data::ConstElementPtr key_list){
+    int i = 0;
+    isc::data::ConstElementPtr key_config;
+    // For each key element in the key list:
+    // 1. Create a parser for the key element.
+    // 2. Invoke the parser's build method passing in the key's
+    // configuration.
+    // 3. Add the parser to a local collection of parsers.
+    BOOST_FOREACH(key_config, key_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 TSIGKeyInfoParser(entry_name,
+                                                            keys_));
+        parser->build(key_config);
+        parsers_.push_back(parser);
+    }
+}
+
+void
+TSIGKeyInfoListParser::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();
+    }
+}
+
 // *********************** DnsServerInfoParser  *************************
 
 DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
-    DnsServerInfoStoragePtr servers) 
+    DnsServerInfoStoragePtr servers)
     : entry_name_(entry_name), servers_(servers), local_scalars_() {
     if (!servers_) {
-        isc_throw(D2CfgError, "DdnsServerInfoParser ctor:"
+        isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
                   " server storage cannot be null");
     }
 }
@@ -145,12 +283,12 @@ DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
 DnsServerInfoParser::~DnsServerInfoParser() {
 }
 
-void 
+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 
+    // 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.
@@ -159,24 +297,24 @@ DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
         parser->build(config_pair.second);
         parser->commit();
     }
-        
+
 }
 
-isc::dhcp::ParserPtr 
+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. 
+    // 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, 
+        parser = new isc::dhcp::StringParser(config_id,
                                              local_scalars_.getStringStorage());
-    } else if (config_id == "port") { 
-        parser = new isc::dhcp::Uint32Parser(config_id, 
+    } 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: " 
+        isc_throw(NotImplemented,
+                  "parser error: DnsServerInfo parameter not supported: "
                   << config_id);
     }
 
@@ -184,50 +322,44 @@ DnsServerInfoParser::createConfigParser(const std::string& config_id) {
     return (isc::dhcp::ParserPtr(parser));
 }
 
-void 
+void
 DnsServerInfoParser::commit() {
     std::string hostname;
     std::string ip_address;
-    uint32_t port = DnsServerInfo::standard_dns_port; 
+    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");
-    } 
+    // Fetch the server configuration's parsed 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 must specify one or the other.
-    if ((hostname == "") && (ip_address == "")) {
-        isc_throw(D2CfgError,
-          "Dns Server must specify either hostname or static IP address");
+    if (hostname.empty() == ip_address.empty()) {
+        isc_throw(D2CfgError, "Dns Server must specify one or the other"
+                  " of hostname and IP address");
     }
 
-    DnsServerInfo* serverInfo = NULL;
-    if (hostname != "") {
+    DnsServerInfoPtr serverInfo;
+    if (!hostname.empty()) {
         // 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);
+        isc::asiolink::IOAddress io_addr(DnsServerInfo::EMPTY_IP_STR);
+        serverInfo.reset(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);
+            serverInfo.reset(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));
+    servers_->push_back(serverInfo);
 }
 
 // *********************** DnsServerInfoListParser  *************************
@@ -244,27 +376,27 @@ DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
 DnsServerInfoListParser::~DnsServerInfoListParser(){
 }
 
-void 
+void
 DnsServerInfoListParser::
 build(isc::data::ConstElementPtr server_list){
     int i = 0;
     isc::data::ConstElementPtr server_config;
-    // For each server element in the server list: 
+    // 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 
+    // 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, 
+        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
                                                             servers_));
         parser->build(server_config);
         parsers_.push_back(parser);
     }
 }
 
-void 
+void
 DnsServerInfoListParser::commit() {
     // Domains must have at least one server.
     if (parsers_.size() == 0) {
@@ -272,7 +404,7 @@ DnsServerInfoListParser::commit() {
     }
 
     // Invoke commit on each server parser. This will cause each one to
-    // create it's server instance and commit it to storage. 
+    // create it's server instance and commit it to storage.
     BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
         parser->commit();
     }
@@ -281,11 +413,12 @@ DnsServerInfoListParser::commit() {
 // *********************** DdnsDomainParser  *************************
 
 DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
-                                   DdnsDomainStoragePtr domains) 
-    : entry_name_(entry_name), domains_(domains), 
+                                   DdnsDomainMapPtr domains,
+                                   TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), domains_(domains), keys_(keys),
     local_servers_(new DnsServerInfoStorage()), local_scalars_() {
     if (!domains_) {
-        isc_throw(D2CfgError, 
+        isc_throw(D2CfgError,
                   "DdnsDomainParser ctor, domain storage cannot be null");
     }
 }
@@ -294,11 +427,11 @@ DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
 DdnsDomainParser::~DdnsDomainParser() {
 }
 
-void 
+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 
+    // 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.
@@ -310,14 +443,14 @@ DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
     }
 }
 
-isc::dhcp::ParserPtr 
+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. 
+    // 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, 
+        (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
@@ -326,7 +459,7 @@ DdnsDomainParser::createConfigParser(const std::string& config_id) {
        parser = new DnsServerInfoListParser(config_id, local_servers_);
     } else {
        isc_throw(NotImplemented,
-                "parser error: DdnsDomain parameter not supported: " 
+                "parser error: DdnsDomain parameter not supported: "
                 << config_id);
     }
 
@@ -334,16 +467,16 @@ DdnsDomainParser::createConfigParser(const std::string& config_id) {
     return (isc::dhcp::ParserPtr(parser));
 }
 
-void 
+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); 
+    local_scalars_.getParam("name", name);
 
     // Blank domain names are not allowed.
-    if (name == "") {
+    if (name.empty()) {
         isc_throw(D2CfgError, "Domain name cannot be blank");
     }
 
@@ -352,12 +485,18 @@ DdnsDomainParser::commit() {
     // 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); 
+        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_);
+    // Key name is optional. If it is not blank, then validate it against
+    // the defined list of keys.
+    local_scalars_.getParam("key_name", key_name, DCfgContextBase::OPTIONAL);
+    if (!key_name.empty()) {
+        if ((!keys_) || (keys_->find(key_name) == keys_->end())) {
+            isc_throw(D2CfgError, "DdnsDomain :" << name <<
+                     " specifies and undefined key:" << key_name);
+        }
+    }
 
     // Instantiate the new domain and add it to domain storage.
     DdnsDomainPtr domain(new DdnsDomain(name, key_name, local_servers_));
@@ -369,8 +508,9 @@ DdnsDomainParser::commit() {
 // *********************** DdnsDomainListParser  *************************
 
 DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
-                                       DdnsDomainStoragePtr domains)
-    :list_name_(list_name), domains_(domains), parsers_() {
+                                           DdnsDomainMapPtr domains,
+                                           TSIGKeyInfoMapPtr keys)
+    :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
     if (!domains_) {
         isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
                   " domain storage cannot be null");
@@ -380,28 +520,29 @@ DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
 DdnsDomainListParser::~DdnsDomainListParser(){
 }
 
-void 
+void
 DdnsDomainListParser::
 build(isc::data::ConstElementPtr domain_list){
-    // For each domain element in the 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 
+    // 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_));
+        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
+                                                         domains_, keys_));
         parser->build(domain_config);
         parsers_.push_back(parser);
     }
 }
 
-void 
+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. 
+    // create it's server instance and commit it to storage.
     BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
         parser->commit();
     }
@@ -411,20 +552,20 @@ DdnsDomainListParser::commit() {
 // *********************** DdnsDomainListMgrParser  *************************
 
 DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
-                                                 DdnsDomainListMgrPtr& mgr) 
-    : entry_name_(entry_name), mgr_(mgr), 
-    local_domains_(new DdnsDomainStorage()), local_scalars_() {
+                              DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
+    : entry_name_(entry_name), mgr_(mgr), keys_(keys),
+    local_domains_(new DdnsDomainMap()), local_scalars_() {
 }
 
 
 DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
 }
 
-void 
+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 
+    // 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.
@@ -436,14 +577,14 @@ DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
     }
 }
 
-isc::dhcp::ParserPtr 
+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_);
+       parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
     } else {
        isc_throw(NotImplemented, "parser error: "
                  "DdnsDomainListMgr parameter not supported: " << config_id);
@@ -453,13 +594,12 @@ DdnsDomainListMgrParser::createConfigParser(const std::string& config_id) {
     return (isc::dhcp::ParserPtr(parser));
 }
 
-void 
+void
 DdnsDomainListMgrParser::commit() {
     // Add the new domain to the domain storage.
     mgr_->setDomains(local_domains_);
 }
 
 
-
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 307 - 37
src/bin/d2/d2_config.h

@@ -35,16 +35,25 @@ namespace 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.
+/// The application configuration consists of a set of scalar parameters,
+/// a list of TSIG keys, and two managed lists of domains: one list for
+/// forward domains and one list for reverse domains.
 ///
-/// Each managed list consists of a list one or more domains and is represented
-/// by the class DdnsDomainListMgr.
+/// The key list consists of one or more TSIG keys, each entry described by
+/// a name, the algorithm method name, and its secret key component.
+///
+/// @TODO  NOTE that TSIG configuration parsing is functional, the use of
+/// TSIG Keys during the actual DNS update transactions is not.  This will be
+/// implemented in a future release.
+///
+/// Each managed domain list consists of a list one or more domains and is
+/// represented by the class DdnsDomainListMgr.
 ///
 /// Each domain consists of a set of scalars parameters and a list of DNS
-/// servers which support that domain.  Domains are represented by the class,
-/// DdnsDomain.
+/// servers which support that domain. Among its scalars, is key_name, which
+/// is the name of the TSIG Key to use for with this domain.  This value should
+/// map to one of the TSIG Keys in the key list.  Domains are represented by
+/// the class, DdnsDomain.
 ///
 /// Each server consists of a set of scalars used to describe the server such
 /// that the application can carry out DNS update exchanges with it. Servers
@@ -70,6 +79,61 @@ namespace d2 {
 /// each server entry in its list.
 ///
 /// A DdnsServerInfoParser handles the scalars which belong to the server.
+/// The following is sample configuration in JSON form with extra spacing
+/// for clarity:
+///
+/// @code
+/// {
+///  "interface" : "eth1" ,
+///  "ip_address" : "192.168.1.33" ,
+///  "port" : 88 ,
+///  "tsig_keys":
+//// [
+///    {
+///     "name": "d2_key.tmark.org" ,
+///     "algorithm": "md5" ,
+///     "secret": "0123456989"
+///    }
+///  ],
+///  "forward_ddns" :
+///  {
+///    "ddns_domains":
+///    [
+///      {
+///        "name": "tmark.org." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "hostname": "fserver.tmark.org" },
+///          { "hostname": "f2server.tmark.org" }
+///        ]
+///      },
+///      {
+///        "name": "pub.tmark.org." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "hostname": "f3server.tmark.org" }
+///        ]
+///      }
+///    ]
+///  },
+///  "reverse_ddns" :
+///  {
+///    "ddns_domains":
+///    [
+///      {
+///        "name": " 0.168.192.in.addr.arpa." ,
+///        "key_name": "d2_key.tmark.org" ,
+///        "dns_servers" :
+///        [
+///          { "ip_address": "127.0.0.101" , "port": 100 }
+///        ]
+///      }
+///    ]
+///  }
+/// }
+/// @endcode
 
 /// @brief Exception thrown when the error during configuration handling
 /// occurs.
@@ -79,6 +143,77 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+/// @brief Represents a TSIG Key.
+///
+/// Currently, this is simple storage class containing the basic attributes of
+/// a TSIG Key.  It is intended primarily as a reference for working with
+/// actual keys and may eventually be replaced by isc::dns::TSIGKey.  TSIG Key
+/// functionality at this stage is strictly limited to configuration parsing.
+/// @TODO full functionality for using TSIG during DNS updates will be added
+/// in a future release.
+class TSIGKeyInfo {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param name the unique label used to identify this key
+    /// @param algorithm the name of the encryption alogirthm this key uses.
+    /// (@TODO This will be a fixed list of choices)
+    /// @param secret the secret component of this key
+    TSIGKeyInfo(const std::string& name, const std::string& algorithm,
+                const std::string& secret);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfo();
+
+    /// @brief Getter which returns the key's name.
+    ///
+    /// @return returns the name as as std::string.
+    const std::string getName() const {
+        return (name_);
+    }
+
+    /// @brief Getter which returns the key's algorithm.
+    ///
+    /// @return returns the algorithm as as std::string.
+    const std::string getAlgorithm() const {
+        return (algorithm_);
+    }
+
+    /// @brief Getter which returns the key's secret.
+    ///
+    /// @return returns the secret as as std::string.
+    const std::string getSecret() const {
+        return (secret_);
+    }
+
+private:
+    /// @brief The name of the key.
+    ///
+    /// This value is the unique identifeir thay domains use to
+    /// to specify which TSIG key they need.
+    std::string name_;
+
+    /// @brief The algorithm that should be used for this key.
+    std::string algorithm_;
+
+    /// @brief The secret value component of this key.
+    std::string secret_;
+};
+
+/// @brief Defines a pointer for TSIGKeyInfo instances.
+typedef boost::shared_ptr<TSIGKeyInfo> TSIGKeyInfoPtr;
+
+/// @brief Defines a map of TSIGKeyInfos, keyed by the name.
+typedef std::map<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMap;
+
+/// @brief Defines a iterator pairing of name and TSIGKeyInfo
+typedef std::pair<std::string, TSIGKeyInfoPtr> TSIGKeyInfoMapPair;
+
+/// @brief Defines a pointer to map of TSIGkeyInfos
+typedef boost::shared_ptr<TSIGKeyInfoMap> TSIGKeyInfoMapPtr;
+
+
 /// @brief Represents a specific DNS Server.
 /// It provides information about the server's network identity and typically
 /// belongs to a list of servers supporting DNS for a given domain. It will
@@ -88,10 +223,11 @@ class DnsServerInfo {
 public:
 
     /// @brief defines DNS standard port value
-    static const uint32_t standard_dns_port;
+    static const uint32_t STANDARD_DNS_PORT = 53;
 
     /// @brief defines an "empty" string version of an ip address.
-    static const char* empty_ip_str;
+    static const char* EMPTY_IP_STR;
+
 
     /// @brief Constructor
     ///
@@ -106,7 +242,8 @@ public:
     /// @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,
+                  isc::asiolink::IOAddress ip_address,
+                  uint32_t port=STANDARD_DNS_PORT,
                   bool enabled=true);
 
     /// @brief Destructor
@@ -189,7 +326,6 @@ public:
     ///
     /// @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);
@@ -223,7 +359,6 @@ private:
     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.
@@ -233,15 +368,14 @@ private:
 /// @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 map of DdnsDomains, keyed by the domain name.
+typedef std::map<std::string, DdnsDomainPtr> DdnsDomainMap;
 
-/// @brief Defines a domain and domain key pair for iterating.
-typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainPtrPair;
+/// @brief Defines a iterator pairing domain name and DdnsDomain
+typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainMapPair;
 
+/// @brief Defines a pointer to DdnsDomain storage containers.
+typedef boost::shared_ptr<DdnsDomainMap> DdnsDomainMapPtr;
 
 /// @brief Provides storage for and management of a list of DNS domains.
 /// In addition to housing the domain list storage, it provides domain matching
@@ -252,6 +386,9 @@ typedef std::pair<std::string, DdnsDomainPtr> DdnsDomainPtrPair;
 /// As matching capabilities evolve this class is expected to expand.
 class DdnsDomainListMgr {
 public:
+    /// @brief defines the domain name for denoting the wildcard domain.
+    static const char* wildcard_domain_name_;
+
     /// @brief Constructor
     ///
     /// @param name is an arbitrary label assigned to this manager.
@@ -267,7 +404,7 @@ public:
     /// 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.
+    /// 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
@@ -301,21 +438,21 @@ public:
     /// @brief Fetches the domain list.
     ///
     /// @return returns a pointer reference to the list of domains.
-    const DdnsDomainStoragePtr &getDomains() {
+    const DdnsDomainMapPtr &getDomains() {
         return (domains_);
     }
 
     /// @brief Sets the manger's domain list to the given list of domains.
     /// This method will scan the inbound list for the wild card domain and
     /// set the internal wild card domain pointer accordingly.
-    void setDomains(DdnsDomainStoragePtr domains);
+    void setDomains(DdnsDomainMapPtr domains);
 
 private:
     /// @brief An arbitrary label assigned to this manager.
     std::string name_;
 
-    /// @brief Storage for the list of domains.
-    DdnsDomainStoragePtr domains_;
+    /// @brief Map of the domains, keyed by name.
+    DdnsDomainMapPtr domains_;
 
     /// @brief Pointer to the wild card domain.
     DdnsDomainPtr wildcard_domain_;
@@ -325,11 +462,15 @@ private:
 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
+/// add storage to the parser for an arbitrary number and variety of scalar
 /// configuration items (e.g. ints, bools, strings...) without explicitly adding
 /// storage for each individual type needed by the parser.
+///
+/// This class implements a concrete version of the base class by supplying a
+/// "clone" method.
 class DScalarContext : public DCfgContextBase {
 public:
 
@@ -350,7 +491,7 @@ public:
 
 protected:
     /// @brief Copy constructor
-    DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs){
+    DScalarContext(const DScalarContext& rhs) : DCfgContextBase(rhs) {
     }
 
 private:
@@ -358,6 +499,116 @@ private:
     DScalarContext& operator=(const DScalarContext& rhs);
 };
 
+/// @brief Parser for  TSIGKeyInfo
+///
+/// This class parses the configuration element "tsig_key" defined in
+/// src/bin/d2/dhcp-ddns.spec and creates an instance of a TSIGKeyInfo.
+class TSIGKeyInfoParser : 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 "key:0", set during parsing.
+    /// @param servers is a pointer to the storage area to which the parser
+    /// should commit the newly created TSIGKeyInfo instance.
+    TSIGKeyInfoParser(const std::string& entry_name, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoParser();
+
+    /// @brief Performs the actual parsing of the given  "tsig_key" element.
+    ///
+    /// The results of the parsing are retained internally for use during
+    /// commit.
+    ///
+    /// @param key_config is the "tsig_key" configuration to parse
+    virtual void build(isc::data::ConstElementPtr key_config);
+
+    /// @brief Creates a parser for the given "tsig_key" member element id.
+    ///
+    /// The key elements currently supported are(see dhcp-ddns.spec):
+    ///   1. name
+    ///   2. algorithm
+    ///   3. secret
+    ///
+    /// @param config_id is the "item_name" for a specific member element of
+    /// the "tsig_key" 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 "key: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 TSIGKeyInfo instance. This is given to us as a
+    /// constructor argument by an upper level.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @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 TSIGKeyInfos
+///
+/// This class parses a list of "tsig_key" configuration elements.
+/// (see src/bin/d2/dhcp-ddns.spec). The TSIGKeyInfo instances are added
+/// to the given storage upon commit.
+class TSIGKeyInfoListParser : public isc::dhcp::DhcpConfigParser {
+public:
+
+    /// @brief Constructor
+    ///
+    /// @param list_name is an arbitrary label assigned to this parser instance.
+    /// @param keys is a pointer to the storage area to which the parser
+    /// should commit the newly created TSIGKeyInfo instance.
+    TSIGKeyInfoListParser(const std::string& list_name, TSIGKeyInfoMapPtr keys);
+
+    /// @brief Destructor
+    virtual ~TSIGKeyInfoListParser();
+
+    /// @brief Performs the parsing of the given list "tsig_key" elements.
+    ///
+    /// It iterates over each key entry in the list:
+    ///   1. Instantiate a TSIGKeyInfoParser 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 key entries in the list
+    /// prepping them for commit.
+    ///
+    /// @param key_list_config is the list of "tsig_key" elements to parse.
+    virtual void build(isc::data::ConstElementPtr key_list_config);
+
+    /// @brief Iterates over the internal list of TSIGKeyInfoParsers,
+    /// invoking commit on each.  This causes each parser to instantiate a
+    /// TSIGKeyInfo from its internal data values and add that that key
+    /// instance to the storage area, keys_.
+    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 TSIGKeyInfo instances. This is given to us
+    /// as a constructor argument by an upper level.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Local storage of TSIGKeyInfoParser instances
+    isc::dhcp::ParserCollection parsers_;
+};
+
 /// @brief Parser for  DnsServerInfo
 ///
 /// This class parses the configuration element "dns_server" defined in
@@ -389,7 +640,7 @@ public:
     /// The server elements currently supported are(see dhcp-ddns.spec):
     ///   1. hostname
     ///   2. ip_address
-    ///   3. port 
+    ///   3. port
     ///
     /// @param config_id is the "item_name" for a specific member element of
     /// the "dns_server" specification.
@@ -481,9 +732,10 @@ public:
     /// 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
+    /// @param keys is a pointer to a map of the defined TSIG keys.
     /// should commit the newly created DdnsDomain instance.
-    DdnsDomainParser(const std::string& entry_name,
-                     DdnsDomainStoragePtr domains);
+    DdnsDomainParser(const std::string& entry_name, DdnsDomainMapPtr domains,
+                     TSIGKeyInfoMapPtr keys);
 
     /// @brief Destructor
     virtual ~DdnsDomainParser();
@@ -521,7 +773,13 @@ private:
     /// @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_;
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
 
     /// @brief Local storage for DnsServerInfo instances. This is passed into
     /// DnsServerInfoListParser(s), which in turn passes it into each
@@ -546,9 +804,10 @@ public:
     ///
     /// @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
+    /// @param keys is a pointer to a map of the defined TSIG keys.
     /// should commit the newly created DdnsDomain instance.
     DdnsDomainListParser(const std::string& list_name,
-                            DdnsDomainStoragePtr domains_);
+                         DdnsDomainMapPtr domains_, TSIGKeyInfoMapPtr keys);
 
     /// @brief Destructor
     virtual ~DdnsDomainListParser();
@@ -580,7 +839,13 @@ private:
     /// @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_;
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
 
     /// @brief Local storage of DdnsDomainParser instances
     isc::dhcp::ParserCollection parsers_;
@@ -600,9 +865,10 @@ public:
     /// @param entry_name is an arbitrary label assigned to this configuration
     /// definition.
     /// @param mgr is a pointer to the DdnsDomainListMgr to populate.
+    /// @param keys is a pointer to a map of the defined TSIG keys.
     /// @throw throws D2CfgError if mgr pointer is empty.
     DdnsDomainListMgrParser(const std::string& entry_name,
-                     DdnsDomainListMgrPtr& mgr);
+                     DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys);
 
     /// @brief Destructor
     virtual ~DdnsDomainListMgrParser();
@@ -616,9 +882,8 @@ public:
 
     /// @brief Creates a parser for the given manager member element id.
     ///
-    /// 
     /// The manager elements currently supported are (see dhcp-ddns.spec):
-    ///     1. ddns_domains 
+    ///     1. ddns_domains
     ///
     /// @param config_id is the "item_name" for a specific member element of
     /// the manager specification.
@@ -640,11 +905,17 @@ private:
     /// upper level.
     DdnsDomainListMgrPtr mgr_;
 
+    /// @brief Pointer to the map of defined TSIG keys.
+    /// This map is passed into us and contains all of the TSIG keys defined
+    /// for this configuration.  It is used to validate the key name entry of
+    /// DdnsDomains that specify one.
+    TSIGKeyInfoMapPtr keys_;
+
     /// @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_;
+    DdnsDomainMapPtr local_domains_;
 
     /// @brief Local storage area for scalar parameter values. Use to hold
     /// data until time to commit.
@@ -654,7 +925,6 @@ private:
 };
 
 
-
 }; // end of isc::d2 namespace
 }; // end of isc namespace
 

+ 10 - 6
src/bin/d2/d2_messages.mes

@@ -68,10 +68,14 @@ application and will exit.
 A warning message is issued when an attempt is made to shut down the
 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
-element ids not specified the configuration manager's parse order list. This is
-programming logic error.
+% DCTL_ORDER_ERROR configuration contains more elements than the parsing order
+An error message which indicates that configuration being parsed includes
+element ids not specified the configuration manager's parse order list. This 
+is a programmatic error.
+
+% DCTL_ORDER_NO_ELEMENT element: %1 is in the parsing order but is missing from the configuration
+An error message indicating that the listed element is in specified in the
+parsing order but is not in the configuration.  The configuration is incorrect. 
 
 % DCTL_PARSER_FAIL configuration parsing failed for configuration element: %1
 On receipt of message containing details to a change of its configuration,
@@ -121,8 +125,8 @@ 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.
+which match the cited fully qualified domain name (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

+ 32 - 23
src/bin/d2/d_cfg_mgr.cc

@@ -40,8 +40,6 @@ namespace d2 {
 
 // *********************** DCfgContextBase  *************************
 
-const bool DCfgContextBase::optional_ = true;
-
 DCfgContextBase::DCfgContextBase():
         boolean_values_(new BooleanStorage()),
         uint32_values_(new Uint32Storage()),
@@ -143,30 +141,42 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
         // elements are parsed in the order the value_map presents them.
 
         if (parse_order_.size() > 0) {
-            // NOTE: When using ordered parsing, the parse order list MUST
-            // include every possible element id that the value_map may contain.
-            // Entries in the map that are not in the parse order, would not be
-            // parsed. For now we will flag this as a programmatic error.  One
-            // could attempt to adjust for this, by identifying such entries
-            // and parsing them either first or last but which would be correct?
-            // Better to make hold the engineer accountable.
-            if (values_map.size() > parse_order_.size()) {
-                LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
-                return (isc::config::createAnswer(1,
-                        "Configuration contains elements not in parse order"));
-            }
-
             // For each element_id in the parse order list, look for it in the
             // value map.  If the element exists in the map, pass it and it's
-            // associated data in for parsing.  If there is no matching entry
-            // in the value map, then assume the element is optional and move
-            // on to next element_id.
+            // associated data in for parsing.  
+            // If there is no matching entry in the value map an error is 
+            // thrown.  Optional elements may not be used with ordered parsing.
+            int parsed_count = 0;
             std::map<std::string, ConstElementPtr>::const_iterator it;
             BOOST_FOREACH(element_id, parse_order_) {
                 it = values_map.find(element_id);
                 if (it != values_map.end()) {
+                    ++parsed_count;
                     buildAndCommit(element_id, it->second);
                 }
+                else {
+                    LOG_ERROR(dctl_logger, DCTL_ORDER_NO_ELEMENT)
+                              .arg(element_id);
+                    isc_throw(DCfgMgrBaseError, "Element:" << element_id <<  
+                              " is listed in the parse order but is not "
+                              " present in the configuration");
+                }
+            }
+
+            // NOTE: When using ordered parsing, the parse order list MUST
+            // include every possible element id that the value_map may contain.
+            // Entries in the map that are not in the parse order, would not be
+            // parsed. For now we will flag this as a programmatic error.  One
+            // could attempt to adjust for this, by identifying such entries
+            // and parsing them either first or last but which would be correct?
+            // Better to hold the engineer accountable.  So, if we parsed none
+            // or we parsed fewer than are in the map; then either the parse i
+            // order is incomplete OR the map has unsupported values.
+            if (!parsed_count || 
+                (parsed_count && ((parsed_count + 1) < values_map.size()))) {
+                LOG_ERROR(dctl_logger, DCTL_ORDER_ERROR);
+                isc_throw(DCfgMgrBaseError, 
+                        "Configuration contains elements not in parse order");
             }
         } else {
             // Order doesn't matter so iterate over the value map directly.
@@ -185,8 +195,7 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
     } 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() +
-                     " for element: " + element_id);
+                     string("Configuration parsing failed: ") + ex.what());
 
         // An error occurred, so make sure that we restore original context.
         context_ = original_context;
@@ -202,7 +211,7 @@ void DCfgMgrBase::buildAndCommit(std::string& element_id,
     // based on the element id.
     ParserPtr parser = createConfigParser(element_id);
     if (!parser) {
-        isc_throw(DCfgMgrBaseError, std::string("Could not create parser"));
+        isc_throw(DCfgMgrBaseError, "Could not create parser");
     }
 
     try {
@@ -217,8 +226,8 @@ void DCfgMgrBase::buildAndCommit(std::string& element_id,
         // nothing something we are concerned with here.)
         parser->commit();
     } catch (const isc::Exception& ex) {
-        isc_throw(DCfgMgrBaseError, std::string("Could not build and commit")
-                                    + ex.what());
+        isc_throw(DCfgMgrBaseError, 
+                  "Could not build and commit: " << ex.what());
     } catch (...) {
         isc_throw(DCfgMgrBaseError, "Non-ISC exception occurred");
     }

+ 39 - 32
src/bin/d2/d_cfg_mgr.h

@@ -40,10 +40,9 @@ public:
 /// Derivations simply add additional storage as needed.  Note that this class
 /// declares the pure virtual clone() method, its copy constructor is protected,
 /// and its copy operator is inaccessible.  Derivations must supply an
-/// implementation of clone that calls the base class copy constructor as
-/// well as performing whatever steps are necessary to copy its additional
-/// storage.  This allows the management class to perform context backup and
-/// restoration without derivation specific knowledge using logic like
+/// implementation of clone that calls the base class copy constructor.
+/// This allows the management class to perform context backup and restoration
+/// without derivation specific knowledge using logic like
 /// the following:
 ///
 ///    // Make a backup copy
@@ -55,7 +54,8 @@ public:
 class DCfgContextBase {
 public:
     /// @brief Indicator that a configuration parameter is optional.
-    static const bool optional_;
+    static const bool OPTIONAL = true;
+    static const bool REQUIRED = false;
 
     /// @brief Constructor
     DCfgContextBase();
@@ -129,13 +129,14 @@ public:
         return (string_values_);
     }
 
-    /// @brief Creates a clone of this context object.  As mentioned in the
-    /// the class brief, derivation must supply an implementation that
-    /// initializes the base class storage as well as its own.  Typically
-    /// the derivation's clone method would return the result of passing
-    /// "*this" into its own copy constructor:
+    /// @brief Creates a clone of this context object.
     ///
-    /// -----------------------------------------------------------------
+    /// As mentioned in the the class brief, derivation must supply an
+    /// implementation that initializes the base class storage as well as its
+    /// own.  Typically the derivation's clone method would return the result
+    /// of passing  "*this" into its own copy constructor:
+    ///
+    /// @code
     /// class DStubContext : public DCfgContextBase {
     /// public:
     ///  :
@@ -153,7 +154,7 @@ public:
     ///    // Here's the derivation's additional storage.
     ///    isc::dhcp::Uint32StoragePtr extra_values_;
     ///  :
-    /// -----------------------------------------------------------------
+    /// @endcode
     ///
     /// @return returns a raw pointer to the new clone.
     virtual DCfgContextBase* clone() = 0;
@@ -189,17 +190,12 @@ typedef std::vector<std::string> ElementIdList;
 /// configuration values, storing the parsed information in its converted form,
 /// and retrieving the information on demand.  It is intended to be the worker
 /// class which is handed a set of configuration values to process by upper
-/// application management layers. Typically this call chain would look like
-/// this:
-/// External configuration event:
-///  --> Controller::configHandler(new configuration)
-///      --> Controller.updateConfig(new configuration)
-///          --> Controller.Process.configure(new configuration)
-///              --> Process.CfgMgr.parseConfig(new configuration)
+/// application management layers.
 ///
 /// The class presents a public method for receiving new configurations,
 /// parseConfig.  This method coordinates the parsing effort as follows:
 ///
+/// @code
 ///    make backup copy of configuration context
 ///    for each top level element in new configuration
 ///        get derivation-specific parser for element
@@ -209,6 +205,7 @@ typedef std::vector<std::string> ElementIdList;
 ///
 ///    if an error occurred
 ///        restore configuration context from backup
+/// @endcode
 ///
 /// After making a backup of the current context, it iterates over the top-level
 /// elements in the new configuration.  The order in which the elements are
@@ -219,16 +216,19 @@ typedef std::vector<std::string> ElementIdList;
 ///
 /// This allows a derivation to specify the order in which its elements are
 /// parsed if there are dependencies between elements.
+///
 /// To parse a given element, its id is passed into createConfigParser,
 /// which returns an instance of the appropriate parser.  This method is
 /// abstract so the derivation's implementation determines the type of parser
 /// created. This isolates the knowledge of specific element ids and which
 /// application specific parsers to derivation.
+///
 /// Once the parser has been created, it is used to parse the data value
 /// associated with the element id and update the context with the parsed
 /// results.
-/// In the event that an error occurs, parsing is halted and the configuration
-/// context is restored from backup.
+///
+/// In the event that an error occurs, parsing is halted and the
+/// configuration context is restored from backup.
 class DCfgMgrBase {
 public:
     /// @brief Constructor
@@ -254,12 +254,14 @@ public:
                                            config_set);
 
     /// @brief Adds a given element id to the end of the parse order list.
-    /// The order in which elements are retrieved from this is FIFO.  Elements
-    /// should be added in the order in which they are to be parsed.
-    //
+    ///
+    /// The order in which elements are retrieved from this is the order in
+    /// which they are added to the list. Derivations should use this method
+    /// to populate the parse order as part of their constructor.
+    ///
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
-    void addToParseOrder(std::string& element_id){
+    void addToParseOrder(const std::string& element_id){
         parse_order_.push_back(element_id);
     }
 
@@ -278,9 +280,11 @@ public:
     }
 
 protected:
-    /// @brief Given an element_id returns an instance of the appropriate
-    /// parser.  This method is abstract, isolating any direct knowledge of
-    /// element_ids and parsers to within the application-specific derivation.
+    /// @brief  Create a parser instance based on an element id.
+    ///
+    /// Given an element_id returns an instance of the appropriate parser.
+    /// This method is abstract, isolating any direct knowledge of element_ids
+    /// and parsers to within the application-specific derivation.
     ///
     /// @param element_id is the string name of the element as it will appear
     /// in the configuration set.
@@ -292,7 +296,9 @@ protected:
 
 private:
 
-    /// @brief Given an element_id and data value, instantiate the appropriate
+    /// @brief Parse a configuration element.
+    ///
+    /// Given an element_id and data value, instantiate the appropriate
     /// parser,  parse the data value, and commit the results.
     ///
     /// @param element_id is the string name of the element as it will appear
@@ -304,9 +310,10 @@ private:
     void buildAndCommit(std::string& element_id,
                         isc::data::ConstElementPtr value);
 
-    /// @brief An FIFO list of element ids, used to dictate the element
-    /// parsing order.  If the list is empty, the natural order in the
-    /// configuration set it used.
+    /// @brief A list of element ids which specifies the element parsing order.
+    ///
+    /// If the list is empty, the natural order in the configuration set
+    /// it used.
     ElementIdList parse_order_;
 
     /// @brief Pointer to the configuration context instance.

+ 34 - 2
src/bin/d2/dhcp-ddns.spec

@@ -24,9 +24,40 @@
         "item_optional": true,
         "item_default": 51771 
     },
-
     {
-        "item_name": "foward_ddns",
+        "item_name": "tsig_keys",
+        "item_type": "list",
+        "item_optional": true, 
+        "item_default": [],
+        "list_item_spec":
+        {
+            "item_name": "tsig_key",
+            "item_type": "map",
+            "item_optional": false,
+            "item_default": {"algorithm" : "hmac_md5"},
+            "map_item_spec": [ 
+            {
+                "item_name": "name",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "algorithm",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            },
+            {
+                "item_name": "secret",
+                "item_type": "string",
+                "item_optional": false,
+                "item_default": ""
+            }]
+        }
+    },
+    {
+        "item_name": "forward_ddns",
         "item_type": "map",
         "item_optional": false,
          "item_default": {},
@@ -171,3 +202,4 @@
     ]
   }
 }
+

+ 449 - 64
src/bin/d2/tests/d2_cfg_mgr_unittests.cc

@@ -105,6 +105,78 @@ bool checkServer(DnsServerInfoPtr server, const char* hostname,
     return (result);
 }
 
+/// @brief Convenience function which compares the contents of the given
+/// TSIGKeyInfo 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 key is a pointer to the key to check against.
+/// @param name is the value to compare against key's name_.
+/// @param algorithm is the string value to compare against key's algorithm.
+/// @param secret is the value to compare against key's secret.
+///
+/// @return returns true if there is a match across the board, otherwise it
+/// returns false.
+bool checkKey(TSIGKeyInfoPtr key, const char* name,
+                 const char *algorithm, const char* secret)
+{
+    // Return value, assume its a match.
+    bool result = true;
+    if (!key)
+    {
+        EXPECT_TRUE(key);
+        return false;
+    }
+
+    // Check name.
+    if (key->getName() != name) {
+        EXPECT_EQ(key->getName(),name);
+        result = false;
+    }
+
+    // Check algorithm. 
+    if (key->getAlgorithm() != algorithm) {
+        EXPECT_EQ(key->getAlgorithm(), algorithm);
+        result = false;
+    }
+
+    // Check secret.
+    if (key->getSecret() !=  secret) {
+        EXPECT_EQ (key->getSecret(), secret);
+        result = false;
+    }
+
+    return (result);
+}
+
+/// @brief Test fixture class for testing DnsServerInfo parsing.
+class TSIGKeyInfoTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    TSIGKeyInfoTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~TSIGKeyInfoTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        keys_.reset(new TSIGKeyInfoMap());
+        parser_.reset(new TSIGKeyInfoParser("test", keys_));
+    }
+
+    /// @brief Storage for "committing" keys.
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
 /// @brief Test fixture class for testing DnsServerInfo parsing.
 class DnsServerInfoTest : public ConfigParseTest {
 public:
@@ -132,12 +204,254 @@ public:
     isc::dhcp::ParserPtr parser_;
 };
 
+
+/// @brief Test fixture class for testing DDnsDomain parsing.
+class DdnsDomainTest : public ConfigParseTest {
+public:
+
+    /// @brief Constructor
+    DdnsDomainTest() {
+        reset();
+    }
+
+    /// @brief Destructor
+    ~DdnsDomainTest() {
+    }
+
+    /// @brief Wipe out the current storage and parser and replace
+    /// them with new ones.
+    void reset() {
+        keys_.reset(new TSIGKeyInfoMap());
+        domains_.reset(new DdnsDomainMap());
+        parser_.reset(new DdnsDomainParser("test", domains_, keys_));
+    }
+
+    /// @brief Add TSIGKeyInfos to the key map
+    ///
+    /// @param name the name of the key
+    /// @param algorithm the algorithm of the key
+    /// @param secret the secret value of the key
+    void addKey(const std::string& name, const std::string& algorithm,
+                const std::string& secret) {
+        TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
+        (*keys_)[name]=key_info; 
+    }
+
+    /// @brief Storage for "committing" domains.
+    DdnsDomainMapPtr domains_;
+
+    /// @brief Storage for TSIGKeys
+    TSIGKeyInfoMapPtr keys_;
+
+    /// @brief Pointer to the current parser instance.
+    isc::dhcp::ParserPtr parser_;
+};
+
+/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
+/// It verifies that:
+/// 1. Name cannot be blank.
+/// 2. Algorithm cannot be blank.
+/// 3. Secret cannot be blank. 
+/// @TODO TSIG keys are not fully functional. Only basic validation is 
+/// currently supported. This test will need to expand as they evolve.
+TEST_F(TSIGKeyInfoTest, invalidEntryTests) {
+    // Config with a blank name entry.
+    std::string config = "{"
+                         " \"name\": \"\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank name.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Config with a blank algorithm entry.
+    config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank algorithm.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+
+    // Config with a blank secret entry.
+    config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"\" "
+                         "}";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that build succeeds but commit fails on blank secret.
+    EXPECT_NO_THROW(parser_->build(config_set_));
+    EXPECT_THROW(parser_->commit(), D2CfgError);
+}
+
+/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
+/// when given a valid combination of entries.
+TEST_F(TSIGKeyInfoTest, validEntryTests) {
+    // Valid entries for TSIG key, all items are required.
+    std::string config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"md5\" , "
+                         " \"secret\": \"0123456789\" "
+                         "}";
+    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 keys are present
+    int count =  keys_->size();
+    EXPECT_EQ(count, 1);
+
+    // Find the key and retrieve it.
+    TSIGKeyInfoMap::iterator gotit = keys_->find("d2_key_one");
+    ASSERT_TRUE(gotit != keys_->end());
+    TSIGKeyInfoPtr& key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "d2_key_one", "md5", "0123456789"));
+}
+
+/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoTest, invalidTSIGKeyList) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret11\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"\" ,"
+                         "   \"secret\": \"secret12\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret13\" "
+                         " }"
+                         " ]";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+
+    // 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 attempting to parse an invalid list of TSIGKeyInfo
+/// entries is detected.
+TEST_F(TSIGKeyInfoTest, duplicateTSIGKey) {
+    // Construct a list of keys with an invalid key entry.
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret11\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"algo2\" ,"
+                         "   \"secret\": \"secret12\" "
+                         " },"
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret13\" "
+                         " }"
+                         " ]";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+
+    // 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 a valid list of TSIG Keys parses correctly.
+TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
+    // Construct a valid list of keys. 
+    std::string config = "["
+
+                         " { \"name\": \"key1\" , "
+                         "   \"algorithm\": \"algo1\" ,"
+                         "   \"secret\": \"secret1\" "
+                         " },"
+                         " { \"name\": \"key2\" , "
+                         "   \"algorithm\": \"algo2\" ,"
+                         "   \"secret\": \"secret2\" "
+                         " },"
+                         " { \"name\": \"key3\" , "
+                         "   \"algorithm\": \"algo3\" ,"
+                         "   \"secret\": \"secret3\" "
+                         " }"
+                         " ]";
+
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that the list builds and commits without errors.
+    // Create the list parser.
+    isc::dhcp::ParserPtr parser;
+    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
+    ASSERT_NO_THROW(parser->build(config_set_));
+    ASSERT_NO_THROW(parser->commit());
+
+    // Verify the correct number of keys are present
+    int count =  keys_->size();
+    ASSERT_EQ(count, 3);
+
+    // Find the 1st key and retrieve it.
+    TSIGKeyInfoMap::iterator gotit = keys_->find("key1");
+    ASSERT_TRUE(gotit != keys_->end());
+    TSIGKeyInfoPtr& key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key1", "algo1", "secret1"));
+
+    // Find the 2nd key and retrieve it.
+    gotit = keys_->find("key2");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key2", "algo2", "secret2"));
+
+    // Find the 3rd key and retrieve it.
+    gotit = keys_->find("key3");
+    ASSERT_TRUE(gotit != keys_->end());
+    key = gotit->second;
+
+    // Verify the key contents.
+    EXPECT_TRUE(checkKey(key, "key3", "algo3", "secret3"));
+}
+
 /// @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) {
+TEST_F(DnsServerInfoTest, invalidEntryTests) {
     // 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\", "
@@ -162,13 +476,14 @@ TEST_F(DnsServerInfoTest, invalidEntyTests) {
     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) {
+TEST_F(DnsServerInfoTest, validEntryTests) {
     // Valid entries for dynamic host
     std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
     ASSERT_NO_THROW(fromJSON(config));
@@ -184,8 +499,8 @@ TEST_F(DnsServerInfoTest, validEntyTests) {
     // 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));
+                            DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
 
     // Start over for a new test.
     reset();
@@ -225,7 +540,7 @@ TEST_F(DnsServerInfoTest, validEntyTests) {
     // Verify the server exists and has the correct values.
     server = (*servers_)[0];
     EXPECT_TRUE(checkServer(server, "", "192.168.2.5",
-                            DnsServerInfo::standard_dns_port));
+                            DnsServerInfo::STANDARD_DNS_PORT));
 }
 
 /// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
@@ -273,18 +588,18 @@ TEST_F(ConfigParseTest, validServerList) {
 
     // 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));
+    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));
+    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));
+    EXPECT_TRUE(checkServer(server, "three.tmark", DnsServerInfo::EMPTY_IP_STR,
+                            DnsServerInfo::STANDARD_DNS_PORT));
 }
 
 /// @brief Tests the enforcement of data validation when parsing DdnsDomains.
@@ -293,16 +608,12 @@ TEST_F(ConfigParseTest, validServerList) {
 /// 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) {
+/// 5. That an undefined key name is detected.
+TEST_F(DdnsDomainTest, 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));
+    DdnsDomainMapPtr domains;
+    ASSERT_THROW(isc::dhcp::ParserPtr(
+                 new DdnsDomainParser("test", domains, keys_)), D2CfgError);
 
     // Create a domain configuration without a name
     std::string config = "{  \"key_name\": \"d2_key.tmark.org\" , "
@@ -316,8 +627,8 @@ TEST_F(ConfigParseTest, invalidDdnsDomainEntry) {
     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);
+    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\" , "
@@ -327,7 +638,7 @@ TEST_F(ConfigParseTest, invalidDdnsDomainEntry) {
     ASSERT_NO_THROW(fromJSON(config));
 
     // Verify that the domain configuration build fails.
-    ASSERT_THROW(parser->build(config_set_), D2CfgError);
+    ASSERT_THROW(parser_->build(config_set_), D2CfgError);
 
     // Create a domain configuration with a mal-formed server entry.
     config = "{ \"name\": \"tmark.org\" , "
@@ -338,17 +649,28 @@ TEST_F(ConfigParseTest, invalidDdnsDomainEntry) {
     ASSERT_NO_THROW(fromJSON(config));
 
     // Verify that the domain configuration build fails.
-    ASSERT_THROW(parser->build(config_set_), isc::BadValue);
-}
+    ASSERT_THROW(parser_->build(config_set_), isc::BadValue);
 
+    // Create a domain configuration without an defined key name
+    config = "{ \"name\": \"tmark.org\" , "
+             "  \"key_name\": \"d2_key.tmark.org\" , "
+             "  \"dns_servers\" : [ "
+             "  {  \"ip_address\": \"127.0.0.3\" , "
+             "    \"port\": 300 } ] } ";
+    ASSERT_NO_THROW(fromJSON(config));
+
+    // Verify that the domain configuration build succeeds but commit fails.
+    ASSERT_NO_THROW(parser_->build(config_set_));
+    ASSERT_THROW(parser_->commit(), D2CfgError);
+}
 
 /// @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) {
+/// (It indirectly verifies the operation of DdnsDomainMap).
+TEST_F(DdnsDomainTest, ddnsDomainParsing) {
     // Create a valid domain configuration entry containing three valid
     // servers.
     std::string config =
@@ -363,24 +685,21 @@ TEST_F(ConfigParseTest, ddnsDomainParsing) {
                         "    \"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));
+    // Add a TSIG key to the test key map, so key validation will pass.
+    addKey("d2_key.tmark.org", "md5", "0123456789");
 
     // Verify that the domain configuration builds and commits without error.
-    ASSERT_NO_THROW(parser->build(config_set_));
-    ASSERT_NO_THROW(parser->commit());
+    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();
+    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());
+    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
+    ASSERT_TRUE(gotit != domains_->end());
     DdnsDomainPtr& domain = gotit->second;
 
     // Verify the name and key_name values.
@@ -414,7 +733,7 @@ TEST_F(ConfigParseTest, ddnsDomainParsing) {
 /// @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) {
+TEST_F(DdnsDomainTest, DdnsDomainListParsing) {
     // Create a valid domain list configuration, with two domains
     // that have three servers each.
     std::string config =
@@ -442,23 +761,26 @@ TEST_F(ConfigParseTest, DdnsDomainListParsing) {
 
     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));
+    // Add keys to key map so key validation passes.
+    addKey("d2_key.tmark.org", "algo1", "secret1");
+    addKey("d2_key.billcat.net", "algo2", "secret2");
+
+    // Create the list parser
+    isc::dhcp::ParserPtr list_parser;    
+    ASSERT_NO_THROW(list_parser.reset(
+                    new DdnsDomainListParser("test", domains_, keys_)));
 
     // Verify that the domain configuration builds and commits without error.
-    ASSERT_NO_THROW(parser->build(config_set_));
-    ASSERT_NO_THROW(parser->commit());
+    ASSERT_NO_THROW(list_parser->build(config_set_));
+    ASSERT_NO_THROW(list_parser->commit());
 
     // Verify that the domain storage contains the correct number of domains.
-    int count =  domains->size();
+    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());
+    DdnsDomainMap::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.
@@ -484,8 +806,8 @@ TEST_F(ConfigParseTest, DdnsDomainListParsing) {
     EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
 
     // Verify second domain
-    gotit = domains->find("billcat.net");
-    ASSERT_TRUE(gotit != domains->end());
+    gotit = domains_->find("billcat.net");
+    ASSERT_TRUE(gotit != domains_->end());
     domain = gotit->second;
 
     // Verify the name and key_name values of the second domain.
@@ -512,7 +834,7 @@ TEST_F(ConfigParseTest, DdnsDomainListParsing) {
 }
 
 /// @brief Tests that a domain list configuration cannot contain duplicates.
-TEST_F(ConfigParseTest, duplicateDomainTest) {
+TEST_F(DdnsDomainTest, duplicateDomainTest) {
     // Create a domain list configuration that contains two domains with
     // the same name.
     std::string config =
@@ -527,17 +849,16 @@ TEST_F(ConfigParseTest, duplicateDomainTest) {
                         "  { \"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));
+    // Create the list parser
+    isc::dhcp::ParserPtr list_parser;    
+    ASSERT_NO_THROW(list_parser.reset(
+                    new DdnsDomainListParser("test", domains_, keys_)));
 
     // Verify that the parse build succeeds but the commit fails.
-    ASSERT_NO_THROW(parser->build(config_set_));
-    ASSERT_THROW(parser->commit(), D2CfgError);
+    ASSERT_NO_THROW(list_parser->build(config_set_));
+    ASSERT_THROW(list_parser->commit(), D2CfgError);
 }
 
 /// @brief Tests construction of D2CfgMgr
@@ -575,6 +896,18 @@ TEST_F(D2CfgMgrTest, fullConfigTest) {
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": ["
+                        "{"
+                        "  \"name\": \"d2_key.tmark.org\" , "
+                        "  \"algorithm\": \"md5\" , "
+                        "  \"secret\": \"ssh-dont-tell\"  "
+                        "},"
+                        "{"
+                        "  \"name\": \"d2_key.billcat.net\" , "
+                        "  \"algorithm\": \"md5\" , "
+                        "  \"secret\": \"ollie-ollie-in-free\"  "
+                        "}"
+                        "],"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"tmark.org\" , "
@@ -615,7 +948,7 @@ TEST_F(D2CfgMgrTest, fullConfigTest) {
 
     // Verify that we can parse the configuration.
     answer_ = cfg_mgr_->parseConfig(config_set_);
-    EXPECT_TRUE(checkAnswer(0));
+    ASSERT_TRUE(checkAnswer(0));
 
     // Verify that the D2 context can be retrieved and is not null.
     D2CfgContextPtr context;
@@ -639,7 +972,7 @@ TEST_F(D2CfgMgrTest, fullConfigTest) {
     ASSERT_TRUE(mgr);
 
     // Verify that the forward manager has the correct number of domains.
-    DdnsDomainStoragePtr domains = mgr->getDomains();
+    DdnsDomainMapPtr domains = mgr->getDomains();
     ASSERT_TRUE(domains);
     int count =  domains->size();
     EXPECT_EQ(count, 2);
@@ -648,7 +981,7 @@ TEST_F(D2CfgMgrTest, fullConfigTest) {
     // 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;
+    DdnsDomainMapPair domain_pair;
     BOOST_FOREACH(domain_pair, (*domains)) {
         DdnsDomainPtr domain = domain_pair.second;
         DnsServerInfoStoragePtr servers = domain->getServers();
@@ -695,6 +1028,7 @@ TEST_F(D2CfgMgrTest, forwardMatchTest) {
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"tmark.org\" , "
@@ -711,7 +1045,10 @@ TEST_F(D2CfgMgrTest, forwardMatchTest) {
                         "  \"dns_servers\" : [ "
                         "  { \"hostname\": \"global.net\" } "
                         "  ] } "
-                        "] } }";
+                        "] }, " 
+                        "\"reverse_ddns\" : {} "
+                        "}";
+
 
     ASSERT_NO_THROW(fromJSON(config));
     // Verify that we can parse the configuration.
@@ -757,6 +1094,7 @@ TEST_F(D2CfgMgrTest, matchNoWildcard) {
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"tmark.org\" , "
@@ -768,7 +1106,9 @@ TEST_F(D2CfgMgrTest, matchNoWildcard) {
                         "  \"dns_servers\" : [ "
                         "  { \"ip_address\": \"127.0.0.2\" } "
                         "  ] } "
-                        "] } }";
+                        "] }, "
+                        "\"reverse_ddns\" : {} "
+                        " }";
 
     ASSERT_NO_THROW(fromJSON(config));
 
@@ -802,13 +1142,16 @@ TEST_F(D2CfgMgrTest, matchAll) {
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"*\" , "
                         "  \"dns_servers\" : [ "
                         "  { \"ip_address\": \"127.0.0.1\" } "
                         "  ] } "
-                        "] } }";
+                        "] }, "
+                        "\"reverse_ddns\" : {} "
+                        "}";
 
     ASSERT_NO_THROW(fromJSON(config));
 
@@ -846,6 +1189,8 @@ TEST_F(D2CfgMgrTest, matchReverse) {
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"forward_ddns\" : {}, "
                         "\"reverse_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"100.168.192.in-addr.arpa\" , "
@@ -893,5 +1238,45 @@ TEST_F(D2CfgMgrTest, matchReverse) {
     ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError);
 }
 
+TEST_F(D2CfgMgrTest, tsigTest) {
+    std::string config = "{ "
+                        "\"interface\" : \"eth1\" , "
+                        "\"ip_address\" : \"192.168.1.33\" , "
+                        "\"port\" : 88 , "
+                        "\"tsig_keys\": [] ,"
+                        "\"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\" } "
+                        "  ] } "
+                        "] },"
+                        "\"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));
+}
 
 } // end of anonymous namespace

+ 15 - 3
src/bin/d2/tests/d_cfg_mgr_unittests.cc

@@ -88,17 +88,17 @@ public:
 /// 3. Destruction works properly.
 /// 4. Construction with a null context is not allowed.
 TEST(DCfgMgrBase, construction) {
-    DCfgMgrBase *cfg_mgr = NULL;
+    DCfgMgrBasePtr cfg_mgr;
 
     // Verify that configuration manager constructions without error.
-    ASSERT_NO_THROW(cfg_mgr=new DStubCfgMgr());
+    ASSERT_NO_THROW(cfg_mgr.reset(new DStubCfgMgr()));
 
     // Verify that the context can be retrieved and is not null.
     DCfgContextBasePtr context = cfg_mgr->getContext();
     EXPECT_TRUE(context);
 
     // Verify that the manager can be destructed without error.
-    EXPECT_NO_THROW(delete cfg_mgr);
+    EXPECT_NO_THROW(cfg_mgr.reset());
 
     // Verify that an attempt to construct a manger with a null context fails.
     ASSERT_THROW(DCtorTestCfgMgr(), DCfgMgrBaseError);
@@ -145,6 +145,7 @@ TEST_F(DStubCfgMgrTest, basicParseTest) {
 /// 2. A parse order list with too few elements is detected.
 /// 3. Ordered parsing parses the elements in the order specified by the
 /// configuration manager's parse order list.
+/// 4. A parse order list with too many elements is detected.
 TEST_F(DStubCfgMgrTest, parseOrderTest) {
     // Element ids used for test.
     std::string charlie("charlie");
@@ -213,6 +214,17 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
 
     // Verify that the parsed order is the order we configured.
     EXPECT_TRUE(cfg_mgr_->getParseOrder() == cfg_mgr_->parsed_order_);
+
+    // Create a parse order list that has too many entries.  Verify that
+    // when parsing the test config, it fails.
+    cfg_mgr_->addToParseOrder("delta");
+
+    // Verify the parse order list is the size we expect.
+    EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
+
+    // Verify the configuration fails.
+    answer_ = cfg_mgr_->parseConfig(config_set_);
+    EXPECT_TRUE(checkAnswer(1));
 }
 
 /// @brief Tests that element ids supported by the base class as well as those

+ 7 - 2
src/bin/d2/tests/d_test_stubs.cc

@@ -25,13 +25,18 @@ const char* valid_d2_config = "{ "
                         "\"interface\" : \"eth1\" , "
                         "\"ip_address\" : \"192.168.1.33\" , "
                         "\"port\" : 88 , "
+                        "\"tsig_keys\": ["
+                        "{ \"name\": \"d2_key.tmark.org\" , "
+                        "   \"algorithm\": \"md5\" ,"
+                        "   \"secret\": \"0123456989\" "
+                        "} ],"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \"tmark.org\" , "
                         "  \"key_name\": \"d2_key.tmark.org\" , "
                         "  \"dns_servers\" : [ "
                         "  { \"hostname\": \"one.tmark\" } "
-                        "     ] } ] }, "
+                        "] } ] }, "
                         "\"reverse_ddns\" : {"
                         "\"ddns_domains\": [ "
                         "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
@@ -296,7 +301,7 @@ DStubCfgMgr::createConfigParser(const std::string& element_id) {
         // to test parse ordering, by permitting a wide range of element ids
         // to "succeed" without specifically supporting them.
         if (SimFailure::shouldFailOn(SimFailure::ftElementUnknown)) {
-            isc_throw(DCfgMgrBaseError, "Configuration parameter not supported"
+            isc_throw(DCfgMgrBaseError, "Configuration parameter not supported: "
                       << element_id);
         }
 

+ 6 - 4
src/bin/d2/tests/d_test_stubs.h

@@ -628,10 +628,12 @@ public:
         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;
-        //}
+        #if 1
+        if (rcode != 0) {
+            std::cout << "checkAnswer rcode:" << rcode << " comment: "
+                  << *comment << std::endl;
+        }
+        #endif
         return (rcode == should_be);
     }