]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#35,!517] Added hostname-char-set to hostname-char-replacement network/subnet scopes
authorThomas Markwalder <tmark@isc.org>
Fri, 27 Sep 2019 19:52:28 +0000 (15:52 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 10 Oct 2019 12:32:44 +0000 (08:32 -0400)
src/lib/dhcpsrv/d2_client_cfg.*
    Added hostname-char-set and hostname-char-replacement to DdnsParms
    DdnsParams::getHostnameSanitizer()  - new function to return a sanitizer

src/lib/dhcpsrv/d2_client_mgr.h
    D2ClientMgr::adjustDomainName() - now gets sanitizer from ddns_params

src/lib/dhcpsrv/network.*
src/lib/dhcpsrv/parsers/base_network_parser.cc
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
    Added hostname-char-set and hostname-char-replacement to networks and subnets

21 files changed:
src/lib/dhcpsrv/d2_client_cfg.cc
src/lib/dhcpsrv/d2_client_cfg.h
src/lib/dhcpsrv/d2_client_mgr.h
src/lib/dhcpsrv/network.cc
src/lib/dhcpsrv/network.h
src/lib/dhcpsrv/parsers/base_network_parser.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.h
src/lib/dhcpsrv/parsers/simple_parser4.cc
src/lib/dhcpsrv/parsers/simple_parser6.cc
src/lib/dhcpsrv/srv_config.cc
src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc
src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/d2_client_unittest.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
src/lib/dhcpsrv/tests/network_unittest.cc
src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc
src/lib/dhcpsrv/tests/shared_network_unittest.cc
src/lib/dhcpsrv/tests/srv_config_unittest.cc
src/lib/dhcpsrv/tests/subnet_unittest.cc

index 83b1d292644377fc09f10f884361ec59bf25838c..e6ca69fb870bf6fc601be142c19db50f8f9ffc34 100644 (file)
@@ -214,6 +214,23 @@ D2ClientConfig::toElement() const {
     return (result);
 }
 
+str::StringSanitizerPtr
+DdnsParams::getHostnameSanitizer() const {
+    str::StringSanitizerPtr sanitizer;
+    // If we have a local char_set we need to create the sanitizer.
+    if (!hostname_char_set_.empty()) {
+        try {
+            sanitizer.reset(new str::StringSanitizer(hostname_char_set_,
+                                                     hostname_char_replacement_));
+        } catch (const std::exception& ex) {
+            isc_throw(BadValue, "hostname_char_set_: '" << hostname_char_set_ <<
+                      "' is not a valid regular expression");
+        }
+    }
+
+    return (sanitizer);
+}
+
 std::ostream&
 operator<<(std::ostream& os, const D2ClientConfig& config) {
     os << config.toText();
index 0bb80a83dd96c3aa1711c95c5f036e9218874af3..4b67409df489ab932592d807e8c4d5a373e5739f 100644 (file)
@@ -197,8 +197,6 @@ public:
     /// @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();
@@ -244,7 +242,8 @@ struct DdnsParams {
     DdnsParams() :
         enable_updates_(false), override_no_update_(false), override_client_update_(false),
         replace_client_name_mode_(D2ClientConfig::RCM_NEVER),
-        generated_prefix_(""), qualifying_suffix_("") {};
+        generated_prefix_(""), qualifying_suffix_(""), hostname_char_set_(""),
+        hostname_char_replacement_("") {};
 
     /// @brief Indicates whether or not DHCP DDNS updating is enabled.
     bool enable_updates_;
@@ -265,8 +264,24 @@ struct DdnsParams {
     /// @brief Suffix Kea should use when to qualify partial domain-names.
     std::string qualifying_suffix_;
 
-    /// @brief Pointer to compiled regular expression string sanitizer
-    util::str::StringSanitizerPtr hostname_sanitizer_;
+    /// @brief Regular expression describing invalid characters for client hostnames.
+    /// If empty, host name scrubbing should not be done.
+    std::string hostname_char_set_;
+
+    /// @brief A string to replace invalid characters when scrubbing hostnames.
+    /// Meaningful only if hostname_char_set_ is not empty.
+    std::string hostname_char_replacement_;
+
+    /// @brief Returns a regular expression string sanitizer
+    ///
+    /// If hostname_char_set_ is not empty, then it is used in conjunction
+    /// hostname_char_replacment_ (which may be empty) to create and
+    /// return a StringSanitizer instance.  Otherwise it will return
+    /// an empty pointer.
+    ///
+    /// @return pointer to the StringSanitizer instance or an empty pointer
+    /// @throw BadValue if the compilation fails.
+    isc::util::str::StringSanitizerPtr getHostnameSanitizer() const;
 };
 
 /// @brief Defines a pointer for DdnsParams instances.
index cc655b4c121cda5592c399fcbbee1e1441fdf17c..63bb64234f9a68ed6b7aa2dba6262a802e5052a2 100644 (file)
@@ -482,7 +482,8 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddn
         // Sanitize the name the client sent us, if we're configured to do so.
         std::string client_name = fqdn.getDomainName();
 
-        if (ddns_params.hostname_sanitizer_) {
+        isc::util::str::StringSanitizerPtr sanitizer = ddns_params.getHostnameSanitizer();
+        if (sanitizer) {
             // We need the raw text form, so we can replace escaped chars
             dns::Name tmp(client_name);
             std::string raw_name = tmp.toRawText();
@@ -499,7 +500,7 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddn
                     ss << ".";
                 }
 
-                ss << ddns_params.hostname_sanitizer_->scrub(*label);
+                ss << sanitizer->scrub(*label);
             }
 
             client_name = ss.str();
index 41a8feca4f10a94b2b69e6bd92dbae4b0cb40fed..ed361588d72ca12c492e781b8f2407e493bc80ae 100644 (file)
@@ -268,6 +268,14 @@ Network::toElement() const {
         map->set("ddns-qualifying-suffix", Element::create(ddns_qualifying_suffix_));
     }
 
+    if (!hostname_char_set_.unspecified()) {
+        map->set("hostname-char-set", Element::create(hostname_char_set_));
+    }
+
+    if (!hostname_char_replacement_.unspecified()) {
+        map->set("hostname-char-replacement", Element::create(hostname_char_replacement_));
+    }
+
     return (map);
 }
 
index 928f5ea067730b4dcabc05d7f95b77b25a22f3b9..36715e6a112fd6587dda7b0a214a0f9e9618f006 100644 (file)
@@ -206,7 +206,8 @@ public:
           host_reservation_mode_(HR_ALL, true), cfg_option_(new CfgOption()),
           calculate_tee_times_(), t1_percent_(), t2_percent_(),
           ddns_send_updates_(), ddns_override_no_update_(), ddns_override_client_update_(),
-          ddns_replace_client_name_mode_(), ddns_generated_prefix_(), ddns_qualifying_suffix_() {
+          ddns_replace_client_name_mode_(), ddns_generated_prefix_(), ddns_qualifying_suffix_(),
+          hostname_char_set_(), hostname_char_replacement_() {
     }
 
     /// @brief Virtual destructor.
@@ -529,14 +530,14 @@ public:
                                      inheritance, "ddns-send-updates"));
     }
 
