1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-22 06:50:18 +03:00

dns-domain: accept encoded domain names without terminating zero label

Commit 1be9b30a3b17 ("dhcp6: use dns_name_from_wire_format") introduced a
stricter validation of domains received via DHCPv6, by using function
dns_name_from_wire_format() which rejects the domain when it is missing the
terminating zero label. According to RFC 4704 § 4.2, DHCPv6 servers should
always add the zero label:

   To send a fully qualified domain name, the Domain Name field is set
   to the DNS-encoded domain name including the terminating zero-length
   label.  To send a partial name, the Domain Name field is set to the
   DNS-encoded domain name without the terminating zero-length label.

   [...]

   Servers SHOULD send the complete fully qualified domain name in
   Client FQDN options.

In practice, there is at least on common DHCPv6 server implementation (dnsmasq)
that sends the FQDN option without the ending zero-length label; after
upgrading to the new systemd, the client cannot parse the option and therefore
the machine doesn't get the hostname provided by dnsmasq.

This commit restores the old behavior that considers a domain valid even when
it's missing the terminating zero label.

Here's a quick reproducer:

--8<--

ip link add veth0 type veth peer name veth1
ip netns add ns1
ip link set veth1 netns ns1
ip link set veth0 address 00:11:22:33:44:55
ip link set veth0 up
ip -n ns1 link set veth1 up
ip -n ns1 address add dev veth1 fd01::1/64

ip netns exec ns1 dnsmasq \
   --pid-file=/tmp/dnsmasq.pid --no-hosts \
   --bind-interfaces --interface veth1 --except-interface lo \
   --dhcp-range=fd01::100,fd01::200 --enable-ra \
   --dhcp-host 00:11:22:33:44:55,foobar &

cat <<EOF > /etc/systemd/network/veth0.network
[Match]
Name=veth0

[Network]
DHCP=ipv6
EOF

networkctl reload
networkctl up veth0
sleep 5
hostname

--8<--

Without this change, systemd-networkd prints the following message and doesn't
set the hostname from DHCP:

  veth0: DHCPv6 client: Failed to parse FQDN option, ignoring: Bad message
This commit is contained in:
Beniamino Galvani 2025-01-29 11:51:18 +01:00 committed by Luca Boccassi
parent 515ab90e4d
commit 30675a6ee9
3 changed files with 9 additions and 4 deletions

View File

@ -165,7 +165,9 @@ TEST(parse_domain) {
domain = mfree(domain);
data = (uint8_t []) { 4, 't', 'e', 's', 't' };
assert_se(dhcp6_option_parse_domainname(data, 5, &domain) < 0);
ASSERT_OK(dhcp6_option_parse_domainname(data, 5, &domain));
ASSERT_STREQ(domain, "test");
domain = mfree(domain);
data = (uint8_t []) { 0 };
assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0);

View File

@ -924,9 +924,12 @@ int dns_name_from_wire_format(const uint8_t **data, size_t *len, char **ret) {
const char *label;
uint8_t c;
/* Unterminated name */
/* RFC 4704 § 4: fully qualified domain names include the terminating
* zero-length label, partial names don't. According to the RFC, DHCPv6
* servers should always send the fully qualified name, but that's not
* true in practice. Also accept partial names. */
if (optlen == 0)
return -EBADMSG;
break;
/* RFC 1035 § 3.1 total length of encoded name is limited to 255 octets */
if (*len - optlen > 255)

View File

@ -197,7 +197,7 @@ TEST(dns_name_from_wire_format) {
test_dns_name_from_wire_format_one("", in0, sizeof(in0), strlen(""));
test_dns_name_from_wire_format_one("foo", in1, sizeof(in1), strlen("foo"));
test_dns_name_from_wire_format_one(NULL, in1, sizeof(in1) - 1, -EBADMSG);
test_dns_name_from_wire_format_one("foo", in1, sizeof(in1) - 1, strlen("foo"));
test_dns_name_from_wire_format_one("hallo.foo.bar", in2, sizeof(in2), strlen("hallo.foo.bar"));
test_dns_name_from_wire_format_one("hallo.foo", in2_1, sizeof(in2_1), strlen("hallo.foo"));