]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[540-recommend-moving-hostname-char-set-and-hostname-char-replacement-out-of-dhcp...
authorFrancis Dupont <fdupont@isc.org>
Tue, 11 Jun 2019 00:10:18 +0000 (02:10 +0200)
committerFrancis Dupont <fdupont@isc.org>
Thu, 13 Jun 2019 13:21:07 +0000 (15:21 +0200)
16 files changed:
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp4/tests/dhcp4_test_utils.cc
src/bin/dhcp4/tests/fqdn_unittest.cc
src/bin/dhcp4/tests/get_config_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/bin/dhcp6/tests/get_config_unittest.cc
src/lib/dhcpsrv/d2_client_cfg.cc
src/lib/dhcpsrv/d2_client_cfg.h
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/tests/d2_client_unittest.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

index 9081571139957f8d01dc3f47ff1c1bf86d247034..0bda963150424cfd4e5395a861098e26da98f0f9 100644 (file)
@@ -373,6 +373,12 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
     if (rcode == 0) {
         CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
 
+       // Update the fetch globals callback.
+       auto cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+       cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+           return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+       });
+
         // Use new configuration.
         CfgMgr::instance().commit();
     } else {
index 227e2a2274046c53e495ff40dc26456d8fce4523..885c145cc8e9e33b00f63bd93a68ee2f6a3c9958 100644 (file)
@@ -368,6 +368,9 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // early.
         Dhcp4ConfigParser global_parser;
 
+        // D2 client configuration.
+        D2ClientConfigPtr d2_client_cfg;
+
         // Make parsers grouping.
         const std::map<std::string, ConstElementPtr>& values_map =
                                                         mutable_cfg->mapValue();
@@ -445,8 +448,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
                 // Apply defaults
                 D2ClientConfigParser::setAllDefaults(config_pair.second);
                 D2ClientConfigParser parser;
-                D2ClientConfigPtr cfg = parser.parse(config_pair.second);
-                srv_cfg->setD2ClientConfig(cfg);
+                d2_client_cfg = parser.parse(config_pair.second);
                 continue;
             }
 
@@ -557,7 +559,9 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
                  (config_pair.first == "calculate-tee-times") ||
                  (config_pair.first == "t1-percent") ||
                  (config_pair.first == "t2-percent") ||
-                 (config_pair.first == "loggers")) {
+                 (config_pair.first == "loggers") ||
+                 (config_pair.first == "hostname-char-set") ||
+                 (config_pair.first == "hostname-char-replacement")) {
 
                 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
                                                                         config_pair.second);
@@ -584,6 +588,16 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // defined as part of shared networks.
         global_parser.sanityChecks(srv_cfg, mutable_cfg);
 
+        // Validate D2 client confuguration.
+        if (!d2_client_cfg) {
+            d2_client_cfg.reset(new D2ClientConfig());
+            d2_client_cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+                return (CfgMgr::instance().getStagingCfg()->getConfiguredGlobals());
+             });
+        }
+        d2_client_cfg->validateContents();
+        srv_cfg->setD2ClientConfig(d2_client_cfg);
+
     } catch (const isc::Exception& ex) {
         LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
                   .arg(config_pair.first).arg(ex.what());
index d492e43fb061d411dd8f02b820d37d92d6ba6b7b..2ee1bca77482e2cc8bfb7b97a9ec808e40269bce 100644 (file)
@@ -4170,6 +4170,165 @@ TEST_F(Dhcp4ParserTest, d2ClientConfig) {
     EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
     EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
     EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+}
+
+// This test checks the ability of the server to parse a configuration
+// containing a full, valid dhcp-ddns (D2ClientConfig) entry with
+// hostname-char-* at the global scope.
+TEST_F(Dhcp4ParserTest, d2ClientConfigGlobal) {
+    ConstElementPtr status;
+
+    // Verify that the D2 configuration can be fetched and is set to disabled.
+    D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+
+    // Verify that the convenience method agrees.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    string config_str = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        " \"dhcp-ddns\" : {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"192.168.2.1\", "
+        "     \"server-port\" : 777, "
+        "     \"sender-ip\" : \"192.168.2.2\", "
+        "     \"sender-port\" : 778, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\" },"
+        "\"hostname-char-set\" : \"[^A-Za-z0-9_-]\", "
+        "\"hostname-char-replacement\" : \"x\", "
+        "\"valid-lifetime\": 4000 }";
+
+    // Convert the JSON string to configuration elements.
+    ConstElementPtr config;
+    ASSERT_NO_THROW(config = parseDHCP4(config_str, true));
+    extractConfig(config_str);
+
+    // Pass the configuration in for parsing.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Verify that DHCP-DDNS updating is enabled.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+    // Verify that the D2 configuration can be retrieved.
+    d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are correct.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("192.168.2.1", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(777, d2_client_config->getServerPort());
+    EXPECT_EQ("192.168.2.2", d2_client_config->getSenderIp().toText());
+    EXPECT_EQ(778, d2_client_config->getSenderPort());
+    EXPECT_EQ(2048, d2_client_config->getMaxQueueSize());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+}
+
+// This test checks the ability of the server to parse a configuration
+// containing a full, valid dhcp-ddns (D2ClientConfig) entry with
+// hostname-char-* at the local and global scopes (local has the priority).
+TEST_F(Dhcp4ParserTest, d2ClientConfigBoth) {
+    ConstElementPtr status;
+
+    // Verify that the D2 configuration can be fetched and is set to disabled.
+    D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+
+    // Verify that the convenience method agrees.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    string config_str = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        " \"dhcp-ddns\" : {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"192.168.2.1\", "
+        "     \"server-port\" : 777, "
+        "     \"sender-ip\" : \"192.168.2.2\", "
+        "     \"sender-port\" : 778, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\", "
+        "     \"hostname-char-set\" : \"[^A-Za-z0-9_-]\", "
+        "     \"hostname-char-replacement\" : \"x\" }, "
+        "\"hostname-char-set\" : \"[^A-Z]\", "
+        "\"hostname-char-replacement\" : \"z\", "
+        "\"valid-lifetime\": 4000 }";
+
+    // Convert the JSON string to configuration elements.
+    ConstElementPtr config;
+    ASSERT_NO_THROW(config = parseDHCP4(config_str, true));
+    extractConfig(config_str);
+
+    // Pass the configuration in for parsing.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Verify that DHCP-DDNS updating is enabled.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+    // Verify that the D2 configuration can be retrieved.
+    d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are correct.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("192.168.2.1", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(777, d2_client_config->getServerPort());
+    EXPECT_EQ("192.168.2.2", d2_client_config->getSenderIp().toText());
+    EXPECT_EQ(778, d2_client_config->getSenderPort());
+    EXPECT_EQ(2048, d2_client_config->getMaxQueueSize());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 }
 
 // This test checks the ability of the server to handle a configuration
index e9147dfc2b4833da0c24a82e9d7b0c7948d88f53..c223984eab8ffd40d5d3b24ee9a111f00ae67169 100644 (file)
@@ -635,6 +635,10 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
     } );
 
     if (commit) {
+        auto cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+        cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+            return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+        });
         CfgMgr::instance().commit();
     }
  }
