#include <unistd.h>
#include "sd-dhcp-lease.h"
+#include "dns-resolver-internal.h"
#include "alloc-util.h"
#include "dhcp-lease-internal.h"
#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"
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);
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);
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;
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)