From: Thomas Markwalder Date: Thu, 2 Oct 2025 18:10:19 +0000 (-0400) Subject: [#4142] Initial fix minus UTs X-Git-Tag: Kea-3.1.3~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2d5bfbee23e3acbee7068e96d17b82cb78f1320e;p=thirdparty%2Fkea.git [#4142] Initial fix minus UTs /src/bin/dhcp4/dhcp4_messages.* DHCP4_CLIENT_HOSTNAME_SCRUBBED_EMPTY DHCP4_CLIENT_FQDN_SCRUBBED_EMPTY - new messages /src/bin/dhcp4/dhcp4_srv.cc Dhcpv4Srv::processClientFqdnOption() - catch FQDNScrubbedEmtpy, log and drop FQDN option Dhcpv4Srv::processHostnameOption() - log and drop hostname option if scrubbed empty /src/bin/dhcp6/dhcp6_messages.* DHCP6_CLIENT_FQDN_SCRUBBED_EMPTY - new message /src/bin/dhcp6/dhcp6_srv.cc Dhcpv6Srv::processClientFqdn() - catch FQDNScrubbedEmtpy, log and drop FQDN option /src/lib/dhcpsrv/d2_client_mgr.h FQDNScrubbedEmpty - new exception type D2ClientMgr:: adjustDomainName() - throw FQDNScrubbedEmpty when FQDN is scrubbed empty --- diff --git a/src/bin/dhcp4/dhcp4_messages.h b/src/bin/dhcp4/dhcp4_messages.h index 97a9ac3a3b..18472293e3 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 d6279d4be8..fd9e4d17f9 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: sanitiziing 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: sanitiziing 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 ca8ce1164a..137316a6d9 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -2717,8 +2717,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 @@ -2860,7 +2867,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/dhcp6/dhcp6_messages.h b/src/bin/dhcp6/dhcp6_messages.h index cae45ff606..e251d4d917 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 e411ecc6fe..cc239d0668 100644 --- a/src/bin/dhcp6/dhcp6_messages.mes +++ b/src/bin/dhcp6/dhcp6_messages.mes @@ -1173,3 +1173,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: sanitiziing 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. diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index c7cb73f360..ce67a47c45 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -2338,7 +2338,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/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h index 7344f19a40..b160b47fe1 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 upon attempt to add subnet with an ID that belongs +/// to the subnet that already exists. +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 @@ -264,6 +272,9 @@ public: /// @param ddns_params DDNS behavioral configuration parameters /// @tparam T FQDN Option class containing the FQDN data such as /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn + /// + /// @throw FQDNScrubbedEmtpy 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 +526,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.