@@ -680,6 +684,10 @@ Dhcpv4SrvTest::configureWithStatus(const std::string& config, NakedDhcpv4Srv& sr
             cfg_db->createManagers();
             } );
         if (commit) {
+            auto cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+            cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+                return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+            });
             CfgMgr::instance().commit();
         }
     }
index 6f3ae43530cedd555250d11942e71b59f6f28846..b208430a34b0d62a870745335f5b4da256f4c471 100644 (file)
@@ -197,6 +197,31 @@ const char* CONFIGS[] = {
             "\"qualifying-suffix\": \"example.org\""
         "}"
     "}",
+    // 7
+    // Configuration which enables DNS updates and hostname sanitization.
+    // the second at the global scope.
+    "{ \"interfaces-config\": {"
+        "      \"interfaces\": [ \"*\" ]"
+        "},"
+        "\"valid-lifetime\": 3000,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"id\": 1,"
+        "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
+        "    \"option-data\": [ {"
+        "        \"name\": \"routers\","
+        "        \"data\": \"10.0.0.200,10.0.0.201\""
+        "    } ],"
+        "    \"reservations\": ["
+        "       {"
+        "         \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
+        "         \"hostname\":   \"unique-xxx-host.example.org\""
+        "       }"
+        "    ]"
+        " }],"
+        "\"hostname-char-set\" : \"[^A-Za-z0-9.-]\","
+        "\"hostname-char-replacement\" : \"x\""
+    "}"
 };
 
 class NameDhcpv4SrvTest : public Dhcpv4SrvTest {
@@ -503,11 +528,11 @@ public:
             "    \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.10\" } ]"
             " }],"
             "\"dhcp-ddns\": {"
-            "\"enable-updates\": true,"
-            "\"qualifying-suffix\": \"fake-suffix.isc.org.\","
-            "\"hostname-char-set\": \"[^A-Za-z0-9.-]\","
-            "\"hostname-char-replacement\": \"x\","
-            "\"replace-client-name\": \"%s\""
+            "  \"enable-updates\": true,"
+            "  \"qualifying-suffix\": \"fake-suffix.isc.org.\","
+            "  \"hostname-char-set\": \"[^A-Za-z0-9.-]\","
+            "  \"hostname-char-replacement\": \"x\","
+            "  \"replace-client-name\": \"%s\""
             "}}";
 
         // Create the configuration and configure the server
@@ -1798,6 +1823,68 @@ TEST_F(NameDhcpv4SrvTest, sanitizeHost) {
     }
 }
 