-    /// @brief Sets new ddns-send-updates  
+    /// @brief Sets new ddns-send-updates
     ///
     /// @param ddns_send_updates_ New value to use.
     void setDdnsSendUpdates(const util::Optional<bool>& ddns_send_updates) {
         ddns_send_updates_ = ddns_send_updates;
     }
 
-    /// @brief Returns ddns-override-no-update 
+    /// @brief Returns ddns-override-no-update
     ///
     /// @param inheritance inheritance mode to be used.
     util::Optional<bool>
@@ -545,14 +546,14 @@ public:
                                      inheritance, "ddns-override-no-update"));
     }
 
-    /// @brief Sets new ddns-override-no-update 
+    /// @brief Sets new ddns-override-no-update
     ///
     /// @param ddns_override_no_update New value to use.
     void setDdnsOverrideNoUpdate(const util::Optional<bool>& ddns_override_no_update) {
         ddns_override_no_update_ = ddns_override_no_update;
     }
 
-    /// @brief Returns ddns-overridie-client-update 
+    /// @brief Returns ddns-overridie-client-update
     ///
     /// @param inheritance inheritance mode to be used.
     util::Optional<bool>
@@ -561,14 +562,14 @@ public:
                                      inheritance, "ddns-override-client-update"));
     }
 
-    /// @brief Sets new ddns-override-client-update  
+    /// @brief Sets new ddns-override-client-update
     ///
     /// @param ddns-override-client-update New value to use.
     void setDdnsOverrideClientUpdate(const util::Optional<bool>& ddns_override_client_update) {
         ddns_override_client_update_ = ddns_override_client_update;
     }
 
-    /// @brief Returns ddns-replace-client-name-mode 
+    /// @brief Returns ddns-replace-client-name-mode
     ///
     /// @param inheritance inheritance mode to be used.
     util::Optional<D2ClientConfig::ReplaceClientNameMode>
@@ -578,7 +579,7 @@ public:
         // Thus we call getProperty here without a global name to check if it
         // is specified on network level only.
         const util::Optional<D2ClientConfig::ReplaceClientNameMode>& mode
-            = getProperty<Network>(&Network::getDdnsReplaceClientNameMode, 
+            = getProperty<Network>(&Network::getDdnsReplaceClientNameMode,
                                    ddns_replace_client_name_mode_, inheritance);
 
         // If it is not specified at network level we need this special
@@ -604,15 +605,15 @@ public:
         return (mode);
     }
 
-    /// @brief Sets new ddns-replace-client-name-mode  
+    /// @brief Sets new ddns-replace-client-name-mode
     ///
     /// @param ddns_replace_client_name_mode New value to use.
-    void setDdnsReplaceClientNameMode(const util::Optional<D2ClientConfig::ReplaceClientNameMode>& 
+    void setDdnsReplaceClientNameMode(const util::Optional<D2ClientConfig::ReplaceClientNameMode>&
                                           ddns_replace_client_name_mode) {
         ddns_replace_client_name_mode_ = ddns_replace_client_name_mode;
     }
 
-    /// @brief Returns ddns-generated-prefix 
+    /// @brief Returns ddns-generated-prefix
     ///
     /// @param inheritance inheritance mode to be used.
     util::Optional<std::string>
@@ -628,7 +629,7 @@ public:
         ddns_generated_prefix_ = ddns_generated_prefix;
     }
 
-    /// @brief Returns ddns-qualifying-suffix 
+    /// @brief Returns ddns-qualifying-suffix
     ///
     /// @param inheritance inheritance mode to be used.
     util::Optional<std::string>
@@ -637,14 +638,40 @@ public:
                                      inheritance, "ddns-qualifying-suffix"));
     }
 
-    /// @brief Sets new ddns-qualifying-suffix  
+    /// @brief Sets new ddns-qualifying-suffix
     ///
     /// @param ddns_qualifying_suffix New value to use.
     void setDdnsQualifyingSuffix(const util::Optional<std::string>& ddns_qualifying_suffix) {
         ddns_qualifying_suffix_ = ddns_qualifying_suffix;
     }
 
+    /// @brief Return the char set regexp used to sanitize client hostnames.
+    util::Optional<std::string>
+    getHostnameCharSet(const Inheritance& inheritance = Inheritance::ALL) const {
+        return (getProperty<Network>(&Network::getHostnameCharSet, hostname_char_set_,
+                                     inheritance, "hostname-char-set"));
+    }
+
+    /// @brief Sets new hostname-char-set
+    ///
+    /// @param hostname_char_set New value to use.
+    void setHostnameCharSet(const util::Optional<std::string>& hostname_char_set) {
+        hostname_char_set_ = hostname_char_set;
+    }
+
+    /// @brief Return the invalid char replacement used to sanitize client hostnames.
+    util::Optional<std::string>
+    getHostnameCharReplacement(const Inheritance& inheritance = Inheritance::ALL) const {
+        return (getProperty<Network>(&Network::getHostnameCharReplacement, hostname_char_replacement_,
+                                     inheritance, "hostname-char-replacement"));
+    }
 
+    /// @brief Sets new hostname-char-replacement
+    ///
+    /// @param hostname_char_replacement New value to use.
+    void setHostnameCharReplacement(const util::Optional<std::string>& hostname_char_replacement) {
+        hostname_char_replacement_ = hostname_char_replacement;
+    }
 
     /// @brief Unparses network object.
     ///
@@ -928,6 +955,14 @@ protected:
     /// @brief Suffix Kea should use when to qualify partial domain-names.
     util::Optional<std::string> ddns_qualifying_suffix_;
 
+    /// @brief Regular expression describing invalid characters for client
+    /// hostnames.
+    util::Optional<std::string> hostname_char_set_;
+
+    /// @brief A string to replace invalid characters when scrubbing hostnames.
+    /// Meaningful only if hostname_char_set_ is not empty.
+    util::Optional<std::string> hostname_char_replacement_;
+
     /// @brief Pointer to another network that this network belongs to.
     ///
     /// The most common case is that this instance is a subnet which belongs
index c67ee172fe493ff1bfa2b78e7ff3e9bb9325c39c..ef96b3df6ad41e24a2bb30a3337e6224db7ca9f6 100644 (file)
@@ -8,6 +8,7 @@
 #include <dhcpsrv/triplet.h>
 #include <dhcpsrv/parsers/base_network_parser.h>
 #include <util/optional.h>
