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();
/// @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();
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_;
/// @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.
// 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();
ss << ".";
}
- ss << ddns_params.hostname_sanitizer_->scrub(*label);
+ ss << sanitizer->scrub(*label);
}
client_name = ss.str();
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);
}
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.
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>
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>
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>
// 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
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>
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>
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.
///
/// @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
#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;
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"));
}
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
/// The elements currently supported are (see isc::dhcp::D2ClientConfig
/// for details on each):
/// -# enable-updates
- /// -# qualifying-suffix
/// -# server-ip
/// -# server-port
/// -# sender-ip
/// -# 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);
{ "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.
{ "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 },
};
"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.
{ "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 },
};
{ "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.
{ "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 }
};
"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.
{ "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 }
};
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;
}
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));
" \"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";
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));
" \"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";
#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>
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);
" \"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);
}
}
}
+// 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
#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>
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);
" \"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);
}
}
}
+// 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
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_;
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_;
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
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
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.
&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
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.
&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());
" \"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\","
" \"reservation-mode\": \"all\","
" \"calculate-tee-times\": true,"
" \"t1-percent\": .45,"
- " \"t2-percent\": .65"
+ " \"t2-percent\": .65,"
+ " \"hostname-char-set\": \"\""
" },"
" {"
" \"id\": 2,"
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();
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();
" \"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\","
" \"client-class\": \"\","
" \"require-client-classes\": []\n,"
" \"reservation-mode\": \"all\","
- " \"rapid-commit\": false"
+ " \"rapid-commit\": false,"
+ " \"hostname-char-set\": \"\""
" },"
" {"
" \"id\": 2,"
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();
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();
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
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
// 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;
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));
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_);
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);
// 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;
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));
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));
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);
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
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