+// Verifies that setting global hostname-char-set sanitizes Hostname option
+// values received from clients.
+TEST_F(NameDhcpv4SrvTest, sanitizeHostGlobal) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[7], *client.getServer());
+
+    // Make sure that DDNS is not enabled.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    struct Scenario {
+        std::string description_;
+        std::string original_;
+        std::string sanitized_;
+    };
+
+    std::vector<Scenario> scenarios = {
+        {
+            "unqualified host name with invalid characters",
+            "one-&$_-host",
+            "one-xxx-host"
+        },
+        {
+            "qualified host name with invalid characters",
+            "two-&$_-host.other.org",
+            "two-xxx-host.other.org"
+        },
+        {
+            "unqualified host name with all valid characters",
+            "three-ok-host",
+            "three-ok-host"
+        },
+        {
+            "qualified host name with valid characters",
+            "four-ok-host.other.org",
+            "four-ok-host.other.org"
+        }
+    };
+
+    Pkt4Ptr resp;
+    OptionStringPtr hostname;
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE((scenario).description_);
+        {
+            // Set the hostname option.
+            ASSERT_NO_THROW(client.includeHostname((scenario).original_));
+
+            // Send the DHCPDISCOVER and make sure that the server responded.
+            ASSERT_NO_THROW(client.doDiscover());
+            resp = client.getContext().response_;
+            ASSERT_TRUE(resp);
+            ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
+
+            // Make sure the response hostname is what we expect.
+            hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
+            ASSERT_TRUE(hostname);
+            EXPECT_EQ((scenario).sanitized_, hostname->getValue());
+        }
+    }
+}
+
 // Verifies that setting hostname-char-set sanitizes FQDN option
 // values received from clients.
 TEST_F(NameDhcpv4SrvTest, sanitizeFqdn) {
@@ -1867,5 +1954,72 @@ TEST_F(NameDhcpv4SrvTest, sanitizeFqdn) {
     }
 }
 
+// Verifies that setting global hostname-char-set sanitizes FQDN option
+// values received from clients.
+TEST_F(NameDhcpv4SrvTest, sanitizeFqdnGlobal) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Configure DHCP server.
+    configure(CONFIGS[7], *client.getServer());
+
+    // Make sure that DDNS is not enabled.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    struct Scenario {
+        std::string description_;
+        std::string original_;
+        Option4ClientFqdn::DomainNameType name_type_;
+        std::string sanitized_;
+    };
+
+    std::vector<Scenario> scenarios = {
+        {
+            "unqualified FQDN with invalid characters",
+            "one-&*_-host",
+            Option4ClientFqdn::PARTIAL,
+            "one-xxx-host."
+        },
+        {
+            "qualified FQDN with invalid characters",
+            "two-&*_-host.other.org",
+            Option4ClientFqdn::FULL,
+            "two-xxx-host.other.org."
+        },
+        {
+            "unqualified FQDN name with all valid characters",
+            "three-ok-host",
+            Option4ClientFqdn::PARTIAL,
+            "three-ok-host."
+        },
+        {
+            "qualified FQDN name with valid characters",
+            "four-ok-host.other.org",
+            Option4ClientFqdn::FULL,
+            "four-ok-host.other.org."
+        }
+    };
+
+    Pkt4Ptr resp;
+    Option4ClientFqdnPtr fqdn;
+    for (auto scenario = scenarios.begin(); scenario != scenarios.end(); ++scenario) {
+        SCOPED_TRACE((*scenario).description_);
+        {
+        // Set the hostname option.
+        ASSERT_NO_THROW(client.includeHostname((*scenario).original_));
+        ASSERT_NO_THROW(client.includeFQDN(0, (*scenario).original_, (*scenario).name_type_));
+
+        // Send the DHCPDISCOVER and make sure that the server responded.
+        ASSERT_NO_THROW(client.doDiscover());
+        resp = client.getContext().response_;
+        ASSERT_TRUE(resp);
+        ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
+
+        // Make sure the response fqdn is what we expect.
+        fqdn = boost::dynamic_pointer_cast<Option4ClientFqdn>(resp->getOption(DHO_FQDN));
+        ASSERT_TRUE(fqdn);
+        EXPECT_EQ((*scenario).sanitized_, fqdn->getDomainName());
+        }
+    }
+}
 
 } // end of anonymous namespace
