summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h3
-rw-r--r--src/libsystemd-network/dhcp-option.h1
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c150
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)