+#include <util/strutil.h>
 
 using namespace isc::data;
 using namespace isc::util;
@@ -186,7 +187,7 @@ BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
     if (network_data->contains("ddns-replace-client-name")) {
         network->setDdnsReplaceClientNameMode(getAndConvert<D2ClientConfig::ReplaceClientNameMode,
                                                             D2ClientConfig::stringToReplaceClientNameMode>
-                                                            (network_data, "ddns-replace-client-name", 
+                                                            (network_data, "ddns-replace-client-name",
                                                              "ReplaceClientName mode"));
     }
 
@@ -197,6 +198,31 @@ BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
     if (network_data->contains("ddns-qualifying-suffix")) {
         network->setDdnsQualifyingSuffix(getString(network_data, "ddns-qualifying-suffix"));
     }
+
+    std::string hostname_char_set;
+    if (network_data->contains("hostname-char-set")) {
+        hostname_char_set = getString(network_data, "hostname-char-set");
+        network->setHostnameCharSet(hostname_char_set);
+    }
+
+    std::string hostname_char_replacement;
+    if (network_data->contains("hostname-char-replacement")) {
+        hostname_char_replacement = getString(network_data, "hostname-char-replacement");
+        network->setHostnameCharReplacement(hostname_char_replacement);
+    }
+
+    // We need to validate santizer values here so we can detect problems and
+    // cause a configuration.  We dont' retain the compilation because it's not
+    // something we can inherit.
+    if (!hostname_char_set.empty()) {
+        try {
+            str::StringSanitizerPtr sanitizer(new str::StringSanitizer(hostname_char_set,
+                                                                       hostname_char_replacement));
+        } catch (const std::exception& ex) {
+            isc_throw(BadValue, "hostname-char-set '" << hostname_char_set
+                      << "' is not a valid regular expression");
+        }
+    }
 }
 
 } // end of namespace isc::dhcp
index 8333c4b74962c0e1a80c567c54d9bd748148d438..045d619a220fb3a552f8a025f18003bbab03e9c6 100644 (file)
@@ -796,7 +796,6 @@ public:
     /// The elements currently supported are (see isc::dhcp::D2ClientConfig
     /// for details on each):
     /// -# enable-updates
-    /// -# qualifying-suffix
     /// -# server-ip
     /// -# server-port
     /// -# sender-ip
@@ -804,10 +803,6 @@ public:
     /// -# max-queue-size
     /// -# ncr-protocol
     /// -# ncr-format
-    /// -# override-no-update
-    /// -# override-client-update
-    /// -# replace-client-name
-    /// -# generated-prefix
     ///
     /// @return returns a pointer to newly created D2ClientConfig.
     D2ClientConfigPtr parse(isc::data::ConstElementPtr d2_client_cfg);
index a2a0e74557984fb2837ec1cd19c6d67f8263588d..f0ed3d2ddf2755710831587b2072fe8650927711 100644 (file)
@@ -111,7 +111,9 @@ const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
     { "ddns-replace-client-name",       Element::string, "never" },
     { "ddns-generated-prefix",          Element::string, "myhost" },
     // TKM should this still be true? qualifying-suffix has no default ??
-    { "ddns-generated-suffix",          Element::string, "" }
+    { "ddns-generated-suffix",          Element::string, "" },
+    { "hostname-char-set",            Element::string, "" },
+    { "hostname-char-replacement",    Element::string, "" },
 };
 
 /// @brief This table defines all option definition parameters.
@@ -211,6 +213,8 @@ const SimpleKeywords SimpleParser4::SUBNET4_PARAMETERS = {
     { "ddns-replace-client-name",       Element::string },
     { "ddns-generated-prefix",          Element::string },
     { "ddns-qualifying-suffix",         Element::string },
+    { "hostname-char-set",              Element::string },
+    { "hostname-char-replacement",      Element::string },
     { "metadata",                       Element::map },
 };
 
@@ -275,7 +279,9 @@ const ParamsList SimpleParser4::INHERIT_TO_SUBNET4 = {
     "ddns-override-client-update",
     "ddns-replace-client-name",
     "ddns-generated-prefix",
-    "ddns-qualifying-suffix"
+    "ddns-qualifying-suffix",
+    "hostname-char-set",
+    "hostname-char-replacement"
 };
 
 /// @brief This table defines all pool parameters.
@@ -328,6 +334,8 @@ const SimpleKeywords SimpleParser4::SHARED_NETWORK4_PARAMETERS = {
     { "ddns-replace-client-name",       Element::string },
     { "ddns-generated-prefix",          Element::string },
     { "ddns-qualifying-suffix",         Element::string },
+    { "hostname-char-set",              Element::string },
+    { "hostname-char-replacement",      Element::string },
     { "metadata",                       Element::map },
 };
 
index 0454560e6b677efeb3e4293236d31b1d8d1cfedf..fcc61f5adc778e80ed314489fb76746a82dddb77 100644 (file)
@@ -106,7 +106,9 @@ const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
     { "ddns-replace-client-name",       Element::string, "never" },
     { "ddns-generated-prefix",          Element::string, "myhost" },
     // TKM should this still be true? qualifying-suffix has no default ??
-    { "ddns-generated-suffix",          Element::string, "" }
+    { "ddns-generated-suffix",          Element::string, "" },
+    { "hostname-char-set",              Element::string, "" },
+    { "hostname-char-replacement",      Element::string, "" },
 };
 
 /// @brief This table defines all option definition parameters.
@@ -204,6 +206,8 @@ const SimpleKeywords SimpleParser6::SUBNET6_PARAMETERS = {
     { "ddns-replace-client-name",       Element::string },
     { "ddns-generated-prefix",          Element::string },
     { "ddns-qualifying-suffix",         Element::string },
+    { "hostname-char-set",              Element::string },
+    { "hostname-char-replacement",      Element::string },
     { "metadata",                       Element::map }
 };
 
@@ -256,7 +260,9 @@ const ParamsList SimpleParser6::INHERIT_TO_SUBNET6 = {
     "ddns-override-client-update",
     "ddns-replace-client-name",
     "ddns-generated-prefix",
-    "ddns-qualifying-suffix"
+    "ddns-qualifying-suffix",
+    "hostname-char-set",
+    "hostname-char-replacement"
 };
 
 /// @brief This table defines all pool parameters.
@@ -328,6 +334,8 @@ const SimpleKeywords SimpleParser6::SHARED_NETWORK6_PARAMETERS = {
     { "ddns-replace-client-name",       Element::string },
     { "ddns-generated-prefix",          Element::string },
     { "ddns-qualifying-suffix",         Element::string },
+    { "hostname-char-set",              Element::string },
+    { "hostname-char-replacement",      Element::string },
     { "metadata",                       Element::map }
 };
 
