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