diff options
Diffstat (limited to 'src/libsystemd-network/sd-dhcp-lease.c')
-rw-r--r-- | src/libsystemd-network/sd-dhcp-lease.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 37f4b3b2c9..1c0cd6829b 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) @@ -1140,6 +1290,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { fputc('\n', f); } + sd_dns_resolver *resolvers; + r = sd_dhcp_lease_get_dnr(lease, &resolvers); + if (r > 0) { + fputs("DNR=", f); + serialize_dnr(f, resolvers, r, NULL); + fputc('\n', f); + } + r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { fputs("NTP=", f); @@ -1248,6 +1406,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *next_server = NULL, *broadcast = NULL, *dns = NULL, + *dnr = NULL, *ntp = NULL, *sip = NULL, *pop3 = NULL, @@ -1285,6 +1444,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "NEXT_SERVER", &next_server, "BROADCAST", &broadcast, "DNS", &dns, + "DNR", &dnr, "NTP", &ntp, "SIP", &sip, "POP3", &pop3, @@ -1387,6 +1547,13 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { lease->servers[SD_DHCP_LEASE_DNS].size = r; } + if (dnr) { + r = deserialize_dnr(&lease->dnr, dnr); + if (r < 0) + log_debug_errno(r, "Failed to deserialize DNR servers %s, ignoring: %m", dnr); + lease->n_dnr = r; + } + if (ntp) { r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp); if (r < 0) |