index ef12fda612d2e242f26d46498616544b0fa3e5a0..0c681496d15816fd8d4f423f8619f368a684749a 100644 (file)
@@ -589,6 +589,8 @@ DdnsParamsPtr SrvConfig::getDdnsParams(const Subnet& subnet) const {
     params->replace_client_name_mode_= subnet.getDdnsReplaceClientNameMode().get();
     params->generated_prefix_ = subnet.getDdnsGeneratedPrefix().get();
     params->qualifying_suffix_ = subnet.getDdnsQualifyingSuffix().get();
+    params->hostname_char_set_ = subnet.getHostnameCharSet().get();
+    params->hostname_char_replacement_ = subnet.getHostnameCharReplacement().get();
 
     return params;
 }
index 3114d0bea73bd03a97cf17138eb09c49a3ae0b5c..c2f870c0e1c3ce746735c2fbaef2cae7c8d25120 100644 (file)
@@ -205,6 +205,8 @@ TEST(CfgSharedNetworks4Test, unparse) {
     network1->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
     network1->setDdnsGeneratedPrefix("prefix");
     network1->setDdnsQualifyingSuffix("example.com.");
+    network1->setHostnameCharSet("[^A-Z]");
+    network1->setHostnameCharReplacement("x");
 
     network2->setIface("eth1");
     network2->setT1(Triplet<uint32_t>(100));
@@ -257,7 +259,9 @@ TEST(CfgSharedNetworks4Test, unparse) {
         "    \"relay\": { \"ip-addresses\": [ \"198.16.1.1\", \"198.16.1.2\" ] },\n"
         "    \"subnet4\": [ ],\n"
         "    \"t1-percent\": .35,\n"
-        "    \"t2-percent\": .655\n"
+        "    \"t2-percent\": .655,\n"
+        "    \"hostname-char-replacement\": \"x\",\n"
+        "    \"hostname-char-set\": \"[^A-Z]\"\n"
         "  }\n"
         "]\n";
 
index e6643657fcdce753f9ac6864ed85166324e8bb87..b2f71ebe80d95e4e0302c48263eaaee5b0750c74 100644 (file)
@@ -206,6 +206,8 @@ TEST(CfgSharedNetworks6Test, unparse) {
     network1->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
     network1->setDdnsGeneratedPrefix("prefix");
     network1->setDdnsQualifyingSuffix("example.com.");
+    network1->setHostnameCharSet("[^A-Z]");
+    network1->setHostnameCharReplacement("x");
 
     network2->setIface("eth1");
     network2->setT1(Triplet<uint32_t>(100));
@@ -264,7 +266,9 @@ TEST(CfgSharedNetworks6Test, unparse) {
         "    \"relay\": { \"ip-addresses\": [ \"2001:db8:1::1\", \"2001:db8:1::2\" ] },\n"
         "    \"subnet6\": [ ],\n"
         "    \"t1-percent\": .35,\n"
-        "    \"t2-percent\": .655\n"
+        "    \"t2-percent\": .655,\n"
+        "    \"hostname-char-replacement\": \"x\",\n"
+        "    \"hostname-char-set\": \"[^A-Z]\"\n"
         "  }\n"
         "]\n";
 
index ce7e4cd0f85ca34ac51e70ef1d94bdb2de0ea484..19fe8052fa0f07a41c7b79a234849e9c07825d48 100644 (file)
@@ -20,6 +20,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_selector.h>
+#include <testutils/gtest_utils.h>
 #include <testutils/test_to_element.h>
 #include <util/doubles.h>
 
@@ -1034,6 +1035,14 @@ TEST(CfgSubnets4Test, unparseSubnet) {
     subnet3->setSname("frog");
     subnet3->setFilename("/dev/null");
     subnet3->setValid(Triplet<uint32_t>(100, 200, 300));
+    subnet3->setDdnsSendUpdates(true);
+    subnet3->setDdnsOverrideNoUpdate(true);
+    subnet3->setDdnsOverrideClientUpdate(true);
+    subnet3->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
+    subnet3->setDdnsGeneratedPrefix("prefix");
+    subnet3->setDdnsQualifyingSuffix("example.com.");
+    subnet3->setHostnameCharSet("[^A-Z]");
+    subnet3->setHostnameCharReplacement("x");
 
     data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }");
     subnet1->setContext(ctx1);
@@ -1100,8 +1109,17 @@ TEST(CfgSubnets4Test, unparseSubnet) {
         "    \"require-client-classes\": [ \"foo\", \"bar\" ],\n"
         "    \"calculate-tee-times\": true,\n"
         "    \"t1-percent\": 0.50,\n"
-        "    \"t2-percent\": 0.65\n"
+        "    \"t2-percent\": 0.65,\n"
+        "    \"ddns-generated-prefix\": \"prefix\",\n"
+        "    \"ddns-override-client-update\": true,\n"
+        "    \"ddns-override-no-update\": true,\n"
+        "    \"ddns-qualifying-suffix\": \"example.com.\",\n"
+        "    \"ddns-replace-client-name\": \"always\",\n"
+        "    \"ddns-send-updates\": true,\n"
+        "    \"hostname-char-replacement\": \"x\",\n"
+        "    \"hostname-char-set\": \"[^A-Z]\"\n"
         "} ]\n";
+
     runToElementTest<CfgSubnets4>(expected, cfg);
 }
 
@@ -1541,4 +1559,61 @@ TEST(CfgSubnets4Test, validLifetimeValidation) {
     }
 }
 
+// This test verifies the Subnet4 parser's validation logic for
+// hostname sanitizer values.
+TEST(CfgSubnets4Test, hostnameSanitizierValidation) {
+
+    // First we create a set of elements that provides all
+    // required for a Subnet4.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"10.1.2.0/24\", \n"
+        "            \"interface\": \"\", \n"
+        "            \"renew-timer\": 100, \n"
+        "            \"rebind-timer\": 200, \n"
+        "            \"match-client-id\": false, \n"
+        "            \"authoritative\": false, \n"
+        "            \"next-server\": \"\", \n"
+        "            \"server-hostname\": \"\", \n"
+        "            \"boot-file-name\": \"\", \n"
+        "            \"client-class\": \"\", \n"
+        "            \"require-client-classes\": [] \n,"
+        "            \"reservation-mode\": \"all\", \n"
+        "            \"4o6-interface\": \"\", \n"
+        "            \"4o6-interface-id\": \"\", \n"
+        "            \"4o6-subnet\": \"\" \n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+                    << "invalid JSON:" << json << "\n test is broken";
+
+    {
+        SCOPED_TRACE("invalid regular expression");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("hostname-char-set", data::Element::create("^[A-"));
+        copied->set("hostname-char-replacement", data::Element::create("x"));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        EXPECT_THROW_MSG(subnet = parser.parse(copied), DhcpConfigError,
+                         "subnet configuration failed: hostname-char-set "
+                         "'^[A-' is not a valid regular expression");
+
+    }
+    {
+        SCOPED_TRACE("valid regular expression");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("hostname-char-set", data::Element::create("^[A-Z]"));
+        copied->set("hostname-char-replacement", data::Element::create("x"));
+        Subnet4Ptr subnet;
+        Subnet4ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        EXPECT_EQ("^[A-Z]", subnet->getHostnameCharSet().get());
+        EXPECT_EQ("x", subnet->getHostnameCharReplacement().get());
+    }
+}
+
 } // end of anonymous namespace
index f2af0c58e716db34db8cb5c0d629d49b56a895e7..b4ba887c0f93e33f9c3425a36445a5f84bae824b 100644 (file)
@@ -15,6 +15,7 @@
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_selector.h>
+#include <testutils/gtest_utils.h>
 #include <testutils/test_to_element.h>
 #include <util/doubles.h>
 
@@ -632,6 +633,14 @@ TEST(CfgSubnets6Test, unparseSubnet) {
     subnet3->setT2Percent(0.65);
     subnet3->setValid(Triplet<uint32_t>(100, 200, 300));
     subnet3->setPreferred(Triplet<uint32_t>(50, 100, 150));
+    subnet3->setDdnsSendUpdates(true);
+    subnet3->setDdnsOverrideNoUpdate(true);
+    subnet3->setDdnsOverrideClientUpdate(true);
+    subnet3->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
+    subnet3->setDdnsGeneratedPrefix("prefix");
+    subnet3->setDdnsQualifyingSuffix("example.com.");
+    subnet3->setHostnameCharSet("[^A-Z]");
+    subnet3->setHostnameCharReplacement("x");
 
     data::ElementPtr ctx1 = data::Element::fromJSON("{ \"comment\": \"foo\" }");
     subnet1->setContext(ctx1);
@@ -694,8 +703,17 @@ TEST(CfgSubnets6Test, unparseSubnet) {
         "    \"require-client-classes\": [ \"foo\", \"bar\" ],\n"
         "    \"calculate-tee-times\": true,\n"
         "    \"t1-percent\": 0.50,\n"
-        "    \"t2-percent\": 0.65\n"
+        "    \"t2-percent\": 0.65,\n"
+        "    \"ddns-generated-prefix\": \"prefix\",\n"
+        "    \"ddns-override-client-update\": true,\n"
+        "    \"ddns-override-no-update\": true,\n"
+        "    \"ddns-qualifying-suffix\": \"example.com.\",\n"
+        "    \"ddns-replace-client-name\": \"always\",\n"
+        "    \"ddns-send-updates\": true,\n"
+        "    \"hostname-char-replacement\": \"x\",\n"
+        "    \"hostname-char-set\": \"[^A-Z]\"\n"
         "} ]\n";
+
     runToElementTest<CfgSubnets6>(expected, cfg);
 }
 
@@ -1329,4 +1347,54 @@ TEST(CfgSubnets6Test, preferredLifetimeValidation) {
     }
 }
 
+// This test verifies the Subnet6 parser's validation logic for
+// hostname sanitizer values.
+TEST(CfgSubnets6Test, hostnameSanitizierValidation) {
+
+    // First we create a set of elements that provides all
+    // required for a Subnet6.
+    std::string json =
+        "        {"
+        "            \"id\": 1,\n"
+        "            \"subnet\": \"2001:db8:1::/64\", \n"
+        "            \"interface\": \"\", \n"
+        "            \"renew-timer\": 100, \n"
+        "            \"rebind-timer\": 200, \n"
+        "            \"valid-lifetime\": 300, \n"
+        "            \"client-class\": \"\", \n"
+        "            \"require-client-classes\": [] \n,"
+        "            \"reservation-mode\": \"all\" \n"
+        "        }";
+
+    data::ElementPtr elems;
+    ASSERT_NO_THROW(elems = data::Element::fromJSON(json))
+                    << "invalid JSON:" << json << "\n test is broken";
+
+    {
+        SCOPED_TRACE("invalid regular expression");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("hostname-char-set", data::Element::create("^[A-"));
+        copied->set("hostname-char-replacement", data::Element::create("x"));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        EXPECT_THROW_MSG(subnet = parser.parse(copied), DhcpConfigError,
+                         "subnet configuration failed: hostname-char-set "
+                         "'^[A-' is not a valid regular expression");
+
+    }
+    {
+        SCOPED_TRACE("valid regular expression");
+
+        data::ElementPtr copied = data::copy(elems);
+        copied->set("hostname-char-set", data::Element::create("^[A-Z]"));
+        copied->set("hostname-char-replacement", data::Element::create("x"));
+        Subnet6Ptr subnet;
+        Subnet6ConfigParser parser;
+        ASSERT_NO_THROW(subnet = parser.parse(copied));
+        EXPECT_EQ("^[A-Z]", subnet->getHostnameCharSet().get());
+        EXPECT_EQ("x", subnet->getHostnameCharReplacement().get());
+    }
+}
+
 } // end of anonymous namespace
index b126d6c7bd1453929072021103bf11bef666f2be..97c5633d1f8e8b79511ff6cf7b9cd0248b5ec99c 100644 (file)
@@ -1000,10 +1000,13 @@ TEST(D2ClientMgr, sanitizeFqdnV4) {
     ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
     ddns_params.generated_prefix_ = "prefix";
     ddns_params.qualifying_suffix_ = "suffix.com";
+    ddns_params.hostname_char_set_ = "[^A-Za-z0-9-]";
+    ddns_params.hostname_char_replacement_ = "x";
 
-    // Create and assign the sanitizer.
-    ASSERT_NO_THROW(ddns_params.hostname_sanitizer_.reset(new
-                    isc::util::str::StringSanitizer("[^A-Za-z0-9-]", "x")));
+    // Get the sanitizer.
+    str::StringSanitizerPtr hostname_sanitizer;
+    ASSERT_NO_THROW(hostname_sanitizer = ddns_params.getHostnameSanitizer());
+    ASSERT_TRUE(hostname_sanitizer);
 
     struct Scenario {
         std::string description_;
@@ -1081,10 +1084,13 @@ TEST(D2ClientMgr, sanitizeFqdnV6) {
     ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
     ddns_params.generated_prefix_ = "prefix";
     ddns_params.qualifying_suffix_ = "suffix.com";
+    ddns_params.hostname_char_set_ = "[^A-Za-z0-9-]";
+    ddns_params.hostname_char_replacement_ = "x";
 
-    // Create and assign the sanitizer.
-    ASSERT_NO_THROW(ddns_params.hostname_sanitizer_.reset(new
-                    isc::util::str::StringSanitizer("[^A-Za-z0-9-]", "x")));
+    // Get the sanitizer.
+    str::StringSanitizerPtr hostname_sanitizer;
+    ASSERT_NO_THROW(hostname_sanitizer = ddns_params.getHostnameSanitizer());
+    ASSERT_TRUE(hostname_sanitizer);
 
     struct Scenario {
         std::string description_;
index 6f31c24a13e2d4b00ff5eea9cf7240ffe0854500..c94938041a8fee6f8004e6800c4fd2cbf636342d 100644 (file)
@@ -2781,6 +2781,12 @@ TEST_F(ParseConfigTest, defaultSubnet4) {
 
     EXPECT_TRUE(subnet->getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(subnet->getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(subnet->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(subnet->getHostnameCharSet().empty());
+
+    EXPECT_TRUE(subnet->getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(subnet->getHostnameCharReplacement().empty());
 }
 
 // This test verifies that it is possible to parse an IPv6 subnet for which
@@ -2852,6 +2858,12 @@ TEST_F(ParseConfigTest, defaultSubnet6) {
 
     EXPECT_TRUE(subnet->getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(subnet->getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(subnet->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(subnet->getHostnameCharSet().empty());
+
+    EXPECT_TRUE(subnet->getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(subnet->getHostnameCharReplacement().empty());
 }
 
 // This test verifies that it is possible to parse an IPv4 shared network
index c5d140954e577d76ae5d7e69b3b90b6585ea0595..a1be7077c5e33968e679532e4498b68d90285065 100644 (file)
@@ -179,6 +179,8 @@ TEST_F(NetworkTest, inheritanceSupport4) {
     globals_->set("ddns-replace-client-name", Element::create("always"));
     globals_->set("ddns-generated-prefix", Element::create("gp"));
     globals_->set("ddns-qualifying-suffix", Element::create("gs"));
+    globals_->set("hostname-char-set", Element::create("gc"));
+    globals_->set("hostname-char-replacement", Element::create("gr"));
 
     // For each parameter for which inheritance is supported run
     // the test that checks if the values are inherited properly.
@@ -303,6 +305,18 @@ TEST_F(NetworkTest, inheritanceSupport4) {
                                              &Network4::setDdnsQualifyingSuffix,
                                              "ns", "gs");
     }
+    {
+        SCOPED_TRACE("hostname-char-set");
+        testNetworkInheritance<TestNetwork4>(&Network4::getHostnameCharSet,
+                                             &Network4::setHostnameCharSet,
+                                             "nc", "gc");
+    }
+    {
+        SCOPED_TRACE("hostname-char-replacement");
+        testNetworkInheritance<TestNetwork4>(&Network4::getHostnameCharReplacement,
+                                             &Network4::setHostnameCharReplacement,
+                                             "nr", "gr");
+    }
 }
 
 // This test verifies that the inheritance is supported for DHCPv6
@@ -317,6 +331,8 @@ TEST_F(NetworkTest, inheritanceSupport6) {
     globals_->set("ddns-replace-client-name", Element::create("always"));
     globals_->set("ddns-generated-prefix", Element::create("gp"));
     globals_->set("ddns-qualifying-suffix", Element::create("gs"));
+    globals_->set("hostname-char-set", Element::create("gc"));
+    globals_->set("hostname-char-replacement", Element::create("gr"));
 
     // For each parameter for which inheritance is supported run
     // the test that checks if the values are inherited properly.
@@ -371,6 +387,18 @@ TEST_F(NetworkTest, inheritanceSupport6) {
                                              &Network4::setDdnsQualifyingSuffix,
                                              "ns", "gs");
     }
+    {
+        SCOPED_TRACE("hostname-char-set");
+        testNetworkInheritance<TestNetwork6>(&Network6::getHostnameCharSet,
+                                             &Network6::setHostnameCharSet,
+                                             "nc", "gc");
+    }
+    {
+        SCOPED_TRACE("hostname-char-replacement");
+        testNetworkInheritance<TestNetwork6>(&Network6::getHostnameCharReplacement,
+                                             &Network6::setHostnameCharReplacement,
+                                             "nr", "gr");
+    }
 
     // Interface-id requires special type of test.
     boost::shared_ptr<TestNetwork6> net_child(new TestNetwork6());
index 2dac1ffa76d203147e2f0ac18ff42c024cbd1bb1..0ce596933205a944dd24a7eda7909f3e88a6011b 100644 (file)
@@ -141,6 +141,8 @@ public:
                 "    \"ddns-replace-client-name\": \"always\","
                 "    \"ddns-generated-prefix\": \"prefix\","
                 "    \"ddns-qualifying-suffix\": \"example.com.\","
+                "    \"hostname-char-set\": \"[^A-Z]\","
+                "    \"hostname-char-replacement\": \"x\","
                 "    \"option-data\": ["
                 "        {"
                 "            \"name\": \"domain-name-servers\","
@@ -171,7 +173,8 @@ public:
                 "            \"reservation-mode\": \"all\","
                 "            \"calculate-tee-times\": true,"
                 "            \"t1-percent\": .45,"
-                "            \"t2-percent\": .65"
+                "            \"t2-percent\": .65,"
+                "            \"hostname-char-set\": \"\""
                 "        },"
                 "        {"
                 "            \"id\": 2,"
@@ -258,6 +261,8 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     EXPECT_EQ(D2ClientConfig::RCM_ALWAYS, network->getDdnsReplaceClientNameMode().get());
     EXPECT_EQ("prefix", network->getDdnsGeneratedPrefix().get());
     EXPECT_EQ("example.com.", network->getDdnsQualifyingSuffix().get());
+    EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
+    EXPECT_EQ("x", network->getHostnameCharReplacement().get());
 
     // Relay information.
     auto relay_info = network->getRelayInfo();
@@ -275,20 +280,24 @@ TEST_F(SharedNetwork4ParserTest, parse) {
     EXPECT_TRUE(context->get("comment"));
 
     // Subnet with id 1
-    Subnet4Ptr subnet1 = network->getSubnet(SubnetID(1));
-    ASSERT_TRUE(subnet1);
-    EXPECT_EQ("10.1.2.0", subnet1->get().first.toText());
-    EXPECT_EQ(300, subnet1->getValid());
-    EXPECT_EQ(200, subnet1->getValid().getMin());
-    EXPECT_EQ(400, subnet1->getValid().getMax());
+    Subnet4Ptr subnet = network->getSubnet(SubnetID(1));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("10.1.2.0", subnet->get().first.toText());
+    EXPECT_EQ(300, subnet->getValid());
+    EXPECT_EQ(200, subnet->getValid().getMin());
+    EXPECT_EQ(400, subnet->getValid().getMax());
+    EXPECT_FALSE(subnet->getHostnameCharSet().unspecified());
+    EXPECT_EQ("", subnet->getHostnameCharSet().get());
 
     // Subnet with id 2
-    Subnet4Ptr subnet2 = network->getSubnet(SubnetID(2));
-    ASSERT_TRUE(subnet2);
-    EXPECT_EQ("192.0.2.0", subnet2->get().first.toText());
-    EXPECT_EQ(30, subnet2->getValid());
-    EXPECT_EQ(30, subnet2->getValid().getMin());
-    EXPECT_EQ(30, subnet2->getValid().getMax());
+    subnet = network->getSubnet(SubnetID(2));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("192.0.2.0", subnet->get().first.toText());
+    EXPECT_EQ(30, subnet->getValid());
+    EXPECT_EQ(30, subnet->getValid().getMin());
+    EXPECT_EQ(30, subnet->getValid().getMax());
+    EXPECT_EQ("[^A-Z]", subnet->getHostnameCharSet().get());
+    EXPECT_EQ("x", subnet->getHostnameCharReplacement().get());
 
     // DHCP options
     ConstCfgOptionPtr cfg_option = network->getCfgOption();
@@ -463,6 +472,8 @@ public:
                 "    \"ddns-replace-client-name\": \"always\","
                 "    \"ddns-generated-prefix\": \"prefix\","
                 "    \"ddns-qualifying-suffix\": \"example.com.\","
+                "    \"hostname-char-set\": \"[^A-Z]\","
+                "    \"hostname-char-replacement\": \"x\","
                 "    \"option-data\": ["
                 "        {"
                 "            \"name\": \"dns-servers\","
@@ -486,7 +497,8 @@ public:
                 "            \"client-class\": \"\","
                 "            \"require-client-classes\": []\n,"
                 "            \"reservation-mode\": \"all\","
-                "            \"rapid-commit\": false"
+                "            \"rapid-commit\": false,"
+                "            \"hostname-char-set\": \"\""
                 "        },"
                 "        {"
                 "            \"id\": 2,"
@@ -561,6 +573,8 @@ TEST_F(SharedNetwork6ParserTest, parse) {
     EXPECT_EQ(D2ClientConfig::RCM_ALWAYS, network->getDdnsReplaceClientNameMode().get());
     EXPECT_EQ("prefix", network->getDdnsGeneratedPrefix().get());
     EXPECT_EQ("example.com.", network->getDdnsQualifyingSuffix().get());
+    EXPECT_EQ("[^A-Z]", network->getHostnameCharSet().get());
+    EXPECT_EQ("x", network->getHostnameCharReplacement().get());
 
     // Relay information.
     auto relay_info = network->getRelayInfo();
@@ -578,26 +592,30 @@ TEST_F(SharedNetwork6ParserTest, parse) {
     EXPECT_EQ(0, context->size());
 
     // Subnet with id 1
-    Subnet6Ptr subnet1 = network->getSubnet(SubnetID(1));
-    ASSERT_TRUE(subnet1);
-    EXPECT_EQ("3000::", subnet1->get().first.toText());
-    EXPECT_EQ(300, subnet1->getPreferred());
-    EXPECT_EQ(200, subnet1->getPreferred().getMin());
-    EXPECT_EQ(400, subnet1->getPreferred().getMax());
-    EXPECT_EQ(400, subnet1->getValid());
-    EXPECT_EQ(300, subnet1->getValid().getMin());
-    EXPECT_EQ(500, subnet1->getValid().getMax());
+    Subnet6Ptr subnet = network->getSubnet(SubnetID(1));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("3000::", subnet->get().first.toText());
+    EXPECT_EQ(300, subnet->getPreferred());
+    EXPECT_EQ(200, subnet->getPreferred().getMin());
+    EXPECT_EQ(400, subnet->getPreferred().getMax());
+    EXPECT_EQ(400, subnet->getValid());
+    EXPECT_EQ(300, subnet->getValid().getMin());
+    EXPECT_EQ(500, subnet->getValid().getMax());
+    EXPECT_FALSE(subnet->getHostnameCharSet().unspecified());
+    EXPECT_EQ("", subnet->getHostnameCharSet().get());
 
     // Subnet with id 2
-    Subnet6Ptr subnet2 = network->getSubnet(SubnetID(2));
-    ASSERT_TRUE(subnet2);
-    EXPECT_EQ("2001:db8:1::", subnet2->get().first.toText());
-    EXPECT_EQ(30, subnet2->getPreferred());
-    EXPECT_EQ(30, subnet2->getPreferred().getMin());
-    EXPECT_EQ(30, subnet2->getPreferred().getMax());
-    EXPECT_EQ(40, subnet2->getValid());
-    EXPECT_EQ(40, subnet2->getValid().getMin());
-    EXPECT_EQ(40, subnet2->getValid().getMax());
+    subnet = network->getSubnet(SubnetID(2));
+    ASSERT_TRUE(subnet);
+    EXPECT_EQ("2001:db8:1::", subnet->get().first.toText());
+    EXPECT_EQ(30, subnet->getPreferred());
+    EXPECT_EQ(30, subnet->getPreferred().getMin());
+    EXPECT_EQ(30, subnet->getPreferred().getMax());
+    EXPECT_EQ(40, subnet->getValid());
+    EXPECT_EQ(40, subnet->getValid().getMin());
+    EXPECT_EQ(40, subnet->getValid().getMax());
+    EXPECT_EQ("[^A-Z]", subnet->getHostnameCharSet().get());
+    EXPECT_EQ("x", subnet->getHostnameCharReplacement().get());
 
     // DHCP options
     ConstCfgOptionPtr cfg_option = network->getCfgOption();
index 2d66f182374eb2bbef430912b086d7fab041f039..4d055d1fd5362949f1419a4dff9b308f86eff1e3 100644 (file)
@@ -84,6 +84,12 @@ TEST(SharedNetwork4Test, defaults) {
 
     EXPECT_TRUE(network->getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(network->getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(network->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(network->getHostnameCharSet().empty());
+
+    EXPECT_TRUE(network->getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(network->getHostnameCharReplacement().empty());
 }
 
 // This test verifies that shared network can be given a name and that
@@ -715,6 +721,12 @@ TEST(SharedNetwork6Test, defaults) {
 
     EXPECT_TRUE(network->getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(network->getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(network->getHostnameCharSet().unspecified());
+    EXPECT_TRUE(network->getHostnameCharSet().empty());
+
+    EXPECT_TRUE(network->getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(network->getHostnameCharReplacement().empty());
 }
 
 // This test verifies that shared network can be given a name and that
index fd3fcf84d58b258e4f39c4072fc39cb46cb86879..13e382d2c280b5fbe429bd6551e95b8d5bf6fbc7 100644 (file)
@@ -1120,6 +1120,9 @@ TEST_F(SrvConfigTest, getDdnsParamsTest4) {
 
     // Disable sending updates globally.
     conf.addConfiguredGlobal("ddns-send-updates", Element::create(false));
+    // Configure global host sanitizing.
+    conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
+    conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
 
     // Add a plain subnet
     Triplet<uint32_t> def_triplet;
@@ -1156,6 +1159,7 @@ TEST_F(SrvConfigTest, getDdnsParamsTest4) {
     subnet2->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
     subnet2->setDdnsGeneratedPrefix("prefix");
     subnet2->setDdnsQualifyingSuffix("example.com.");
+    subnet2->setHostnameCharSet("");
 
     // Get DDNS params for subnet1.
     ASSERT_NO_THROW(params = conf_.getDdnsParams(*subnet1));
@@ -1167,11 +1171,19 @@ TEST_F(SrvConfigTest, getDdnsParamsTest4) {
     EXPECT_EQ(D2ClientConfig::RCM_NEVER, params->replace_client_name_mode_);
     EXPECT_TRUE(params->generated_prefix_.empty());
     EXPECT_TRUE(params->qualifying_suffix_.empty());
+    EXPECT_EQ("[^A-Z]", params->hostname_char_set_);
+    EXPECT_EQ("x", params->hostname_char_replacement_);
+
+    // We inherited a non-blank hostname_char_set so we
+    // should get a sanitizer instance.
+    isc::util::str::StringSanitizerPtr sanitizer;
+    ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
+    EXPECT_TRUE(sanitizer);
 
     // Get DDNS params for subnet2.
     ASSERT_NO_THROW(params = conf_.getDdnsParams(*subnet2));
 
-    // Verify subnet1 values are right. Note, updates should be disabled,
+    // Verify subnet2 values are right. Note, updates should be disabled,
     // because D2Client is disabled.
     EXPECT_FALSE(params->enable_updates_);
     EXPECT_TRUE(params->override_no_update_);
@@ -1179,6 +1191,12 @@ TEST_F(SrvConfigTest, getDdnsParamsTest4) {
     EXPECT_EQ(D2ClientConfig::RCM_ALWAYS, params->replace_client_name_mode_);
     EXPECT_EQ("prefix", params->generated_prefix_);
     EXPECT_EQ("example.com.", params->qualifying_suffix_);
+    EXPECT_EQ("", params->hostname_char_set_);
+    EXPECT_EQ("x", params->hostname_char_replacement_);
+
+    // We have a blank hostname-char-set so we should not get a sanitizer instance.
+    ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
+    ASSERT_FALSE(sanitizer);
 
     // Enable D2Client.
     enableD2Client(true);
@@ -1211,6 +1229,9 @@ TEST_F(SrvConfigTest, getDdnsParamsTest6) {
 
     // Disable sending updates globally.
     conf.addConfiguredGlobal("ddns-send-updates", Element::create(false));
+    // Configure global host sanitizing.
+    conf.addConfiguredGlobal("hostname-char-set", Element::create("[^A-Z]"));
+    conf.addConfiguredGlobal("hostname-char-replacement", Element::create("x"));
 
     // Add a plain subnet
     Triplet<uint32_t> def_triplet;
@@ -1247,6 +1268,7 @@ TEST_F(SrvConfigTest, getDdnsParamsTest6) {
     subnet2->setDdnsReplaceClientNameMode(D2ClientConfig::RCM_ALWAYS);
     subnet2->setDdnsGeneratedPrefix("prefix");
     subnet2->setDdnsQualifyingSuffix("example.com.");
+    subnet2->setHostnameCharSet("");
 
     // Get DDNS params for subnet1.
     ASSERT_NO_THROW(params = conf_.getDdnsParams(*subnet1));
@@ -1258,6 +1280,14 @@ TEST_F(SrvConfigTest, getDdnsParamsTest6) {
     EXPECT_EQ(D2ClientConfig::RCM_NEVER, params->replace_client_name_mode_);
     EXPECT_TRUE(params->generated_prefix_.empty());
     EXPECT_TRUE(params->qualifying_suffix_.empty());
+    EXPECT_EQ("[^A-Z]", params->hostname_char_set_);
+    EXPECT_EQ("x", params->hostname_char_replacement_);
+
+    // We inherited a non-blank hostname_char_set so we
+    // should get a sanitizer instance.
+    isc::util::str::StringSanitizerPtr sanitizer;
+    ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
+    EXPECT_TRUE(sanitizer);
 
     // Get DDNS params for subnet2.
     ASSERT_NO_THROW(params = conf_.getDdnsParams(*subnet2));
@@ -1270,6 +1300,12 @@ TEST_F(SrvConfigTest, getDdnsParamsTest6) {
     EXPECT_EQ(D2ClientConfig::RCM_ALWAYS, params->replace_client_name_mode_);
     EXPECT_EQ("prefix", params->generated_prefix_);
     EXPECT_EQ("example.com.", params->qualifying_suffix_);
+    EXPECT_EQ("", params->hostname_char_set_);
+    EXPECT_EQ("x", params->hostname_char_replacement_);
+
+    // We have a blank hostname-char-set so we should not get a sanitizer instance.
+    ASSERT_NO_THROW(sanitizer = params->getHostnameSanitizer());
+    ASSERT_FALSE(sanitizer);
 
     // Enable D2Client.
     enableD2Client(true);
index 5bde914cdaa3253091ab1ed8092ed4b40857559e..160c2a4caa86395a82dde7fb1d07bd2ef4534ebf 100644 (file)
@@ -133,6 +133,12 @@ TEST(Subnet4Test, defaults) {
 
     EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(subnet.getHostnameCharSet().unspecified());
+    EXPECT_TRUE(subnet.getHostnameCharSet().empty());
+
+    EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
 }
 
 // Checks that the subnet id can be either autogenerated or set to an
@@ -855,6 +861,12 @@ TEST(SharedNetwork6Test, defaults) {
 
     EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().unspecified());
     EXPECT_TRUE(subnet.getDdnsQualifyingSuffix().empty());
+
+    EXPECT_TRUE(subnet.getHostnameCharSet().unspecified());
+    EXPECT_TRUE(subnet.getHostnameCharSet().empty());
+
+    EXPECT_TRUE(subnet.getHostnameCharReplacement().unspecified());
+    EXPECT_TRUE(subnet.getHostnameCharReplacement().empty());
 }
 
 // Checks that the subnet id can be either autogenerated or set to an