index 62b2945ae27e439224b84174296a101dfb6e1e28..0b62c8d3efe0a9f4f87c6119198b2295ba5bfd5a 100644 (file)
@@ -2064,8 +2064,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2129,8 +2127,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2221,8 +2217,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2313,8 +2307,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2407,8 +2399,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2585,8 +2575,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2763,8 +2751,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2859,8 +2845,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2955,8 +2939,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3051,8 +3033,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3145,8 +3125,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3239,8 +3217,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3361,8 +3337,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3483,8 +3457,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3605,8 +3577,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3727,8 +3697,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3821,8 +3789,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3951,8 +3917,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4045,8 +4009,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4118,8 +4080,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4191,8 +4151,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4273,8 +4231,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4346,8 +4302,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4419,8 +4373,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4492,8 +4444,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4565,8 +4515,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4676,8 +4624,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4787,8 +4733,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4908,8 +4852,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5009,8 +4951,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5156,8 +5096,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5276,8 +5214,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5416,8 +5352,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5527,8 +5461,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5643,8 +5575,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5754,8 +5684,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5855,8 +5783,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5993,8 +5919,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6102,8 +6026,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6215,8 +6137,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6280,8 +6200,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6439,8 +6357,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6533,8 +6449,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6627,8 +6541,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6808,8 +6720,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6917,8 +6827,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7170,8 +7078,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7294,8 +7200,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7472,8 +7376,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7594,8 +7496,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7657,8 +7557,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7720,8 +7618,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7783,8 +7679,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7846,8 +7740,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7940,8 +7832,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8034,8 +7924,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8128,8 +8016,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8222,8 +8108,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8342,8 +8226,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8436,8 +8318,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8530,8 +8410,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8625,8 +8503,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8724,8 +8600,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8823,8 +8697,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8941,8 +8813,6 @@ const char* UNPARSED_CONFIGS[] = {
 "            \"comment\": \"No dynamic DNS\",\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -9095,8 +8965,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -9272,8 +9140,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
index d38ba4433cebe88ff86934853bae4662afc50ea5..ee95de32cd5b7bcd97e8a22c310b8ac49e87311e 100644 (file)
@@ -375,6 +375,12 @@ ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
     if (rcode == CONTROL_RESULT_SUCCESS) {
         CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
 
+        // Update the fetch globals callback.
+        auto cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+        cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+            return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+        });
+
         // Use new configuration.
         CfgMgr::instance().commit();
     } else {
index a0068ecf8fe9f8e414b19e8de765717d3fdb08f5..ff6cce7e13d370048235dad0d1822c43e3e266e5 100644 (file)
@@ -473,6 +473,9 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         // early.
         Dhcp6ConfigParser global_parser;
 
+        // D2 client configuration.
+        D2ClientConfigPtr d2_client_cfg;
+
         BOOST_FOREACH(config_pair, values_map) {
             // In principle we could have the following code structured as a series
             // of long if else if clauses. That would give a marginal performance
@@ -567,8 +570,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
                 // Apply defaults
                 D2ClientConfigParser::setAllDefaults(config_pair.second);
                 D2ClientConfigParser parser;
-                D2ClientConfigPtr cfg = parser.parse(config_pair.second);
-                srv_config->setD2ClientConfig(cfg);
+                d2_client_cfg = parser.parse(config_pair.second);
                 continue;
             }
 
@@ -674,7 +676,9 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
                  (config_pair.first == "calculate-tee-times") ||
                  (config_pair.first == "t1-percent") ||
                  (config_pair.first == "t2-percent") ||
-                 (config_pair.first == "loggers")) {
+                 (config_pair.first == "loggers") ||
+                 (config_pair.first == "hostname-char-set") ||
+                 (config_pair.first == "hostname-char-replacement")) {
 
                 CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
                                                                         config_pair.second);
@@ -706,6 +710,16 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         // defined as part of shared networks.
         global_parser.sanityChecks(srv_config, mutable_cfg);
 
