resolve: move sd-* api into libsystemd-network

This duplicates the svc param constants for the benefit of the
resolved-core library.
This commit is contained in:
Ronan Pigott
2024-10-21 09:10:20 -07:00
parent e3a23b1679
commit ee2108dcd5
9 changed files with 41 additions and 6 deletions
@@ -0,0 +1,59 @@
/* 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);
+1
View File
@@ -23,6 +23,7 @@ sources = files(
'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',
+388
View File
@@ -0,0 +1,388 @@
/* 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;
}
+44
View File
@@ -0,0 +1,44 @@
#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 */