From: Thomas Markwalder Date: Fri, 27 Sep 2019 19:52:28 +0000 (-0400) Subject: [#35,!517] Added hostname-char-set to hostname-char-replacement network/subnet scopes X-Git-Tag: tmark-post-35-refactor~16 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=590767d4d6e21c942487bfebed4486e55d3063fc;p=thirdparty%2Fkea.git [#35,!517] Added hostname-char-set to hostname-char-replacement network/subnet scopes 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 --- diff --git a/src/lib/dhcpsrv/d2_client_cfg.cc b/src/lib/dhcpsrv/d2_client_cfg.cc index 83b1d29264..e6ca69fb87 100644 --- a/src/lib/dhcpsrv/d2_client_cfg.cc +++ b/src/lib/dhcpsrv/d2_client_cfg.cc @@ -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(); diff --git a/src/lib/dhcpsrv/d2_client_cfg.h b/src/lib/dhcpsrv/d2_client_cfg.h index 0bb80a83dd..4b67409df4 100644 --- a/src/lib/dhcpsrv/d2_client_cfg.h +++ b/src/lib/dhcpsrv/d2_client_cfg.h @@ -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. diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h index cc655b4c12..63bb64234f 100644 --- a/src/lib/dhcpsrv/d2_client_mgr.h +++ b/src/lib/dhcpsrv/d2_client_mgr.h @@ -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(); diff --git a/src/lib/dhcpsrv/network.cc b/src/lib/dhcpsrv/network.cc index 41a8feca4f..ed361588d7 100644 --- a/src/lib/dhcpsrv/network.cc +++ b/src/lib/dhcpsrv/network.cc @@ -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); } diff --git a/src/lib/dhcpsrv/network.h b/src/lib/dhcpsrv/network.h index 928f5ea067..36715e6a11 100644 --- a/src/lib/dhcpsrv/network.h +++ b/src/lib/dhcpsrv/network.h @@ -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& 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 @@ -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& 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 @@ -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& 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 @@ -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& mode - = getProperty(&Network::getDdnsReplaceClientNameMode, + = getProperty(&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& + void setDdnsReplaceClientNameMode(const util::Optional& 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 @@ -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 @@ -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& ddns_qualifying_suffix) { ddns_qualifying_suffix_ = ddns_qualifying_suffix; } + /// @brief Return the char set regexp used to sanitize client hostnames. + util::Optional + getHostnameCharSet(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&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& hostname_char_set) { + hostname_char_set_ = hostname_char_set; + } + + /// @brief Return the invalid char replacement used to sanitize client hostnames. + util::Optional + getHostnameCharReplacement(const Inheritance& inheritance = Inheritance::ALL) const { + return (getProperty(&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& 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 ddns_qualifying_suffix_; + /// @brief Regular expression describing invalid characters for client + /// hostnames. + util::Optional hostname_char_set_; + + /// @brief A string to replace invalid characters when scrubbing hostnames. + /// Meaningful only if hostname_char_set_ is not empty. + util::Optional 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 diff --git a/src/lib/dhcpsrv/parsers/base_network_parser.cc b/src/lib/dhcpsrv/parsers/base_network_parser.cc index c67ee172fe..ef96b3df6a 100644 --- a/src/lib/dhcpsrv/parsers/base_network_parser.cc +++ b/src/lib/dhcpsrv/parsers/base_network_parser.cc @@ -8,6 +8,7 @@ #include #include #include +#include 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 - (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 diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index 8333c4b749..045d619a22 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -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); diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc index a2a0e74557..f0ed3d2ddf 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser4.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc @@ -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 }, }; diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc index 0454560e6b..fcc61f5adc 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser6.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc @@ -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 } }; diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc index ef12fda612..0c681496d1 100644 --- a/src/lib/dhcpsrv/srv_config.cc +++ b/src/lib/dhcpsrv/srv_config.cc @@ -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; } diff --git a/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc index 3114d0bea7..c2f870c0e1 100644 --- a/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_shared_networks4_unittest.cc @@ -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(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"; diff --git a/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc index e6643657fc..b2f71ebe80 100644 --- a/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_shared_networks6_unittest.cc @@ -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(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"; diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index ce7e4cd0f8..19fe8052fa 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1034,6 +1035,14 @@ TEST(CfgSubnets4Test, unparseSubnet) { subnet3->setSname("frog"); subnet3->setFilename("/dev/null"); subnet3->setValid(Triplet(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(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 diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index f2af0c58e7..b4ba887c0f 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -632,6 +633,14 @@ TEST(CfgSubnets6Test, unparseSubnet) { subnet3->setT2Percent(0.65); subnet3->setValid(Triplet(100, 200, 300)); subnet3->setPreferred(Triplet(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(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 diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc index b126d6c7bd..97c5633d1f 100644 --- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -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_; diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index 6f31c24a13..c94938041a 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc @@ -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 diff --git a/src/lib/dhcpsrv/tests/network_unittest.cc b/src/lib/dhcpsrv/tests/network_unittest.cc index c5d140954e..a1be7077c5 100644 --- a/src/lib/dhcpsrv/tests/network_unittest.cc +++ b/src/lib/dhcpsrv/tests/network_unittest.cc @@ -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(&Network4::getHostnameCharSet, + &Network4::setHostnameCharSet, + "nc", "gc"); + } + { + SCOPED_TRACE("hostname-char-replacement"); + testNetworkInheritance(&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(&Network6::getHostnameCharSet, + &Network6::setHostnameCharSet, + "nc", "gc"); + } + { + SCOPED_TRACE("hostname-char-replacement"); + testNetworkInheritance(&Network6::getHostnameCharReplacement, + &Network6::setHostnameCharReplacement, + "nr", "gr"); + } // Interface-id requires special type of test. boost::shared_ptr net_child(new TestNetwork6()); diff --git a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc index 2dac1ffa76..0ce5969332 100644 --- a/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_parser_unittest.cc @@ -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(); diff --git a/src/lib/dhcpsrv/tests/shared_network_unittest.cc b/src/lib/dhcpsrv/tests/shared_network_unittest.cc index 2d66f18237..4d055d1fd5 100644 --- a/src/lib/dhcpsrv/tests/shared_network_unittest.cc +++ b/src/lib/dhcpsrv/tests/shared_network_unittest.cc @@ -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 diff --git a/src/lib/dhcpsrv/tests/srv_config_unittest.cc b/src/lib/dhcpsrv/tests/srv_config_unittest.cc index fd3fcf84d5..13e382d2c2 100644 --- a/src/lib/dhcpsrv/tests/srv_config_unittest.cc +++ b/src/lib/dhcpsrv/tests/srv_config_unittest.cc @@ -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 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 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); diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index 5bde914cda..160c2a4caa 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -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