+        // Validate D2 client confuguration.
+        if (!d2_client_cfg) {
+            d2_client_cfg.reset(new D2ClientConfig());
+            d2_client_cfg->setFetchGlobalsFn([]() -> ConstElementPtr {
+                return (CfgMgr::instance().getStagingCfg()->getConfiguredGlobals());
+             });
+        }
+        d2_client_cfg->validateContents();
+        srv_config->setD2ClientConfig(d2_client_cfg);
+
     } catch (const isc::Exception& ex) {
         LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
                   .arg(config_pair.first).arg(ex.what());
index 9084da14e9df3ec001ba0670af3009a58b9774a3..5b32232d84c31dd8a4692593d8d9acda4f318725 100644 (file)
@@ -4627,8 +4627,167 @@ TEST_F(Dhcp6ParserTest, d2ClientConfig) {
     EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
     EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
     EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
     EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
     EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+}
+
+// This test checks the ability of the server to parse a configuration
+// containing a full, valid dhcp-ddns (D2ClientConfig) entry with
+// hostname-char-* at the global scope.
+TEST_F(Dhcp6ParserTest, d2ClientConfigGlobal) {
+    // Verify that the D2 configuration can be fetched and is set to disabled.
+    D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+
+    // Verify that the convenience method agrees.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    string config_str = "{ " + genIfaceConfig() + ","
+        "\"preferred-lifetime\": 3000,"
+        "\"valid-lifetime\": 4000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
+        "    \"subnet\": \"2001:db8:1::/64\" } ], "
+        " \"dhcp-ddns\" : {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"3001::1\", "
+        "     \"server-port\" : 777, "
+        "     \"sender-ip\" : \"3001::2\", "
+        "     \"sender-port\" : 778, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\" }, "
+        "\"hostname-char-set\" : \"[^A-Za-z0-9_-]\", "
+        "\"hostname-char-replacement\" : \"x\", "
+        "\"valid-lifetime\": 4000 }";
+
+    // Convert the JSON string to configuration elements.
+    ConstElementPtr config;
+    ASSERT_NO_THROW(config = parseDHCP6(config_str));
+    extractConfig(config_str);
+
+    // Pass the configuration in for parsing.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Verify that DHCP-DDNS updating is enabled.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+    // Verify that the D2 configuration can be retrieved.
+    d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are correct.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("3001::1", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(777, d2_client_config->getServerPort());
+    EXPECT_EQ("3001::2", d2_client_config->getSenderIp().toText());
+    EXPECT_EQ(778, d2_client_config->getSenderPort());
+    EXPECT_EQ(2048, d2_client_config->getMaxQueueSize());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+}
+
+// This test checks the ability of the server to parse a configuration
+// containing a full, valid dhcp-ddns (D2ClientConfig) entry with
+// hostname-char-* at the local and global scopes (local has the priority).
+TEST_F(Dhcp6ParserTest, d2ClientConfigBoth) {
+    // Verify that the D2 configuration can be fetched and is set to disabled.
+    D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+
+    // Verify that the convenience method agrees.
+    ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    string config_str = "{ " + genIfaceConfig() + ","
+        "\"preferred-lifetime\": 3000,"
+        "\"valid-lifetime\": 4000,"
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet6\": [ { "
+        "    \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
+        "    \"subnet\": \"2001:db8:1::/64\" } ], "
+        " \"dhcp-ddns\" : {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"3001::1\", "
+        "     \"server-port\" : 777, "
+        "     \"sender-ip\" : \"3001::2\", "
+        "     \"sender-port\" : 778, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\", "
+        "     \"hostname-char-set\" : \"[^A-Za-z0-9_-]\", "
+        "     \"hostname-char-replacement\" : \"x\" }, "
+        "\"hostname-char-set\" : \"[^A-Z]\", "
+        "\"hostname-char-replacement\" : \"z\", "
+        "\"valid-lifetime\": 4000 }";
+
+    // Convert the JSON string to configuration elements.
+    ConstElementPtr config;
+    ASSERT_NO_THROW(config = parseDHCP6(config_str));
+    extractConfig(config_str);
+
+    // Pass the configuration in for parsing.
+    ConstElementPtr status;
+    EXPECT_NO_THROW(status = configureDhcp6Server(srv_, config));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Verify that DHCP-DDNS updating is enabled.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+
+    // Verify that the D2 configuration can be retrieved.
+    d2_client_config = CfgMgr::instance().getD2ClientConfig();
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are correct.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("3001::1", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(777, d2_client_config->getServerPort());
+    EXPECT_EQ("3001::2", d2_client_config->getSenderIp().toText());
+    EXPECT_EQ(778, d2_client_config->getSenderPort());
+    EXPECT_EQ(2048, d2_client_config->getMaxQueueSize());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Za-z0-9_-]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("x", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 }
 
 // This test checks the ability of the server to handle a configuration
index b581ce5cfa47fcc7ed4d38f94fec5ef59478c3a5..55fbfc0807c19b4f8eed85b00aaded16df19e9d0 100644 (file)
@@ -1841,8 +1841,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -1911,8 +1909,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2007,8 +2003,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2178,8 +2172,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2349,8 +2341,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2520,8 +2510,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2616,8 +2604,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2713,8 +2699,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2810,8 +2794,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -2939,8 +2921,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3035,8 +3015,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3133,8 +3111,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3233,8 +3209,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3348,8 +3322,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3446,8 +3418,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3524,8 +3494,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3602,8 +3570,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3689,8 +3655,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3767,8 +3731,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3845,8 +3807,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -3958,8 +3918,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4071,8 +4029,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4194,8 +4150,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4300,8 +4254,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4449,8 +4401,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4588,8 +4538,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4737,8 +4685,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4848,8 +4794,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -4963,8 +4907,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5033,8 +4975,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5103,8 +5043,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5199,8 +5137,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5295,8 +5231,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5469,8 +5403,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5580,8 +5512,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -5795,8 +5725,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6016,8 +5944,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6135,8 +6061,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6205,8 +6129,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6275,8 +6197,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6471,8 +6391,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6592,8 +6510,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6662,8 +6578,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6730,8 +6644,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6798,8 +6710,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6866,8 +6776,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -6948,8 +6856,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7044,8 +6950,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7140,8 +7044,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7237,8 +7139,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7339,8 +7239,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7441,8 +7339,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7539,8 +7435,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7638,8 +7532,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7742,8 +7634,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -7853,8 +7743,6 @@ const char* UNPARSED_CONFIGS[] = {
 "            \"comment\": \"No dynamic DNS\",\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
@@ -8016,8 +7904,6 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"dhcp-ddns\": {\n"
 "            \"enable-updates\": false,\n"
 "            \"generated-prefix\": \"myhost\",\n"
-"            \"hostname-char-replacement\": \"\",\n"
-"            \"hostname-char-set\": \"\",\n"
 "            \"max-queue-size\": 1024,\n"
 "            \"ncr-format\": \"JSON\",\n"
 "            \"ncr-protocol\": \"UDP\",\n"
index 520cd3ce9b530c6d39f1f86abcaa7725aae7f0a4..0433aef08fa41882e3782d1a9e9e883fec1f7a73 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <dhcp_ddns/ncr_udp.h>
 #include <dhcpsrv/d2_client_cfg.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 
 #include <boost/algorithm/string/predicate.hpp>
index bd88cb46317f4041c3962665305667b26df616e4..4fdec9c81c7d4b7b9335c603d76a02779ec9b8ef 100644 (file)
@@ -295,10 +295,11 @@ public:
     /// @return a pointer to unparsed configuration
     virtual isc::data::ElementPtr toElement() const;
 
-protected:
     /// @brief Validates member values.
     ///
     /// Method is used by the constructor to validate member contents.
+    /// Should be called when parsing is complete to (re)compute
+    /// the hostname sanitizer.
     ///
     /// @throw D2ClientError if given an invalid protocol or format.
     virtual void validateContents();
index 9e62161bceed01691260dff29e1ea494124a45d1..a920754db04a6dcff0b0ceafc2cf66d07dd123e6 100644 (file)
@@ -1493,7 +1493,7 @@ D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
     // parameters to a subnet we need to set a callback function for
     // the d2 client config to allow for fetching global parameters.
     new_config->setFetchGlobalsFn([]() -> ConstElementPtr {
-        return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+        return (CfgMgr::instance().getStagingCfg()->getConfiguredGlobals());
     });
 
     return(new_config);
index 0e744e2e2620649d8529de77946f4a20b1cad7ac..4d907db92b6c1dc0a37841b83500ebdeced33a11 100644 (file)
@@ -70,7 +70,9 @@ const SimpleKeywords SimpleParser6::GLOBAL6_PARAMETERS = {
     { "calculate-tee-times",          Element::boolean },
     { "t1-percent",                   Element::real },
     { "t2-percent",                   Element::real },
-    { "loggers",                      Element::list }
+    { "loggers",                      Element::list },
+    { "hostname-char-set",            Element::string },
+    { "hostname-char-replacement",    Element::string }
 };
 
 /// @brief This table defines default values for option definitions in DHCPv6.
index bedd42b0c489921ffe0151960889ce78d28b2a9b..e1b59a442906964bf6bff817dcc4ac57f67b2981 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2019 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -127,7 +127,9 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
     EXPECT_EQ(d2_client_config->getGeneratedPrefix(), generated_prefix);
     EXPECT_EQ(d2_client_config->getQualifyingSuffix(), qualifying_suffix);
 
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
     EXPECT_EQ(d2_client_config->getHostnameCharSet(), hostname_char_set);
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
     EXPECT_EQ(d2_client_config->getHostnameCharReplacement(), hostname_char_replacement);
     EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 
@@ -180,6 +182,53 @@ TEST(D2ClientConfigTest, constructorsAndAccessors) {
                                                        hostname_char_replacement)),
                  D2ClientError);
 
+    Optional<std::string> opt_hostname_char_set("", true);
+    Optional<std::string> opt_hostname_char_replacement("", true);
+
+    // Veeify that constructor handles optional hostname char stuff.
+    ASSERT_NO_THROW(d2_client_config.reset(new
+                                           D2ClientConfig(enable_updates,
+                                                          server_ip,
+                                                          server_port,
+                                                          sender_ip,
+                                                          sender_port,
+                                                          max_queue_size,
+                                                          ncr_protocol,
+                                                          ncr_format,
+                                                          override_no_update,
+                                                          override_client_update,
+                                                          replace_client_name_mode,
+                                                          generated_prefix,
+                                                          qualifying_suffix,
+                                                          opt_hostname_char_set,
+                                                          opt_hostname_char_replacement)));
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the accessors return the expected values.
+    EXPECT_TRUE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ(d2_client_config->getHostnameCharSet(), opt_hostname_char_set);
+    EXPECT_TRUE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ(d2_client_config->getHostnameCharReplacement(), opt_hostname_char_replacement);
+    EXPECT_FALSE(d2_client_config->getHostnameSanitizer());
+
+    // Verify what toElement returns.
+    expected = "{\n"
+        "\"enable-updates\": true,\n"
+        "\"server-ip\": \"127.0.0.1\",\n"
+        "\"server-port\": 477,\n"
+        "\"sender-ip\": \"127.0.0.1\",\n"
+        "\"sender-port\": 478,\n"
+        "\"max-queue-size\": 2048,\n"
+        "\"ncr-protocol\": \"UDP\",\n"
+        "\"ncr-format\": \"JSON\",\n"
+        "\"override-no-update\": true,\n"
+        "\"override-client-update\": true,\n"
+        "\"replace-client-name\": \"when-present\",\n"
+        "\"generated-prefix\": \"the_prefix\",\n"
+        "\"qualifying-suffix\": \"the.suffix.\"\n"
+        "}\n";
+    runToElementTest<D2ClientConfig>(expected, *d2_client_config);
+
     /// @todo if additional validation is added to ctor, this test needs to
     /// expand accordingly.
 }
index 57f217111357f1d7e09b7395106706bf3fdcb545..231af76def821fcb5b149780c31f2280272b4c55 100644 (file)
@@ -216,6 +216,14 @@ public:
                     continue;
                 }
 
