From: Ronan Pigott Date: Sat, 24 Feb 2024 00:12:46 +0000 (-0700) Subject: network: Introduce sd_dns_resolver X-Git-Tag: v257-rc1~171^2~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1e2ead52e1796e65a13d73cd8fc792390bd4fad4;p=thirdparty%2Fsystemd.git network: Introduce sd_dns_resolver This type will be used to represent a "designated resolver", and the necessary info for communicating with it. Beyond and address endpoint, we may need to know the dns transport, authenticated domain name, DoH path, etc. --- diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index c32a1a9a671..1ab09f26d6e 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -4,6 +4,8 @@ # include #endif +#include "dns-resolver-internal.h" + #include "alloc-util.h" #include "dns-domain.h" #include "escape.h" @@ -2955,27 +2957,6 @@ const char* format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]) { return snprintf_ok(buf, DECIMAL_STR_MAX(int), "%i", i); } -static const char* const dns_svc_param_key_table[_DNS_SVC_PARAM_KEY_MAX_DEFINED] = { - [DNS_SVC_PARAM_KEY_MANDATORY] = "mandatory", - [DNS_SVC_PARAM_KEY_ALPN] = "alpn", - [DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN] = "no-default-alpn", - [DNS_SVC_PARAM_KEY_PORT] = "port", - [DNS_SVC_PARAM_KEY_IPV4HINT] = "ipv4hint", - [DNS_SVC_PARAM_KEY_ECH] = "ech", - [DNS_SVC_PARAM_KEY_IPV6HINT] = "ipv6hint", - [DNS_SVC_PARAM_KEY_DOHPATH] = "dohpath", - [DNS_SVC_PARAM_KEY_OHTTP] = "ohttp", -}; -DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_svc_param_key, int); - -const char* format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]) { - const char *p = dns_svc_param_key_to_string(i); - if (p) - return p; - - return snprintf_ok(buf, DECIMAL_STR_MAX(uint16_t)+3, "key%i", i); -} - static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = { [DNS_PROTOCOL_DNS] = "dns", [DNS_PROTOCOL_MDNS] = "mdns", diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 13d65a02c32..ddee259b1f9 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -362,25 +362,6 @@ const char* format_dns_ede_rcode(int i, char buf[static DECIMAL_STR_MAX(int)]); const char* dns_protocol_to_string(DnsProtocol p) _const_; DnsProtocol dns_protocol_from_string(const char *s) _pure_; -/* https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml#dns-svcparamkeys */ -enum { - DNS_SVC_PARAM_KEY_MANDATORY = 0, /* RFC 9460 section 8 */ - DNS_SVC_PARAM_KEY_ALPN = 1, /* RFC 9460 section 7.1 */ - DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN = 2, /* RFC 9460 Section 7.1 */ - DNS_SVC_PARAM_KEY_PORT = 3, /* RFC 9460 section 7.2 */ - DNS_SVC_PARAM_KEY_IPV4HINT = 4, /* RFC 9460 section 7.3 */ - DNS_SVC_PARAM_KEY_ECH = 5, /* RFC 9460 */ - DNS_SVC_PARAM_KEY_IPV6HINT = 6, /* RFC 9460 section 7.3 */ - DNS_SVC_PARAM_KEY_DOHPATH = 7, /* RFC 9461 */ - DNS_SVC_PARAM_KEY_OHTTP = 8, - _DNS_SVC_PARAM_KEY_MAX_DEFINED, - DNS_SVC_PARAM_KEY_INVALID = 65535 /* RFC 9460 */ -}; - -const char* dns_svc_param_key_to_string(int i) _const_; -const char* format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]); -#define FORMAT_DNS_SVC_PARAM_KEY(i) format_dns_svc_param_key(i, (char [DECIMAL_STR_MAX(uint16_t)+3]) {}) - #define LLMNR_MULTICAST_IPV4_ADDRESS ((struct in_addr) { .s_addr = htobe32(224U << 24 | 252U) }) #define LLMNR_MULTICAST_IPV6_ADDRESS ((struct in6_addr) { .s6_addr = { 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03 } }) diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 6a4bd6aecdd..ed525883f20 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -2,6 +2,8 @@ #include +#include "dns-resolver-internal.h" + #include "alloc-util.h" #include "dns-domain.h" #include "dns-type.h" diff --git a/src/shared/dns-resolver-internal.h b/src/shared/dns-resolver-internal.h new file mode 100644 index 00000000000..08ef782408a --- /dev/null +++ b/src/shared/dns-resolver-internal.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "sd-dns-resolver.h" + +#include "macro.h" +#include "list.h" +#include "socket-netlink.h" + +/* https://www.iana.org/assignments/dns-svcb/dns-svcb.xhtml#dns-svcparamkeys */ +enum { + DNS_SVC_PARAM_KEY_MANDATORY = 0, /* RFC 9460 § 8 */ + DNS_SVC_PARAM_KEY_ALPN = 1, /* RFC 9460 § 7.1 */ + DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN = 2, /* RFC 9460 § 7.1 */ + DNS_SVC_PARAM_KEY_PORT = 3, /* RFC 9460 § 7.2 */ + DNS_SVC_PARAM_KEY_IPV4HINT = 4, /* RFC 9460 § 7.3 */ + DNS_SVC_PARAM_KEY_ECH = 5, /* RFC 9460 */ + DNS_SVC_PARAM_KEY_IPV6HINT = 6, /* RFC 9460 § 7.3 */ + DNS_SVC_PARAM_KEY_DOHPATH = 7, /* RFC 9461 */ + DNS_SVC_PARAM_KEY_OHTTP = 8, + _DNS_SVC_PARAM_KEY_MAX_DEFINED, + DNS_SVC_PARAM_KEY_INVALID = 65535 /* RFC 9460 */ +}; + +const char* dns_svc_param_key_to_string(int i) _const_; +const char* format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]); +#define FORMAT_DNS_SVC_PARAM_KEY(i) format_dns_svc_param_key(i, (char [DECIMAL_STR_MAX(uint16_t)+3]) {}) + +/* Represents a "designated resolver" */ +/* typedef struct sd_dns_resolver sd_dns_resolver; */ +struct sd_dns_resolver { + uint16_t priority; + char *auth_name; + int family; + union in_addr_union *addrs; + size_t n_addrs; + sd_dns_alpn_flags transports; + uint16_t port; + char *dohpath; +}; + +void siphash24_compress_resolver(const sd_dns_resolver *res, struct siphash *state); + +int dns_resolvers_to_dot_addrs(const sd_dns_resolver *resolvers, size_t n_resolvers, + struct in_addr_full ***ret_addrs, size_t *ret_n_addrs); + +int dns_resolver_prio_compare(const sd_dns_resolver *a, const sd_dns_resolver *b); + +int dnr_parse_svc_params(const uint8_t *option, size_t len, sd_dns_resolver *resolver); + +int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolvers, char ***ret_names); + +void sd_dns_resolver_done(sd_dns_resolver *res); + +void dns_resolver_done_many(sd_dns_resolver *resolvers, size_t n); diff --git a/src/shared/meson.build b/src/shared/meson.build index 8fc39429e5e..548b26d8c39 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -154,6 +154,7 @@ shared_sources = files( 'resize-fs.c', 'resolve-util.c', 'rm-rf.c', + 'sd-dns-resolver.c', 'securebits-util.c', 'selinux-util.c', 'serialize.c', diff --git a/src/shared/sd-dns-resolver.c b/src/shared/sd-dns-resolver.c new file mode 100644 index 00000000000..4c76df18d1f --- /dev/null +++ b/src/shared/sd-dns-resolver.c @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dns-resolver-internal.h" +#include "macro.h" +#include "unaligned.h" +#include "socket-netlink.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" + +void sd_dns_resolver_done(sd_dns_resolver *res) { + assert(res); + + res->auth_name = mfree(res->auth_name); + res->addrs = mfree(res->addrs); + res->dohpath = mfree(res->dohpath); +} + +sd_dns_resolver *sd_dns_resolver_unref(sd_dns_resolver *res) { + if (!res) + return NULL; + + sd_dns_resolver_done(res); + return mfree(res); +} + +void dns_resolver_done_many(sd_dns_resolver resolvers[], size_t n) { + assert(resolvers || n == 0); + + FOREACH_ARRAY(res, resolvers, n) + sd_dns_resolver_done(res); + + free(resolvers); +} + +int dns_resolver_prio_compare(const sd_dns_resolver *a, const sd_dns_resolver *b) { + return CMP(ASSERT_PTR(a)->priority, ASSERT_PTR(b)->priority); +} + +int sd_dns_resolver_get_priority(sd_dns_resolver *res, uint16_t *ret_priority) { + assert_return(res, -EINVAL); + assert_return(ret_priority, -EINVAL); + + *ret_priority = res->priority; + return 0; +} + +int sd_dns_resolver_get_adn(sd_dns_resolver *res, const char **ret_adn) { + assert_return(res, -EINVAL); + assert_return(ret_adn, -EINVAL); + + /* Without adn only Do53 can be supported */ + if (!res->auth_name) + return -ENODATA; + + *ret_adn = res->auth_name; + return 0; +} + +int sd_dns_resolver_get_inet_addresses(sd_dns_resolver *res, const struct in_addr **ret_addrs, size_t + *ret_n_addrs) { + assert_return(res, -EINVAL); + assert_return(ret_addrs, -EINVAL); + assert_return(ret_n_addrs, -EINVAL); + assert_return(res->family == AF_INET, -EINVAL); + + /* ADN-only mode has no addrs */ + if (res->n_addrs == 0) + return -ENODATA; + + struct in_addr *addrs = new(struct in_addr, res->n_addrs); + if (!addrs) + return -ENOMEM; + + for (size_t i = 0; i < res->n_addrs; i++) + addrs[i] = res->addrs[i].in; + *ret_addrs = addrs; + *ret_n_addrs = res->n_addrs; + + return 0; +} + +int sd_dns_resolver_get_inet6_addresses(sd_dns_resolver *res, const struct in6_addr **ret_addrs, size_t + *ret_n_addrs) { + assert_return(res, -EINVAL); + assert_return(ret_addrs, -EINVAL); + assert_return(ret_n_addrs, -EINVAL); + assert_return(res->family == AF_INET6, -EINVAL); + + /* ADN-only mode has no addrs */ + if (res->n_addrs == 0) + return -ENODATA; + + struct in6_addr *addrs = new(struct in6_addr, res->n_addrs); + if (!addrs) + return -ENOMEM; + + for (size_t i = 0; i < res->n_addrs; i++) + addrs[i] = res->addrs[i].in6; + *ret_addrs = addrs; + *ret_n_addrs = res->n_addrs; + + return 0; +} + +int sd_dns_resolver_get_alpn(sd_dns_resolver *res, sd_dns_alpn_flags *ret_alpn) { + assert_return(res, -EINVAL); + assert_return(ret_alpn, -EINVAL); + + /* ADN-only mode has no transports */ + if (!res->transports) + return -ENODATA; + + *ret_alpn = res->transports; + return 0; +} + +int sd_dns_resolver_get_port(sd_dns_resolver *res, uint16_t *ret_port) { + assert_return(res, -EINVAL); + assert_return(ret_port, -EINVAL); + + /* port = 0 is the default port */ + *ret_port = res->port; + return 0; +} + +int sd_dns_resolver_get_dohpath(sd_dns_resolver *res, const char **ret_dohpath) { + assert_return(res, -EINVAL); + assert_return(ret_dohpath, -EINVAL); + + /* only present in DoH resolvers */ + if (!res->dohpath) + return -ENODATA; + + *ret_dohpath = res->dohpath; + return 0; +} + +void siphash24_compress_resolver(const sd_dns_resolver *res, struct siphash *state) { + assert(res); + + siphash24_compress_typesafe(res->priority, state); + siphash24_compress_typesafe(res->transports, state); + siphash24_compress_typesafe(res->port, state); + + siphash24_compress_string(res->auth_name, state); + siphash24_compress_string(res->dohpath, state); + + siphash24_compress_typesafe(res->family, state); + FOREACH_ARRAY(addr, res->addrs, res->n_addrs) + siphash24_compress_typesafe(*addr, state); +} + +static const char* const dns_svc_param_key_table[_DNS_SVC_PARAM_KEY_MAX_DEFINED] = { + [DNS_SVC_PARAM_KEY_MANDATORY] = "mandatory", + [DNS_SVC_PARAM_KEY_ALPN] = "alpn", + [DNS_SVC_PARAM_KEY_NO_DEFAULT_ALPN] = "no-default-alpn", + [DNS_SVC_PARAM_KEY_PORT] = "port", + [DNS_SVC_PARAM_KEY_IPV4HINT] = "ipv4hint", + [DNS_SVC_PARAM_KEY_ECH] = "ech", + [DNS_SVC_PARAM_KEY_IPV6HINT] = "ipv6hint", + [DNS_SVC_PARAM_KEY_DOHPATH] = "dohpath", + [DNS_SVC_PARAM_KEY_OHTTP] = "ohttp", +}; +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dns_svc_param_key, int); + +const char* format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX(uint16_t)+3]) { + assert(buf); + + const char *p = dns_svc_param_key_to_string(i); + if (p) + return p; + + return snprintf_ok(buf, DECIMAL_STR_MAX(uint16_t)+3, "key%i", i); +} + +int dnr_parse_svc_params(const uint8_t *option, size_t len, sd_dns_resolver *resolver) { + size_t offset = 0; + int r; + + assert(option || len == 0); + assert(resolver); + + sd_dns_alpn_flags transports = 0; + uint16_t port = 0; + _cleanup_free_ char *dohpath = NULL; + bool alpn = false; + + uint16_t lastkey = 0; + while (offset < len) { + if (offset + 4 > len) + return -EBADMSG; + + uint16_t key = unaligned_read_be16(&option[offset]); + offset += 2; + + /* RFC9460 § 2.2 SvcParam MUST appear in strictly increasing numeric order */ + if (lastkey >= key) + return -EBADMSG; + lastkey = key; + + uint16_t plen = unaligned_read_be16(&option[offset]); + offset += 2; + if (offset + plen > len) + return -EBADMSG; + + switch (key) { + /* Mandatory keys must be understood by the client, otherwise the record should be discarded. + * Automatic mandatory keys must not appear in the mandatory parameter, so these are all + * supplementary. We don't understand any supplementary keys, so if the mandatory parameter + * is present, we cannot use this record.*/ + case DNS_SVC_PARAM_KEY_MANDATORY: + if (plen > 0) + return -EBADMSG; + break; + + case DNS_SVC_PARAM_KEY_ALPN: + if (plen == 0) + return 0; + alpn = true; /* alpn is required. Record that the requirement is met. */ + + size_t poff = offset; + size_t pend = offset + plen; + while (poff < pend) { + uint8_t alen = option[poff++]; + if (poff + alen > len) + return -EBADMSG; + if (memcmp_nn(&option[poff], alen, "dot", STRLEN("dot")) == 0) + transports |= SD_DNS_ALPN_DOT; + if (memcmp_nn(&option[poff], alen, "h2", STRLEN("h2")) == 0) + transports |= SD_DNS_ALPN_HTTP_2_TLS; + if (memcmp_nn(&option[poff], alen, "h3", STRLEN("h3")) == 0) + transports |= SD_DNS_ALPN_HTTP_3; + if (memcmp_nn(&option[poff], alen, "doq", STRLEN("doq")) == 0) + transports |= SD_DNS_ALPN_DOQ; + poff += alen; + } + if (poff != pend) + return -EBADMSG; + break; + + case DNS_SVC_PARAM_KEY_PORT: + if (plen != sizeof(uint16_t)) + return -EBADMSG; + port = unaligned_read_be16(&option[offset]); + /* Server should indicate default port by omitting this param */ + if (port == 0) + return -EBADMSG; + break; + + /* RFC9463 § 5.1 service params MUST NOT include ipv4hint/ipv6hint */ + case DNS_SVC_PARAM_KEY_IPV4HINT: + case DNS_SVC_PARAM_KEY_IPV6HINT: + return -EBADMSG; + + case DNS_SVC_PARAM_KEY_DOHPATH: + r = make_cstring((const char*) &option[offset], plen, + MAKE_CSTRING_REFUSE_TRAILING_NUL, &dohpath); + if (ERRNO_IS_NEG_RESOURCE(r)) + return r; + if (r < 0) + return -EBADMSG; + /* dohpath is a RFC6750 URI template. We don't parse these, but at least check the + * charset is reasonable. */ + if (!in_charset(dohpath, URI_VALID "{}")) + return -EBADMSG; + break; + + default: + break; + } + offset += plen; + } + if (offset != len) + return -EBADMSG; + + /* DNR cannot be used without alpn */ + if (!alpn) + return -EBADMSG; + + /* RFC9461 § 5: If the [SvcParam] indicates support for HTTP, "dohpath" MUST be present. */ + if (!dohpath && (FLAGS_SET(transports, SD_DNS_ALPN_HTTP_2_TLS) || + FLAGS_SET(transports, SD_DNS_ALPN_HTTP_3))) + return -EBADMSG; + + /* No useful transports */ + if (!transports) + return 0; + + resolver->transports = transports; + resolver->port = port; + free_and_replace(resolver->dohpath, dohpath); + return transports; +} + +int dns_resolvers_to_dot_addrs(const sd_dns_resolver *resolvers, size_t n_resolvers, + struct in_addr_full ***ret_addrs, size_t *ret_n_addrs) { + assert(ret_addrs); + assert(ret_n_addrs); + assert(resolvers || n_resolvers == 0); + + struct in_addr_full **addrs = NULL; + size_t n = 0; + CLEANUP_ARRAY(addrs, n, in_addr_full_array_free); + + FOREACH_ARRAY(res, resolvers, n_resolvers) { + if (!FLAGS_SET(res->transports, SD_DNS_ALPN_DOT)) + continue; + + FOREACH_ARRAY(i, res->addrs, res->n_addrs) { + _cleanup_(in_addr_full_freep) struct in_addr_full *addr = NULL; + int r; + + addr = new0(struct in_addr_full, 1); + if (!addr) + return -ENOMEM; + if (!GREEDY_REALLOC(addrs, n+1)) + return -ENOMEM; + + r = free_and_strdup(&addr->server_name, res->auth_name); + if (r < 0) + return r; + addr->family = res->family; + addr->port = res->port; + addr->address = *i; + + addrs[n++] = TAKE_PTR(addr); + } + } + + *ret_addrs = TAKE_PTR(addrs); + *ret_n_addrs = n; + return n; +} + +int dns_resolvers_to_dot_strv(const sd_dns_resolver *resolvers, size_t n_resolvers, char ***ret_names) { + assert(ret_names); + int r; + + _cleanup_strv_free_ char **names = NULL; + size_t len = 0; + + struct in_addr_full **addrs = NULL; + size_t n = 0; + CLEANUP_ARRAY(addrs, n, in_addr_full_array_free); + + r = dns_resolvers_to_dot_addrs(resolvers, n_resolvers, &addrs, &n); + if (r < 0) + return r; + + FOREACH_ARRAY(addr, addrs, n) { + const char *name = in_addr_full_to_string(*addr); + if (!name) + return -ENOMEM; + r = strv_extend_with_size(&names, &len, name); + if (r < 0) + return r; + } + + *ret_names = TAKE_PTR(names); + return len; +} diff --git a/src/shared/socket-netlink.c b/src/shared/socket-netlink.c index 2580c84eadf..09477449b98 100644 --- a/src/shared/socket-netlink.c +++ b/src/shared/socket-netlink.c @@ -347,6 +347,15 @@ struct in_addr_full *in_addr_full_free(struct in_addr_full *a) { return mfree(a); } +void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n) { + assert(addrs || n == 0); + + FOREACH_ARRAY(a, addrs, n) + in_addr_full_freep(a); + + free(addrs); +} + int in_addr_full_new( int family, const union in_addr_union *a, diff --git a/src/shared/socket-netlink.h b/src/shared/socket-netlink.h index a6edb4c4bd3..12647b3d14c 100644 --- a/src/shared/socket-netlink.h +++ b/src/shared/socket-netlink.h @@ -39,6 +39,7 @@ struct in_addr_full { struct in_addr_full *in_addr_full_free(struct in_addr_full *a); DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free); +void in_addr_full_array_free(struct in_addr_full *addrs[], size_t n); int in_addr_full_new(int family, const union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret); int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret); const char* in_addr_full_to_string(struct in_addr_full *a); diff --git a/src/systemd/meson.build b/src/systemd/meson.build index 06ba4cbc075..edcbc873e9a 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -35,6 +35,7 @@ _not_installed_headers = [ 'sd-dhcp6-lease.h', 'sd-dhcp6-option.h', 'sd-dhcp6-protocol.h', + 'sd-dns-resolver.h', 'sd-ipv4acd.h', 'sd-ipv4ll.h', 'sd-lldp-rx.h', diff --git a/src/systemd/sd-dns-resolver.h b/src/systemd/sd-dns-resolver.h new file mode 100644 index 00000000000..79d00ba9bc1 --- /dev/null +++ b/src/systemd/sd-dns-resolver.h @@ -0,0 +1,44 @@ +#ifndef SD_DNS_RESOLVER_H +#define SD_DNS_RESOLVER_H + +#include +#include +#include +#include + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_dns_resolver sd_dns_resolver; + +/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */ +typedef enum sd_dns_alpn_flags { + /* There isn't really an alpn reserved for Do53 service, but designated resolvers may or may not offer + * Do53 service, so we should probably have a flag to represent this capability. Unfortunately DNR + * does not indicate the status to us.*/ + SD_DNS_ALPN_DO53 = 1 << 0, + /* SD_DNS_ALPN_HTTP_1_1, "http/1.1" [RFC9112] */ + SD_DNS_ALPN_HTTP_2_TLS = 1 << 1, /* "h2" [RFC9113] [RFC9461] */ + /* SD_DNS_ALPN_HTTP_2_TCP, "h2c" [RFC9113] */ + SD_DNS_ALPN_HTTP_3 = 1 << 2, /* "h3" [RFC9114] [RFC9461] */ + SD_DNS_ALPN_DOT = 1 << 3, /* "dot" [RFC7858] [RFC9461] */ + SD_DNS_ALPN_DOQ = 1 << 4, /* "doq" [RFC9250] [RFC9461] */ + + _SD_ENUM_FORCE_S64(SD_DNS_ALPN) +} sd_dns_alpn_flags; + +int sd_dns_resolver_get_priority(sd_dns_resolver *res, uint16_t *ret_priority); +int sd_dns_resolver_get_adn(sd_dns_resolver *res, const char **ret_adn); +int sd_dns_resolver_get_inet_addresses(sd_dns_resolver *res, const struct in_addr **ret_addrs, size_t *n); +int sd_dns_resolver_get_inet6_addresses(sd_dns_resolver *res, const struct in6_addr **ret_addrs, size_t *n); +int sd_dns_resolver_get_alpn(sd_dns_resolver *res, sd_dns_alpn_flags *ret_alpn); +int sd_dns_resolver_get_port(sd_dns_resolver *res, uint16_t *ret_port); +int sd_dns_resolver_get_dohpath(sd_dns_resolver *res, const char **ret_dohpath); + +sd_dns_resolver *sd_dns_resolver_unref(sd_dns_resolver *res); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dns_resolver, sd_dns_resolver_unref); + +_SD_END_DECLARATIONS; + +#endif /* SD_DNS_RESOLVER_H */