--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <errno.h>
+
+#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_resolver_transports_to_strv(sd_dns_alpn_flags transports, char ***ret);
+
+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);
'sd-dhcp-server.c',
'sd-dhcp6-client.c',
'sd-dhcp6-lease.c',
+ 'sd-dns-resolver.c',
'sd-ipv4acd.c',
'sd-ipv4ll.c',
'sd-lldp-rx.c',
--- /dev/null
+/* 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 dns_resolver_transports_to_strv(sd_dns_alpn_flags transports, char ***ret) {
+ _cleanup_strv_free_ char **ans = NULL;
+
+ assert(ret);
+
+ if (FLAGS_SET(transports, SD_DNS_ALPN_DO53)) {
+ /* Do53 has no ALPN, this flag is only for our own usage. */
+ }
+
+ if (FLAGS_SET(transports, SD_DNS_ALPN_HTTP_2_TLS))
+ if (strv_extend(&ans, "h2") < 0)
+ return -ENOMEM;
+ if (FLAGS_SET(transports, SD_DNS_ALPN_HTTP_3))
+ if (strv_extend(&ans, "h3") < 0)
+ return -ENOMEM;
+ if (FLAGS_SET(transports, SD_DNS_ALPN_DOT))
+ if (strv_extend(&ans, "dot") < 0)
+ return -ENOMEM;
+ if (FLAGS_SET(transports, SD_DNS_ALPN_DOQ))
+ if (strv_extend(&ans, "doq") < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(ans);
+ return 0;
+}
+
+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;
+}
--- /dev/null
+#ifndef SD_DNS_RESOLVER_H
+#define SD_DNS_RESOLVER_H
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#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 */
# include <gcrypt.h>
#endif
-#include "dns-resolver-internal.h"
-
#include "alloc-util.h"
#include "dns-domain.h"
#include "escape.h"
return LESS_BY(p->fragsize, udp_header_size(p->family));
}
+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_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
[DNS_RCODE_SUCCESS] = "SUCCESS",
[DNS_RCODE_FORMERR] = "FORMERR",
extern const struct hash_ops dns_packet_hash_ops;
+/* 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]) {})
+
static inline uint64_t SD_RESOLVED_FLAGS_MAKE(
DnsProtocol protocol,
int family,
#include <math.h>
-#include "dns-resolver-internal.h"
-
#include "alloc-util.h"
#include "dns-domain.h"
#include "dns-type.h"
+++ /dev/null
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <errno.h>
-
-#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_resolver_transports_to_strv(sd_dns_alpn_flags transports, char ***ret);
-
-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);
'resize-fs.c',
'resolve-util.c',
'rm-rf.c',
- 'sd-dns-resolver.c',
'securebits-util.c',
'selinux-util.c',
'serialize.c',
+++ /dev/null
-/* 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 dns_resolver_transports_to_strv(sd_dns_alpn_flags transports, char ***ret) {
- _cleanup_strv_free_ char **ans = NULL;
-
- assert(ret);
-
- if (FLAGS_SET(transports, SD_DNS_ALPN_DO53)) {
- /* Do53 has no ALPN, this flag is only for our own usage. */
- }
-
- if (FLAGS_SET(transports, SD_DNS_ALPN_HTTP_2_TLS))
- if (strv_extend(&ans, "h2") < 0)
- return -ENOMEM;
- if (FLAGS_SET(transports, SD_DNS_ALPN_HTTP_3))
- if (strv_extend(&ans, "h3") < 0)
- return -ENOMEM;
- if (FLAGS_SET(transports, SD_DNS_ALPN_DOT))
- if (strv_extend(&ans, "dot") < 0)
- return -ENOMEM;
- if (FLAGS_SET(transports, SD_DNS_ALPN_DOQ))
- if (strv_extend(&ans, "doq") < 0)
- return -ENOMEM;
-
- *ret = TAKE_PTR(ans);
- return 0;
-}
-
-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;
-}
'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',
+++ /dev/null
-#ifndef SD_DNS_RESOLVER_H
-#define SD_DNS_RESOLVER_H
-
-#include <errno.h>
-#include <netinet/in.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#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 */