+                // Save global hostname-char-*.
+                if ((config_pair.first == "hostname-char-set") ||
+                    (config_pair.first == "hostname-char-replacement")) {
+                    CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
+                                                                            config_pair.second);
+                    continue;
+                }
+
                 if (config_pair.first == "hooks-libraries") {
                     HooksLibrariesParser hook_parser;
                     HooksConfig&  libraries =
@@ -254,6 +262,7 @@ public:
                 // Used to be done by parser commit
                 D2ClientConfigParser parser;
                 D2ClientConfigPtr cfg = parser.parse(d2_client_config->second);
+                cfg->validateContents();
                 CfgMgr::instance().setD2ClientConfig(cfg);
             }
 
@@ -1683,13 +1692,13 @@ TEST_F(ParseConfigTest, hexOptionData) {
         "0C:00:03:01:C0:00:03:02", // colons
         "0x0C000301C0000302",  // 0x
         "C 0 3 1 C0 0 3 02",  // one or two digit octets
-        "0x0c000301C0000302"   // upper or lower case digits 
+        "0x0c000301C0000302"   // upper or lower case digits
     };
 
     for (auto hex_str : valid_hexes) {
-        ostringstream os; 
+        ostringstream os;
         os <<
-            "{ \n" 
+            "{ \n"
             "  \"option-data\": [ { \n"
             "    \"name\": \"domain-name-servers\", \n"
             "    \"code \": 6, \n"
@@ -2261,6 +2270,11 @@ TEST_F(ParseConfigTest, validD2Config) {
     EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
     ASSERT_TRUE(d2_client_config->getContext());
     EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 
     // Verify that the configuration object unparses.
     ConstElementPtr expected;
@@ -2315,6 +2329,251 @@ TEST_F(ParseConfigTest, validD2Config) {
     EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
     ASSERT_TRUE(d2_client_config->getContext());
     EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+
+    ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns"));
+    ASSERT_TRUE(expected);
+    runToElementTest<D2ClientConfig>(expected, *d2_client_config);
+}
+
+/// @brief Checks that a valid, enabled D2 client configuration works correctly
+/// with hostname-char stuff moved to global.
+TEST_F(ParseConfigTest, validD2ConfigGlobal) {
+
+    // Configuration string containing valid values.
+    std::string config_str =
+        "{ \"dhcp-ddns\" :"
+        "    {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"192.0.2.0\", "
+        "     \"server-port\" : 3432, "
+        "     \"sender-ip\" : \"192.0.2.1\", "
+        "     \"sender-port\" : 3433, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\", "
+        "     \"user-context\": { \"foo\": \"bar\" } "
+        "    },"
+        "  \"hostname-char-set\" : \"[^A-Z]\", "
+        "  \"hostname-char-replacement\" : \"*\" "
+        "}";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config_str);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+    D2ClientConfigPtr d2_client_config;
+    ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are as expected.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("192.0.2.0", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(3432, d2_client_config->getServerPort());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    ASSERT_TRUE(d2_client_config->getContext());
+    EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+
+    // Verify that the configuration object unparses.
+    ConstElementPtr expected;
+    ASSERT_NO_THROW(expected = Element::fromJSON(config_str)->get("dhcp-ddns"));
+    ASSERT_TRUE(expected);
+    runToElementTest<D2ClientConfig>(expected, *d2_client_config);
+
+    // Another valid Configuration string.
+    // This one is disabled, has IPV6 server ip, control flags false,
+    // empty prefix/suffix
+    std::string config_str2 =
+        "{ \"dhcp-ddns\" :"
+        "    {"
+        "     \"enable-updates\" : false, "
+        "     \"server-ip\" : \"2001:db8::\", "
+        "     \"server-port\" : 43567, "
+        "     \"sender-ip\" : \"2001:db8::1\", "
+        "     \"sender-port\" : 3433, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : false, "
+        "     \"override-client-update\" : false, "
+        "     \"replace-client-name\" : \"never\", "
+        "     \"generated-prefix\" : \"\", "
+        "     \"qualifying-suffix\" : \"\", "
+        "     \"user-context\": { \"foo\": \"bar\" } "
+        "    },"
+        "  \"hostname-char-set\" : \"[^A-Z]\", "
+        "  \"hostname-char-replacement\" : \"*\" "
+        "}";
+
+    // Verify that the configuration string parses.
+    rcode = parseConfiguration(config_str2);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
+    EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
+    ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are as expected.
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(43567, d2_client_config->getServerPort());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_NEVER, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
+    ASSERT_TRUE(d2_client_config->getContext());
+    EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+
+    ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns"));
+    ASSERT_TRUE(expected);
+    runToElementTest<D2ClientConfig>(expected, *d2_client_config);
+}
+
+/// @brief Checks that a valid, enabled D2 client configuration works correctly
+/// with hostname-char stuff in both local and global (local has the priority).
+TEST_F(ParseConfigTest, validD2ConfigBoth) {
+
+    // Configuration string containing valid values.
+    std::string config_str =
+        "{ \"dhcp-ddns\" :"
+        "    {"
+        "     \"enable-updates\" : true, "
+        "     \"server-ip\" : \"192.0.2.0\", "
+        "     \"server-port\" : 3432, "
+        "     \"sender-ip\" : \"192.0.2.1\", "
+        "     \"sender-port\" : 3433, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : true, "
+        "     \"override-client-update\" : true, "
+        "     \"replace-client-name\" : \"when-present\", "
+        "     \"generated-prefix\" : \"test.prefix\", "
+        "     \"qualifying-suffix\" : \"test.suffix.\", "
+        "     \"user-context\": { \"foo\": \"bar\" } "
+        "    },"
+        "  \"hostname-char-set\" : \"[^A-Z]\", "
+        "  \"hostname-char-replacement\" : \"*\" "
+        "}";
+
+    // Verify that the configuration string parses.
+    int rcode = parseConfiguration(config_str);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Verify that DHCP-DDNS is enabled and we can fetch the configuration.
+    EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
+    D2ClientConfigPtr d2_client_config;
+    ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are as expected.
+    EXPECT_TRUE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("192.0.2.0", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(3432, d2_client_config->getServerPort());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+    ASSERT_TRUE(d2_client_config->getContext());
+    EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
+
+    // Verify that the configuration object unparses.
+    ConstElementPtr expected;
+    ASSERT_NO_THROW(expected = Element::fromJSON(config_str)->get("dhcp-ddns"));
+    ASSERT_TRUE(expected);
+    runToElementTest<D2ClientConfig>(expected, *d2_client_config);
+
+    // Another valid Configuration string.
+    // This one is disabled, has IPV6 server ip, control flags false,
+    // empty prefix/suffix
+    std::string config_str2 =
+        "{ \"dhcp-ddns\" :"
+        "    {"
+        "     \"enable-updates\" : false, "
+        "     \"server-ip\" : \"2001:db8::\", "
+        "     \"server-port\" : 43567, "
+        "     \"sender-ip\" : \"2001:db8::1\", "
+        "     \"sender-port\" : 3433, "
+        "     \"max-queue-size\" : 2048, "
+        "     \"ncr-protocol\" : \"UDP\", "
+        "     \"ncr-format\" : \"JSON\", "
+        "     \"override-no-update\" : false, "
+        "     \"override-client-update\" : false, "
+        "     \"replace-client-name\" : \"never\", "
+        "     \"generated-prefix\" : \"\", "
+        "     \"qualifying-suffix\" : \"\", "
+        "     \"user-context\": { \"foo\": \"bar\" } "
+        "    },"
+        "  \"hostname-char-set\" : \"[^A-Z]\", "
+        "  \"hostname-char-replacement\" : \"*\" "
+        "}";
+
+    // Verify that the configuration string parses.
+    rcode = parseConfiguration(config_str2);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Verify that DHCP-DDNS is disabled and we can fetch the configuration.
+    EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
+    ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
+    ASSERT_TRUE(d2_client_config);
+
+    // Verify that the configuration values are as expected.
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+    EXPECT_EQ("2001:db8::", d2_client_config->getServerIp().toText());
+    EXPECT_EQ(43567, d2_client_config->getServerPort());
+    EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
+    EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
+    EXPECT_FALSE(d2_client_config->getOverrideNoUpdate());
+    EXPECT_FALSE(d2_client_config->getOverrideClientUpdate());
+    EXPECT_EQ(D2ClientConfig::RCM_NEVER, d2_client_config->getReplaceClientNameMode());
+    EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
+    EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
+    ASSERT_TRUE(d2_client_config->getContext());
+    EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 
     ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns"));
     ASSERT_TRUE(expected);
@@ -2345,6 +2604,36 @@ TEST_F(ParseConfigTest, validDisabledD2Config) {
     ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
     EXPECT_TRUE(d2_client_config);
     EXPECT_FALSE(d2_client_config->getEnableUpdates());
+    EXPECT_TRUE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_FALSE(d2_client_config->getHostnameSanitizer());
+
+    // Retry with hostname-char-* globals.
+    std::string config_str2 =
+        "{ \"dhcp-ddns\" :"
+        "    {"
+        "     \"enable-updates\" : false"
+        "    },"
+        "  \"hostname-char-set\" : \"[^A-Z]\", "
+        "  \"hostname-char-replacement\" : \"*\" "
+        "}";
+
+    // Verify that the configuration string parses.
+    rcode = parseConfiguration(config_str2);
+    ASSERT_TRUE(rcode == 0) << error_text_;
+
+    // Verify that DHCP-DDNS is disabled.
+    EXPECT_FALSE(CfgMgr::instance().ddnsEnabled());
+
+    // Make sure fetched config agrees.
+    ASSERT_NO_THROW(d2_client_config = CfgMgr::instance().getD2ClientConfig());
+    EXPECT_TRUE(d2_client_config);
+    EXPECT_FALSE(d2_client_config->getEnableUpdates());
+    EXPECT_FALSE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_EQ("[^A-Z]", d2_client_config->getHostnameCharSet().get());
+    EXPECT_FALSE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_EQ("*", d2_client_config->getHostnameCharReplacement().get());
+    EXPECT_TRUE(d2_client_config->getHostnameSanitizer());
 }
 
 /// @brief Checks that given a partial configuration, parser supplies
@@ -2394,6 +2683,11 @@ TEST_F(ParseConfigTest, parserDefaultsD2Config) {
               d2_client_config->getGeneratedPrefix());
     EXPECT_EQ("test.suffix.",
               d2_client_config->getQualifyingSuffix());
+    EXPECT_TRUE(d2_client_config->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(d2_client_config->getHostnameCharSet().empty());
+    EXPECT_TRUE(d2_client_config->getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(d2_client_config->getHostnameCharReplacement().empty());
+    EXPECT_FALSE(d2_client_config->getHostnameSanitizer());
 }