diff options
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r-- | src/libsystemd-network/dhcp-lease-internal.h | 3 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-option.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-lease.c | 150 |
3 files changed, 154 insertions, 0 deletions
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index b7bc142ef7..394c71bdf0 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -55,6 +55,9 @@ struct sd_dhcp_lease { DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; + sd_dns_resolver *dnr; + size_t n_dnr; + struct sd_dhcp_route *static_routes; size_t n_static_routes; struct sd_dhcp_route *classless_routes; diff --git a/src/libsystemd-network/dhcp-option.h b/src/libsystemd-network/dhcp-option.h index aaa8f847b1..047c8f3415 100644 --- a/src/libsystemd-network/dhcp-option.h +++ b/src/libsystemd-network/dhcp-option.h @@ -4,6 +4,7 @@ #include <stdint.h> #include "sd-dhcp-option.h" +#include "dns-resolver-internal.h" #include "dhcp-protocol.h" #include "hash-funcs.h" diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 37f4b3b2c9..8c46f8f770 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -11,6 +11,7 @@ #include <unistd.h> #include "sd-dhcp-lease.h" +#include "dns-resolver-internal.h" #include "alloc-util.h" #include "dhcp-lease-internal.h" @@ -26,6 +27,7 @@ #include "network-common.h" #include "network-internal.h" #include "parse-util.h" +#include "sort-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" @@ -229,6 +231,17 @@ int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **ret) { return 0; } +int sd_dhcp_lease_get_dnr(sd_dhcp_lease *lease, sd_dns_resolver **ret_resolvers) { + assert_return(lease, -EINVAL); + assert_return(ret_resolvers, -EINVAL); + + if (!lease->dnr) + return -ENODATA; + + *ret_resolvers = lease->dnr; + return lease->n_dnr; +} + int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); @@ -418,6 +431,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); + dns_resolver_done_many(lease->dnr, lease->n_dnr); free(lease->static_routes); free(lease->classless_routes); free(lease->vendor_specific); @@ -559,6 +573,133 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } +static int lease_parse_dns_name(const uint8_t *optval, size_t optlen, char **ret) { + _cleanup_free_ char *name = NULL; + int r; + + assert(optval); + assert(ret); + + r = dns_name_from_wire_format(&optval, &optlen, &name); + if (r < 0) + return r; + if (r == 0 || optlen != 0) + return -EBADMSG; + + *ret = TAKE_PTR(name); + return r; +} + +static int lease_parse_dnr(const uint8_t *option, size_t len, sd_dns_resolver **ret_dnr, size_t *ret_n_dnr) { + int r; + sd_dns_resolver *res_list = NULL; + size_t n_resolvers = 0; + CLEANUP_ARRAY(res_list, n_resolvers, dns_resolver_done_many); + + assert(option || len == 0); + assert(ret_dnr); + + _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {}; + + size_t offset = 0; + while (offset < len) { + /* Instance Data length */ + if (offset + 2 > len) + return -EBADMSG; + size_t ilen = unaligned_read_be16(option + offset); + if (offset + ilen + 2 > len) + return -EBADMSG; + offset += 2; + size_t iend = offset + ilen; + + /* priority */ + if (offset + 2 > len) + return -EBADMSG; + res.priority = unaligned_read_be16(option + offset); + offset += 2; + + /* Authenticated Domain Name */ + if (offset + 1 > len) + return -EBADMSG; + ilen = option[offset++]; + if (offset + ilen > iend) + return -EBADMSG; + + r = lease_parse_dns_name(option + offset, ilen, &res.auth_name); + if (r < 0) + return r; + if (dns_name_is_root(res.auth_name)) + return -EBADMSG; + offset += ilen; + + /* RFC9463 § 3.1.6: In ADN-only mode, server omits everything after the ADN. + * We don't support these, but they are not invalid. */ + if (offset == iend) { + log_debug("Received ADN-only DNRv4 option, ignoring."); + sd_dns_resolver_done(&res); + continue; + } + + /* IPv4 addrs */ + if (offset + 1 > len) + return -EBADMSG; + ilen = option[offset++]; + if (offset + ilen > iend) + return -EBADMSG; + + size_t n_addrs; + _cleanup_free_ struct in_addr *addrs = NULL; + r = lease_parse_in_addrs(option + offset, ilen, &addrs, &n_addrs); + if (r < 0) + return r; + offset += ilen; + + /* RFC9463 § 3.1.8: option MUST include at least one valid IP addr */ + if (!n_addrs) + return -EBADMSG; + + res.addrs = new(union in_addr_union, n_addrs); + if (!res.addrs) + return -ENOMEM; + for (size_t i = 0; i < n_addrs; i++) { + union in_addr_union addr = {.in = addrs[i]}; + /* RFC9463 § 5.2 client MUST discard multicast and host loopback addresses */ + if (in_addr_is_multicast(AF_INET, &addr) || + in_addr_is_localhost(AF_INET, &addr)) + return -EBADMSG; + res.addrs[i] = addr; + } + res.n_addrs = n_addrs; + res.family = AF_INET; + + /* service params */ + r = dnr_parse_svc_params(option + offset, iend-offset, &res); + if (r < 0) + return r; + if (r == 0) { + /* We can't use this record, but it was not invalid. */ + log_debug("Received DNRv4 option with unsupported SvcParams, ignoring."); + sd_dns_resolver_done(&res); + continue; + } + offset = iend; + + /* Append the latest resolver */ + if (!GREEDY_REALLOC0(res_list, n_resolvers+1)) + return -ENOMEM; + + res_list[n_resolvers++] = TAKE_STRUCT(res); + } + + typesafe_qsort(*ret_dnr, *ret_n_dnr, dns_resolver_prio_compare); + + dns_resolver_done_many(*ret_dnr, *ret_n_dnr); + *ret_dnr = TAKE_PTR(res_list); + *ret_n_dnr = n_resolvers; + + return n_resolvers; +} + static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { int r; @@ -879,6 +1020,15 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; } + case SD_DHCP_OPTION_V4_DNR: + r = lease_parse_dnr(option, len, &lease->dnr, &lease->n_dnr); + if (r < 0) { + log_debug_errno(r, "Failed to parse network-designated resolvers, ignoring: %m"); + return 0; + } + + break; + case SD_DHCP_OPTION_VENDOR_SPECIFIC: if (len <= 0) |