From 92b65b2345e07d826b56ffd65cf47538f1c7a271 Mon Sep 17 00:00:00 2001 From: Thomas Markwalder Date: Tue, 7 Oct 2025 14:41:16 -0400 Subject: [PATCH] [#4155] Backport #4142 to v3_0 Invalid characters cause assert To trigger the issue, three configuration parameters must have specific settings: "hostname-char-set" must be left at the default setting, which is "[^A-Za-z0-9.-]"; "hostname-char-replacement" must be empty (the default); and "ddns-qualifying-suffix" must NOT be empty (the default is empty). DDNS updates do not need to be enabled for this issue to manifest. A client that sends certain option content would then cause kea-dhcp4 to exit unexpectedly. CVE: CVE-2025-11232 Upstream-Status: Backport [https://github.com/isc-projects/kea/commit/92b65b2345e07d826b56ffd65cf47538f1c7a271] Signed-off-by: Ross Burton new file: changelog_unreleased/CVE-2025-11232-catch-empty-sanitized-hostname modified: src/bin/dhcp4/dhcp4_messages.cc modified: src/bin/dhcp4/dhcp4_messages.h modified: src/bin/dhcp4/dhcp4_messages.mes modified: src/bin/dhcp4/dhcp4_srv.cc modified: src/bin/dhcp4/tests/fqdn_unittest.cc modified: src/bin/dhcp6/dhcp6_messages.cc modified: src/bin/dhcp6/dhcp6_messages.h modified: src/bin/dhcp6/dhcp6_messages.mes modified: src/bin/dhcp6/dhcp6_srv.cc modified: src/bin/dhcp6/tests/fqdn_unittest.cc modified: src/lib/dhcpsrv/d2_client_mgr.cc modified: src/lib/dhcpsrv/d2_client_mgr.h modified: src/lib/dhcpsrv/tests/d2_client_unittest.cc --- ...-2025-11232-catch-empty-sanitized-hostname | 6 +++ src/bin/dhcp4/dhcp4_messages.cc | 4 ++ src/bin/dhcp4/dhcp4_messages.h | 2 + src/bin/dhcp4/dhcp4_messages.mes | 14 +++++ src/bin/dhcp4/dhcp4_srv.cc | 21 ++++++-- src/bin/dhcp4/tests/fqdn_unittest.cc | 54 ++++++++++++++++++- src/bin/dhcp6/dhcp6_messages.cc | 2 + src/bin/dhcp6/dhcp6_messages.h | 1 + src/bin/dhcp6/dhcp6_messages.mes | 7 +++ src/bin/dhcp6/dhcp6_srv.cc | 9 +++- src/bin/dhcp6/tests/fqdn_unittest.cc | 23 ++++++++ src/lib/dhcpsrv/d2_client_mgr.cc | 9 +++- src/lib/dhcpsrv/d2_client_mgr.h | 19 ++++++- src/lib/dhcpsrv/tests/d2_client_unittest.cc | 42 +++++++++++++++ 14 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 changelog_unreleased/CVE-2025-11232-catch-empty-sanitized-hostname diff --git a/src/bin/dhcp4/dhcp4_messages.cc b/src/bin/dhcp4/dhcp4_messages.cc index e06ce6a121..5c6a334bad 100644 --- a/src/bin/dhcp4/dhcp4_messages.cc +++ b/src/bin/dhcp4/dhcp4_messages.cc @@ -26,9 +26,11 @@ extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED = "DHCP4_CLASS_UNCONFI extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES = "DHCP4_CLIENTID_IGNORED_FOR_LEASES"; extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA = "DHCP4_CLIENT_FQDN_DATA"; extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS = "DHCP4_CLIENT_FQDN_PROCESS"; +extern const isc::log::MessageID DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY = "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY"; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_DATA = "DHCP4_CLIENT_HOSTNAME_DATA"; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_MALFORMED = "DHCP4_CLIENT_HOSTNAME_MALFORMED"; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_PROCESS = "DHCP4_CLIENT_HOSTNAME_PROCESS"; +extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY = "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY"; extern const isc::log::MessageID DHCP4_CLIENT_NAME_PROC_FAIL = "DHCP4_CLIENT_NAME_PROC_FAIL"; extern const isc::log::MessageID DHCP4_CONFIG_COMPLETE = "DHCP4_CONFIG_COMPLETE"; extern const isc::log::MessageID DHCP4_CONFIG_LOAD_FAIL = "DHCP4_CONFIG_LOAD_FAIL"; @@ -206,9 +208,11 @@ const char* values[] = { "DHCP4_CLIENTID_IGNORED_FOR_LEASES", "%1: not using client identifier for lease allocation for subnet %2", "DHCP4_CLIENT_FQDN_DATA", "%1: Client sent FQDN option: %2", "DHCP4_CLIENT_FQDN_PROCESS", "%1: processing Client FQDN option", + "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY", "%1: sanitizing client's FQDN option '%2' yielded an empty string", "DHCP4_CLIENT_HOSTNAME_DATA", "%1: client sent Hostname option: %2", "DHCP4_CLIENT_HOSTNAME_MALFORMED", "%1: client hostname option malformed: %2", "DHCP4_CLIENT_HOSTNAME_PROCESS", "%1: processing client's Hostname option", + "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY", "%1: sanitizing client's Hostname option '%2' yielded an empty string", "DHCP4_CLIENT_NAME_PROC_FAIL", "%1: failed to process the fqdn or hostname sent by a client: %2", "DHCP4_CONFIG_COMPLETE", "DHCPv4 server has completed configuration: %1", "DHCP4_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2", diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index 9a4d0cda21..6e45c63053 100644 --- a/src/bin/dhcp4/dhcp4_messages.h +++ b/src/bin/dhcp4/dhcp4_messages.h @@ -27,9 +27,11 @@ extern const isc::log::MessageID DHCP4_CLASS_UNCONFIGURED; extern const isc::log::MessageID DHCP4_CLIENTID_IGNORED_FOR_LEASES; extern const isc::log::MessageID DHCP4_CLIENT_FQDN_DATA; extern const isc::log::MessageID DHCP4_CLIENT_FQDN_PROCESS; +extern const isc::log::MessageID DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_DATA; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_MALFORMED; extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_PROCESS; +extern const isc::log::MessageID DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY; extern const isc::log::MessageID DHCP4_CLIENT_NAME_PROC_FAIL; extern const isc::log::MessageID DHCP4_CONFIG_COMPLETE; extern const isc::log::MessageID DHCP4_CONFIG_LOAD_FAIL; diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes index 1deb2e6074..b359d09616 100644 --- a/src/bin/dhcp4/dhcp4_messages.mes +++ b/src/bin/dhcp4/dhcp4_messages.mes @@ -164,6 +164,20 @@ This debug message is issued when the server starts processing the Hostname option sent in the client's query. The argument includes the client and transaction identification information. +% DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY %1: sanitizing client's Hostname option '%2' yielded an empty string +Logged at debug log level 50. +This debug message is issued when the result of sanitizing the +hostname option(12) sent by the client is an empty string. When this occurs +the server will ignore the hostname option. The arguments include the +client and the hostname option it sent. + +% DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY %1: sanitizing client's FQDN option '%2' yielded an empty string +Logged at debug log level 50. +This debug message is issued when the result of sanitizing the +FQDN option(81) sent by the client is an empty string. When this occurs +the server will ignore the FQDN option. The arguments include the +client and the FQDN option it sent. + % DHCP4_CLIENT_NAME_PROC_FAIL %1: failed to process the fqdn or hostname sent by a client: %2 Logged at debug log level 55. This debug message is issued when the DHCP server was unable to process the diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index 0701ed41e9..a6be662889 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -2714,8 +2714,15 @@ Dhcpv4Srv::processClientFqdnOption(Dhcpv4Exchange& ex) { } else { // Adjust the domain name based on domain name value and type sent by the // client and current configuration. - d2_mgr.adjustDomainName(*fqdn, *fqdn_resp, - *(ex.getContext()->getDdnsParams())); + try { + d2_mgr.adjustDomainName(*fqdn, *fqdn_resp, + *(ex.getContext()->getDdnsParams())); + } catch (const FQDNScrubbedEmpty& scrubbed) { + LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY) + .arg(ex.getQuery()->getLabel()) + .arg(scrubbed.what()); + return; + } } // Add FQDN option to the response message. Note that, there may be some @@ -2857,7 +2864,15 @@ Dhcpv4Srv::processHostnameOption(Dhcpv4Exchange& ex) { ex.getContext()->getDdnsParams()->getHostnameSanitizer(); if (sanitizer) { - hostname = sanitizer->scrub(hostname); + auto tmp = sanitizer->scrub(hostname); + if (tmp.empty()) { + LOG_DEBUG(ddns4_logger, DBG_DHCP4_DETAIL, DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY) + .arg(ex.getQuery()->getLabel()) + .arg(hostname); + return; + } + + hostname = tmp; } // Convert hostname to lower case. diff --git a/src/bin/dhcp4/tests/fqdn_unittest.cc b/src/bin/dhcp4/tests/fqdn_unittest.cc index a5d3e4c21a..18e4c6d4b9 100644 --- a/src/bin/dhcp4/tests/fqdn_unittest.cc +++ b/src/bin/dhcp4/tests/fqdn_unittest.cc @@ -2253,7 +2253,7 @@ TEST_F(NameDhcpv4SrvTest, sanitizeHostDefault) { }, { "qualified host name with nuls", - std::string("four-ok-host\000.other.org",23), + std::string("four-ok-host\000.other.org", 23), "four-ok-host.other.org" } }; @@ -3203,4 +3203,56 @@ TEST_F(NameDhcpv4SrvTest, poolDdnsParametersTest) { } } +// Verifies that when the FQDN option is scrubbed empty it is logged +// and ignored. +TEST_F(NameDhcpv4SrvTest, hostnameScrubbedEmpty) { + Dhcp4Client client(srv_, Dhcp4Client::SELECTING); + + // Configure DHCP server. + configure(CONFIGS[2], *client.getServer()); + + // Set the hostname option. + ASSERT_NO_THROW(client.includeHostname("___")); + + // Send the DHCPDISCOVER and make sure that the server responded. + ASSERT_NO_THROW(client.doDiscover()); + auto resp = client.getContext().response_; + ASSERT_TRUE(resp); + ASSERT_EQ(DHCPOFFER, static_cast(resp->getType())); + + // Should have logged that it was scrubbed empty. + std::string log = "DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY"; + EXPECT_EQ(1, countFile(log)); + + // Hostname should not be in the response. + ASSERT_FALSE(resp->getOption(DHO_HOST_NAME)); +} + +// Verifies that when the FQDN option is scrubbed empty it is logged +// and ignored. +TEST_F(NameDhcpv4SrvTest, fqdnScrubbedEmpty) { + Dhcp4Client client(srv_, Dhcp4Client::SELECTING); + + // Configure DHCP server. + configure(CONFIGS[2], *client.getServer()); + + // Include the Client FQDN option. + ASSERT_NO_THROW(client.includeFQDN(Option4ClientFqdn::FLAG_S | Option4ClientFqdn::FLAG_E, + "___", Option4ClientFqdn::PARTIAL)); + + // Send the DHCPDISCOVER and make sure that the server responded. + ASSERT_NO_THROW(client.doDiscover()); + auto resp = client.getContext().response_; + ASSERT_TRUE(resp); + ASSERT_EQ(DHCPOFFER, static_cast(resp->getType())); + + // Should have logged that it was scrubbed empty. + std::string log = "DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY"; + EXPECT_EQ(1, countFile(log)); + + // Hostname should not be in the response. + ASSERT_FALSE(resp->getOption(DHO_FQDN)); +} + + } // end of anonymous namespace diff --git a/src/bin/dhcp6/dhcp6_messages.cc b/src/bin/dhcp6/dhcp6_messages.cc index 229ba74450..9619481aba 100644 --- a/src/bin/dhcp6/dhcp6_messages.cc +++ b/src/bin/dhcp6/dhcp6_messages.cc @@ -27,6 +27,7 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED = "DHCP6_CLASSES_ASSIGNE extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION = "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION"; extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED = "DHCP6_CLASS_ASSIGNED"; extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED = "DHCP6_CLASS_UNCONFIGURED"; +extern const isc::log::MessageID DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY = "DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY"; extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE = "DHCP6_CONFIG_COMPLETE"; extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL = "DHCP6_CONFIG_LOAD_FAIL"; extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE = "DHCP6_CONFIG_PACKET_QUEUE"; @@ -203,6 +204,7 @@ const char* values[] = { "DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION", "%1: client packet has been assigned to the following classes: %2", "DHCP6_CLASS_ASSIGNED", "%1: client packet has been assigned to the following class: %2", "DHCP6_CLASS_UNCONFIGURED", "%1: client packet belongs to an unconfigured class: %2", + "DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY", "%1: sanitizing client's FQDN option '%2' yielded an empty string", "DHCP6_CONFIG_COMPLETE", "DHCPv6 server has completed configuration: %1", "DHCP6_CONFIG_LOAD_FAIL", "configuration error using file: %1, reason: %2", "DHCP6_CONFIG_PACKET_QUEUE", "DHCPv6 packet queue info after configuration: %1", diff --git a/src/bin/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h index 186f7d557a..7af56e716a 100644 --- a/src/bin/dhcp6/dhcp6_messages.h +++ b/src/bin/dhcp6/dhcp6_messages.h @@ -28,6 +28,7 @@ extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED; extern const isc::log::MessageID DHCP6_CLASSES_ASSIGNED_AFTER_SUBNET_SELECTION; extern const isc::log::MessageID DHCP6_CLASS_ASSIGNED; extern const isc::log::MessageID DHCP6_CLASS_UNCONFIGURED; +extern const isc::log::MessageID DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY; extern const isc::log::MessageID DHCP6_CONFIG_COMPLETE; extern const isc::log::MessageID DHCP6_CONFIG_LOAD_FAIL; extern const isc::log::MessageID DHCP6_CONFIG_PACKET_QUEUE; diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes index fff50ed367..79fc984ff5 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -1167,3 +1167,10 @@ such modification. The clients will remember previous server-id, and will use it to extend their leases. As a result, they will have to go through a rebinding phase to re-acquire their leases and associate them with a new server id. + +% DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY %1: sanitizing client's FQDN option '%2' yielded an empty string +Logged at debug log level 50. +This debug message is issued when the result of sanitizing the +FQDN option(39) sent by the client is an empty string. When this occurs +the server will ignore the FQDN option. The arguments include the +client and the FQDN option it sent. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 417960b126..f999c3178f 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -2332,7 +2332,14 @@ Dhcpv6Srv::processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer, } else { // Adjust the domain name based on domain name value and type sent by // the client and current configuration. - d2_mgr.adjustDomainName(*fqdn, *fqdn_resp, *ddns_params); + try { + d2_mgr.adjustDomainName(*fqdn, *fqdn_resp, *ddns_params); + } catch(const FQDNScrubbedEmpty& scrubbed) { + LOG_DEBUG(ddns6_logger, DBG_DHCP6_DETAIL, DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY) + .arg(question->getLabel()) + .arg(scrubbed.what()); + return; + } } // Once we have the FQDN setup to use it for the lease hostname. This diff --git a/src/bin/dhcp6/tests/fqdn_unittest.cc b/src/bin/dhcp6/tests/fqdn_unittest.cc index ca51856e67..7891c1f5e6 100644 --- a/src/bin/dhcp6/tests/fqdn_unittest.cc +++ b/src/bin/dhcp6/tests/fqdn_unittest.cc @@ -2425,4 +2425,27 @@ TEST_F(FqdnDhcpv6SrvTest, poolDdnsParametersTest) { } } +// Verify an FQDN with all invalid chars is ignored. +TEST_F(FqdnDhcpv6SrvTest, fqdnScrubbedEmpty) { + // Create the query. + Pkt6Ptr question = generateMessage(DHCPV6_SOLICIT, Option6ClientFqdn::FLAG_S, + "___" , Option6ClientFqdn::FULL, true); + ASSERT_TRUE(getClientFqdnOption(question)); + subnet_->setHostnameCharReplacement(""); + + // Create the response with an "assigned" lease. + // Set the selected subnet so ddns params get returned correctly. + AllocEngine::ClientContext6 ctx; + ctx.subnet_ = subnet_; + Pkt6Ptr answer = generateMessageWithIds(DHCPV6_ADVERTISE); + addIA(1234, IOAddress("2001:db8:1::1"), answer, ctx); + + // Process the client's FQDN. + ASSERT_NO_THROW(srv_->processClientFqdn(question, answer, ctx)); + + // Should not have an FQDN option in the answer. + EXPECT_FALSE(answer->getOption(D6O_CLIENT_FQDN)); + countFile("DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY"); +} + } // end of anonymous namespace diff --git a/src/lib/dhcpsrv/d2_client_mgr.cc b/src/lib/dhcpsrv/d2_client_mgr.cc index 84ee11d9fb..54c815176e 100644 --- a/src/lib/dhcpsrv/d2_client_mgr.cc +++ b/src/lib/dhcpsrv/d2_client_mgr.cc @@ -186,10 +186,15 @@ std::string D2ClientMgr::qualifyName(const std::string& partial_name, const DdnsParams& ddns_params, const bool trailing_dot) const { + if (partial_name.empty()) { + isc_throw(BadValue, "D2ClientMgr::qualifyName" + " - partial_name cannot be an empty string"); + } + std::ostringstream gen_name; gen_name << partial_name; std::string suffix = ddns_params.getQualifyingSuffix(); - if (!suffix.empty() && partial_name.back() != '.') { + if (!suffix.empty() && (partial_name.back() != '.')) { bool suffix_present = true; std::string str = gen_name.str(); auto suffix_rit = suffix.rbegin(); @@ -241,7 +246,7 @@ D2ClientMgr::qualifyName(const std::string& partial_name, // If the trailing dot should not be appended but it is present, // remove it. if ((len > 0) && (str[len - 1] == '.')) { - gen_name.str(str.substr(0,len-1)); + gen_name.str(str.substr(0, len-1)); } } diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h index 7344f19a40..238fd0a415 100644 --- a/src/lib/dhcpsrv/d2_client_mgr.h +++ b/src/lib/dhcpsrv/d2_client_mgr.h @@ -30,6 +30,14 @@ namespace isc { namespace dhcp { +/// @brief Exception thrown when host name sanitizing reduces +/// the domain name to an empty string. +class FQDNScrubbedEmpty : public Exception { +public: + FQDNScrubbedEmpty(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) { } +}; + /// @brief Defines the type for D2 IO error handler. /// This callback is invoked when a send to kea-dhcp-ddns completes with a /// failed status. This provides the application layer (Kea) with a means to @@ -197,6 +205,7 @@ class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler, /// suffix itself is empty (i.e. ""). /// /// @return std::string containing the qualified name. + /// @throw BadValue if partial_name is empty. std::string qualifyName(const std::string& partial_name, const DdnsParams& ddns_params, const bool trailing_dot) const; @@ -264,6 +273,9 @@ class D2ClientMgr : public dhcp_ddns::NameChangeSender::RequestSendHandler, /// @param ddns_params DDNS behavioral configuration parameters /// @tparam T FQDN Option class containing the FQDN data such as /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn + /// + /// @throw FQDNScrubbedEmpty if hostname sanitizing reduces the input domain + /// name to an empty string. template void adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddns_params); @@ -515,7 +527,12 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddn ss << sanitizer->scrub(label); } - client_name = ss.str(); + std::string clean_name = ss.str(); + if (clean_name.empty() || clean_name == ".") { + isc_throw(FQDNScrubbedEmpty, client_name); + } + + client_name = clean_name; } // If the supplied name is partial, qualify it by adding the suffix. diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc index 68ad2189d6..00375d0066 100644 --- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -627,6 +628,10 @@ TEST_F(D2ClientMgrParamsTest, qualifyName) { qualified_name = mgr.qualifyName(partial_name, *ddns_params_, do_not_dot); EXPECT_EQ("somehost.suffix.com", qualified_name); + // Verify that an empty name throws. + partial_name = ""; + ASSERT_THROW(mgr.qualifyName(partial_name, *ddns_params_, do_not_dot), BadValue); + // Verify that an empty suffix and false flag, does not change the name subnet_->setDdnsQualifyingSuffix(""); partial_name = "somehost"; @@ -1257,4 +1262,41 @@ TEST_F(D2ClientMgrParamsTest, sanitizeFqdnV6) { } } +/// @brief Tests adjustDomainName template method with Option4ClientFqdn +/// when sanitizing scrubs input name empty. +TEST_F(D2ClientMgrParamsTest, adjustDomainNameV4ScrubbedEmpty) { + D2ClientMgr mgr; + + // Create enabled configuration + subnet_->setDdnsSendUpdates(false); + subnet_->setDdnsQualifyingSuffix("suffix.com"); + subnet_->setHostnameCharSet("[^A-Za-z0-9.-]"); + subnet_->setHostnameCharReplacement(""); + + Option4ClientFqdn request(0, Option4ClientFqdn::RCODE_CLIENT(), + "___", Option4ClientFqdn::FULL); + + Option4ClientFqdn response(request); + ASSERT_THROW_MSG(mgr.adjustDomainName(request, response, *ddns_params_), + FQDNScrubbedEmpty, "___."); +} + +/// @brief Tests adjustDomainName template method with Option4ClientFqdn +/// when sanitizing scrubs input name empty. +TEST_F(D2ClientMgrParamsTest, adjustDomainNameV6ScrubbedEmpty) { + D2ClientMgr mgr; + + // Create enabled configuration + subnet_->setDdnsSendUpdates(false); + subnet_->setDdnsQualifyingSuffix("suffix.com"); + subnet_->setHostnameCharSet("[^A-Za-z0-9.-]"); + subnet_->setHostnameCharReplacement(""); + + Option6ClientFqdn request(0, "___", Option6ClientFqdn::FULL); + + Option6ClientFqdn response(request); + ASSERT_THROW_MSG(mgr.adjustDomainName(request, response, *ddns_params_), + FQDNScrubbedEmpty, "___."); +} + } // end of anonymous namespace