Compare commits
25 Commits
49d1f8ceb5
...
27921575ab
Author | SHA1 | Date |
---|---|---|
Ronan Pigott | 27921575ab | |
Antonio Alvarez Feijoo | fb4c82b643 | |
Daan De Meyer | 4d9ccdc9ae | |
Antonio Alvarez Feijoo | bf39626d61 | |
Marius Hoch | ff831e7c50 | |
Ronan Pigott | 9e3482d2df | |
Ronan Pigott | 62ad75c295 | |
Ronan Pigott | 7db6e9c52c | |
Ronan Pigott | 9b9c04c23f | |
Ronan Pigott | afad8b936b | |
Ronan Pigott | 41d515edc4 | |
Ronan Pigott | 748ecb5236 | |
Ronan Pigott | 684ed0e352 | |
Ronan Pigott | 2fab384cfe | |
Ronan Pigott | 257be1d320 | |
Ronan Pigott | 8f69c9ce0f | |
Ronan Pigott | 60722f1a83 | |
Ronan Pigott | a525df4d7d | |
Ronan Pigott | 4f16e209d3 | |
Ronan Pigott | 476ce3a795 | |
Ronan Pigott | ec0e5398f3 | |
Ronan Pigott | 26f9f2677d | |
Ronan Pigott | 25c33e3500 | |
Ronan Pigott | 1e2ead52e1 | |
Ronan Pigott | 427166c3b0 |
|
@ -760,8 +760,9 @@ sensor:modalias:i2c:bmc150_accel:dmi:*:svnLENOVO:*:pvrLenovoYoga300-11IBR:*
|
||||||
sensor:modalias:acpi:ACCL0001*:dmi:*:svnLENOVO:pn60072:pvr851*:*
|
sensor:modalias:acpi:ACCL0001*:dmi:*:svnLENOVO:pn60072:pvr851*:*
|
||||||
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
||||||
|
|
||||||
# IdeaPad Duet 3 10IGL5 (82AT)
|
# IdeaPad Duet 3 10IGL5 (82AT) and 10IGL5-LTE (82HK)
|
||||||
sensor:modalias:acpi:SMO8B30*:dmi:*:svnLENOVO*:pn82AT:*
|
sensor:modalias:acpi:SMO8B30*:dmi:*:svnLENOVO*:pn82AT:*
|
||||||
|
sensor:modalias:acpi:SMO8B30*:dmi:*:svnLENOVO*:pn82HK:*
|
||||||
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
|
||||||
|
|
||||||
#########################################
|
#########################################
|
||||||
|
|
|
@ -922,9 +922,9 @@
|
||||||
target for some other supplement definition. A target cannot have more than one supplement partition
|
target for some other supplement definition. A target cannot have more than one supplement partition
|
||||||
associated with it.</para>
|
associated with it.</para>
|
||||||
|
|
||||||
<para>For example, distributions can use this to implement <variable>$BOOT</variable> as defined in
|
<para>For example, distributions can use this to implement <varname>$BOOT</varname> as defined in
|
||||||
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
|
the <ulink url="https://uapi-group.org/specifications/specs/boot_loader_specification/">Boot Loader
|
||||||
Specification</ulink>. Distributions may prefer to use the ESP as <variable>$BOOT</variable> whenever
|
Specification</ulink>. Distributions may prefer to use the ESP as <varname>$BOOT</varname> whenever
|
||||||
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
|
possible, but to adhere to the spec XBOOTLDR must sometimes be used instead. So, they should create
|
||||||
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
|
two definitions: the first defining an ESP big enough to hold just the bootloader, and a second for
|
||||||
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
|
the XBOOTLDR that's sufficiently large to hold kernels and configured as a supplement for the ESP.
|
||||||
|
|
|
@ -2605,6 +2605,18 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>UseDNR=</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para>When true, designated resolvers advertised by the DHCP server will be used as encrypted
|
||||||
|
DNS servers. See <ulink url="https://datatracker.ietf.org/doc/html/rfc9463">RFC 9463</ulink>.</para>
|
||||||
|
|
||||||
|
<para>Defaults to unset, and the value for <varname>UseDNS=</varname> will be used.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>UseMTU=</varname></term>
|
<term><varname>UseMTU=</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -3131,6 +3143,7 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>UseDNS=</varname></term>
|
<term><varname>UseDNS=</varname></term>
|
||||||
|
<term><varname>UseDNR=</varname></term>
|
||||||
<term><varname>UseNTP=</varname></term>
|
<term><varname>UseNTP=</varname></term>
|
||||||
<term><varname>UseHostname=</varname></term>
|
<term><varname>UseHostname=</varname></term>
|
||||||
<term><varname>UseDomains=</varname></term>
|
<term><varname>UseDomains=</varname></term>
|
||||||
|
@ -3418,6 +3431,16 @@ Token=prefixstable:2002:da8:1::</programlisting></para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>UseDNR=</varname></term>
|
||||||
|
<listitem>
|
||||||
|
<para> When true, the DNR servers received in the Router Advertisement will be used. Defaults to
|
||||||
|
the value of <option>UseDNS=</option>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v256"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>UseDomains=</varname></term>
|
<term><varname>UseDomains=</varname></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
|
|
@ -55,6 +55,9 @@ struct sd_dhcp_lease {
|
||||||
|
|
||||||
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
||||||
|
|
||||||
|
sd_dns_resolver *dnr;
|
||||||
|
size_t n_dnr;
|
||||||
|
|
||||||
struct sd_dhcp_route *static_routes;
|
struct sd_dhcp_route *static_routes;
|
||||||
size_t n_static_routes;
|
size_t n_static_routes;
|
||||||
struct sd_dhcp_route *classless_routes;
|
struct sd_dhcp_route *classless_routes;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "sd-dhcp-option.h"
|
#include "sd-dhcp-option.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
|
|
||||||
#include "dhcp-protocol.h"
|
#include "dhcp-protocol.h"
|
||||||
#include "hash-funcs.h"
|
#include "hash-funcs.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "sd-dhcp6-lease.h"
|
#include "sd-dhcp6-lease.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
|
|
||||||
#include "dhcp6-option.h"
|
#include "dhcp6-option.h"
|
||||||
#include "dhcp6-protocol.h"
|
#include "dhcp6-protocol.h"
|
||||||
|
@ -38,6 +39,8 @@ struct sd_dhcp6_lease {
|
||||||
|
|
||||||
struct in6_addr *dns;
|
struct in6_addr *dns;
|
||||||
size_t dns_count;
|
size_t dns_count;
|
||||||
|
sd_dns_resolver *dnr;
|
||||||
|
size_t n_dnr;
|
||||||
char **domains;
|
char **domains;
|
||||||
struct in6_addr *ntp;
|
struct in6_addr *ntp;
|
||||||
size_t ntp_count;
|
size_t ntp_count;
|
||||||
|
|
|
@ -206,6 +206,7 @@ bool dhcp6_option_can_request(uint16_t option) {
|
||||||
case SD_DHCP6_OPTION_V6_DOTS_RI:
|
case SD_DHCP6_OPTION_V6_DOTS_RI:
|
||||||
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
|
case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
|
||||||
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
|
case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
|
||||||
|
case SD_DHCP6_OPTION_V6_DNR:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -820,74 +821,6 @@ int dhcp6_option_parse_addresses(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
|
|
||||||
_cleanup_free_ char *domain = NULL;
|
|
||||||
const uint8_t *optval;
|
|
||||||
size_t optlen, n = 0;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(data);
|
|
||||||
assert(len);
|
|
||||||
assert(*data || *len == 0);
|
|
||||||
assert(ret);
|
|
||||||
|
|
||||||
optval = *data;
|
|
||||||
optlen = *len;
|
|
||||||
|
|
||||||
if (optlen <= 1)
|
|
||||||
return -ENODATA;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
const char *label;
|
|
||||||
uint8_t c;
|
|
||||||
|
|
||||||
if (optlen == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
c = *optval;
|
|
||||||
optval++;
|
|
||||||
optlen--;
|
|
||||||
|
|
||||||
if (c == 0)
|
|
||||||
/* End label */
|
|
||||||
break;
|
|
||||||
if (c > 63)
|
|
||||||
return -EBADMSG;
|
|
||||||
if (c > optlen)
|
|
||||||
return -EMSGSIZE;
|
|
||||||
|
|
||||||
/* Literal label */
|
|
||||||
label = (const char*) optval;
|
|
||||||
optval += c;
|
|
||||||
optlen -= c;
|
|
||||||
|
|
||||||
if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
if (n != 0)
|
|
||||||
domain[n++] = '.';
|
|
||||||
|
|
||||||
r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
n += r;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n > 0) {
|
|
||||||
if (!GREEDY_REALLOC(domain, n + 1))
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
domain[n] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(domain);
|
|
||||||
*data = optval;
|
|
||||||
*len = optlen;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
|
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
|
||||||
_cleanup_free_ char *domain = NULL;
|
_cleanup_free_ char *domain = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -895,7 +828,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r
|
||||||
assert(optval || optlen == 0);
|
assert(optval || optlen == 0);
|
||||||
assert(ret);
|
assert(ret);
|
||||||
|
|
||||||
r = parse_domain(&optval, &optlen, &domain);
|
r = dns_name_from_wire_format(&optval, &optlen, &domain);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
|
@ -922,11 +855,11 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha
|
||||||
while (optlen > 0) {
|
while (optlen > 0) {
|
||||||
_cleanup_free_ char *name = NULL;
|
_cleanup_free_ char *name = NULL;
|
||||||
|
|
||||||
r = parse_domain(&optval, &optlen, &name);
|
r = dns_name_from_wire_format(&optval, &optlen, &name);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (dns_name_is_root(name)) /* root domain */
|
||||||
continue;
|
return -EBADMSG;
|
||||||
|
|
||||||
r = strv_consume(&names, TAKE_PTR(name));
|
r = strv_consume(&names, TAKE_PTR(name));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -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);
|
|
@ -23,6 +23,7 @@ sources = files(
|
||||||
'sd-dhcp-server.c',
|
'sd-dhcp-server.c',
|
||||||
'sd-dhcp6-client.c',
|
'sd-dhcp6-client.c',
|
||||||
'sd-dhcp6-lease.c',
|
'sd-dhcp6-lease.c',
|
||||||
|
'sd-dns-resolver.c',
|
||||||
'sd-ipv4acd.c',
|
'sd-ipv4acd.c',
|
||||||
'sd-ipv4ll.c',
|
'sd-ipv4ll.c',
|
||||||
'sd-lldp-rx.c',
|
'sd-lldp-rx.c',
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <linux/ipv6.h>
|
#include <linux/ipv6.h>
|
||||||
#include <netinet/icmp6.h>
|
#include <netinet/icmp6.h>
|
||||||
|
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
#include "dns-domain.h"
|
#include "dns-domain.h"
|
||||||
#include "ether-addr-util.h"
|
#include "ether-addr-util.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
|
@ -89,6 +90,13 @@ static void ndisc_dnssl_done(sd_ndisc_dnssl *dnssl) {
|
||||||
strv_free(dnssl->domains);
|
strv_free(dnssl->domains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ndisc_dnr_done(sd_ndisc_dnr *dnr) {
|
||||||
|
if (!dnr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sd_dns_resolver_unref(dnr->resolver);
|
||||||
|
}
|
||||||
|
|
||||||
sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
|
sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
|
||||||
if (!option)
|
if (!option)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -109,6 +117,10 @@ sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
|
||||||
case SD_NDISC_OPTION_CAPTIVE_PORTAL:
|
case SD_NDISC_OPTION_CAPTIVE_PORTAL:
|
||||||
free(option->captive_portal);
|
free(option->captive_portal);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SD_NDISC_OPTION_ENCRYPTED_DNS:
|
||||||
|
ndisc_dnr_done(&option->encrypted_dns);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mfree(option);
|
return mfree(option);
|
||||||
|
@ -1270,6 +1282,283 @@ static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t tim
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ndisc_option_add_encrypted_dns_internal(
|
||||||
|
Set **options,
|
||||||
|
size_t offset,
|
||||||
|
sd_dns_resolver *res,
|
||||||
|
usec_t lifetime,
|
||||||
|
usec_t valid_until) {
|
||||||
|
assert(options);
|
||||||
|
|
||||||
|
sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_ENCRYPTED_DNS, offset);
|
||||||
|
if (!p)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
p->encrypted_dns = (sd_ndisc_dnr) {
|
||||||
|
.resolver = res,
|
||||||
|
.lifetime = lifetime,
|
||||||
|
.valid_until = valid_until,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ndisc_option_consume(options, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ndisc_get_dns_name(const uint8_t *optval, size_t optlen, char **ret) {
|
||||||
|
_cleanup_free_ char *name = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(optval || optlen == 0);
|
||||||
|
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 ndisc_option_parse_encrypted_dns(Set **options, size_t offset, size_t len, const uint8_t *opt) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(options);
|
||||||
|
assert(opt);
|
||||||
|
_cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
|
||||||
|
usec_t lifetime;
|
||||||
|
size_t ilen;
|
||||||
|
|
||||||
|
/* Every field up to and including adn must be present */
|
||||||
|
if (len < 2*8)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
if (opt[0] != SD_NDISC_OPTION_ENCRYPTED_DNS)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
size_t off = 2;
|
||||||
|
|
||||||
|
/* Priority */
|
||||||
|
res.priority = unaligned_read_be16(opt + off);
|
||||||
|
/* Alias mode is not allowed */
|
||||||
|
if (res.priority == 0)
|
||||||
|
return -EBADMSG;
|
||||||
|
off += sizeof(uint16_t);
|
||||||
|
|
||||||
|
/* Lifetime */
|
||||||
|
lifetime = unaligned_be32_sec_to_usec(opt + off, /* max_as_infinity = */ true);
|
||||||
|
off += sizeof(uint32_t);
|
||||||
|
|
||||||
|
/* adn field (length + dns-name) */
|
||||||
|
ilen = unaligned_read_be16(opt + off);
|
||||||
|
off += sizeof(uint16_t);
|
||||||
|
if (off + ilen > len)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
r = ndisc_get_dns_name(opt + off, ilen, &res.auth_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (dns_name_is_root(res.auth_name))
|
||||||
|
return -EBADMSG;
|
||||||
|
off += ilen;
|
||||||
|
|
||||||
|
/* This is the last field in adn-only mode, sans padding */
|
||||||
|
if (8 * DIV_ROUND_UP(off, 8) == len && memeqzero(opt + off, len - off))
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Received ADN-only encrypted DNS option, ignoring.");
|
||||||
|
|
||||||
|
/* Fields following the variable (octets) length adn field are no longer certain to be aligned. */
|
||||||
|
|
||||||
|
/* addrs (length + packed struct in6_addr) */
|
||||||
|
if (off + sizeof(uint16_t) > len)
|
||||||
|
return -EBADMSG;
|
||||||
|
ilen = unaligned_read_be16(opt + off);
|
||||||
|
off += sizeof(uint16_t);
|
||||||
|
if (off + ilen > len || ilen % (sizeof(struct in6_addr)) != 0)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
size_t n_addrs = ilen / (sizeof(struct in6_addr));
|
||||||
|
if (n_addrs == 0)
|
||||||
|
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;
|
||||||
|
memcpy(&addr.in6, opt + off, sizeof(struct in6_addr));
|
||||||
|
if (in_addr_is_multicast(AF_INET6, &addr) ||
|
||||||
|
in_addr_is_localhost(AF_INET, &addr))
|
||||||
|
return -EBADMSG;
|
||||||
|
res.addrs[i] = addr;
|
||||||
|
off += sizeof(struct in6_addr);
|
||||||
|
}
|
||||||
|
res.n_addrs = n_addrs;
|
||||||
|
res.family = AF_INET6;
|
||||||
|
|
||||||
|
/* SvcParam field. (length + SvcParams) */
|
||||||
|
if (off + sizeof(uint16_t) > len)
|
||||||
|
return -EBADMSG;
|
||||||
|
ilen = unaligned_read_be16(opt + off);
|
||||||
|
off += sizeof(uint16_t);
|
||||||
|
if (off + ilen > len)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
r = dnr_parse_svc_params(opt + off, ilen, &res);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) /* This indicates a valid message we don't support */
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
off += ilen;
|
||||||
|
|
||||||
|
/* the remaining padding bytes must be zeroed */
|
||||||
|
if (len - off >= 8 || !memeqzero(opt + off, len - off))
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
sd_dns_resolver *new_res = new(sd_dns_resolver, 1);
|
||||||
|
if (!new_res)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*new_res = TAKE_STRUCT(res);
|
||||||
|
|
||||||
|
return ndisc_option_add_encrypted_dns(options, offset, new_res, lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ndisc_option_build_encrypted_dns(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(option);
|
||||||
|
assert(option->type == SD_NDISC_OPTION_ENCRYPTED_DNS);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
size_t off, len, ilen, plen, poff;
|
||||||
|
|
||||||
|
/* Everything up to adn field is required, so we need at least 2*8 bytes */
|
||||||
|
_cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
_cleanup_strv_free_ char **alpns = NULL;
|
||||||
|
const sd_dns_resolver *res = option->encrypted_dns.resolver;
|
||||||
|
be32_t lifetime = usec_to_be32_sec(MIN(option->encrypted_dns.lifetime,
|
||||||
|
usec_sub_unsigned(option->encrypted_dns.valid_until, timestamp)));
|
||||||
|
|
||||||
|
/* Type (Length field filled in last) */
|
||||||
|
buf[0] = option->type;
|
||||||
|
|
||||||
|
/* Priority */
|
||||||
|
off = 2;
|
||||||
|
unaligned_write_be16(buf + off, res->priority);
|
||||||
|
off += sizeof(be16_t);
|
||||||
|
|
||||||
|
/* Lifetime */
|
||||||
|
memcpy(buf + off, &lifetime, sizeof(be32_t));
|
||||||
|
off += sizeof(be32_t);
|
||||||
|
|
||||||
|
/* ADN */
|
||||||
|
//FIXME can the wire format be longer than this?
|
||||||
|
ilen = strlen(res->auth_name) + 2;
|
||||||
|
|
||||||
|
/* From now on, there isn't guaranteed to be enough space to put each field */
|
||||||
|
if (!GREEDY_REALLOC(buf, off + sizeof(uint16_t) + ilen))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = dns_name_to_wire_format(res->auth_name, buf + off + sizeof(uint16_t), ilen, /* canonical = */ false);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
unaligned_write_be16(buf + off, (uint16_t) r);
|
||||||
|
off += sizeof(uint16_t) + r;
|
||||||
|
|
||||||
|
/* ADN-only mode */
|
||||||
|
if (res->n_addrs == 0)
|
||||||
|
goto padding;
|
||||||
|
|
||||||
|
/* addrs */
|
||||||
|
if (size_multiply_overflow(sizeof(struct in6_addr), res->n_addrs))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ilen = res->n_addrs * sizeof(struct in6_addr);
|
||||||
|
if (!GREEDY_REALLOC(buf, off + sizeof(uint16_t) + ilen))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
unaligned_write_be16(buf + off, ilen);
|
||||||
|
off += sizeof(uint16_t);
|
||||||
|
|
||||||
|
FOREACH_ARRAY(addr, res->addrs, res->n_addrs) {
|
||||||
|
memcpy(buf + off, &addr->in6, sizeof(struct in6_addr));
|
||||||
|
off += sizeof(struct in6_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SvcParam, MUST appear in order */
|
||||||
|
poff = off + sizeof(uint16_t);
|
||||||
|
|
||||||
|
/* ALPN */
|
||||||
|
dns_resolver_transports_to_strv(res->transports, &alpns);
|
||||||
|
|
||||||
|
/* res needs to have at least one valid transport */
|
||||||
|
if (strv_isempty(alpns))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
plen = 0;
|
||||||
|
STRV_FOREACH(alpn, alpns)
|
||||||
|
plen += sizeof(uint8_t) + strlen(*alpn);
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_ALPN);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
unaligned_write_be16(buf + poff, plen);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
|
||||||
|
STRV_FOREACH(alpn, alpns) {
|
||||||
|
size_t alen = strlen(*alpn);
|
||||||
|
buf[poff++] = alen;
|
||||||
|
memcpy(buf + poff, *alpn, alen);
|
||||||
|
poff += alen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* port */
|
||||||
|
if (res->port > 0) {
|
||||||
|
plen = 2;
|
||||||
|
if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_PORT);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
unaligned_write_be16(buf + poff, plen);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
unaligned_write_be16(buf + poff, res->port);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dohpath */
|
||||||
|
if (res->dohpath) {
|
||||||
|
plen = strlen(res->dohpath);
|
||||||
|
if (!GREEDY_REALLOC(buf, poff + 2 * sizeof(uint16_t) + plen))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
unaligned_write_be16(buf + poff, (uint16_t) DNS_SVC_PARAM_KEY_DOHPATH);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
unaligned_write_be16(buf + poff, plen);
|
||||||
|
poff += sizeof(uint16_t);
|
||||||
|
memcpy(buf + poff, res->dohpath, plen);
|
||||||
|
poff += plen;
|
||||||
|
}
|
||||||
|
|
||||||
|
unaligned_write_be16(buf + off, LESS_BY(poff, off));
|
||||||
|
off = poff;
|
||||||
|
|
||||||
|
padding:
|
||||||
|
len = DIV_ROUND_UP(off, 8);
|
||||||
|
if (!GREEDY_REALLOC(buf, 8*len))
|
||||||
|
return -ENOMEM;
|
||||||
|
memzero(buf + off, 8*len - off);
|
||||||
|
|
||||||
|
buf[1] = len;
|
||||||
|
*ret = TAKE_PTR(buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
|
static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
|
||||||
assert(options);
|
assert(options);
|
||||||
assert(opt);
|
assert(opt);
|
||||||
|
@ -1376,6 +1665,10 @@ int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
|
||||||
r = ndisc_option_parse_prefix64(&options, offset, length, opt);
|
r = ndisc_option_parse_prefix64(&options, offset, length, opt);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SD_NDISC_OPTION_ENCRYPTED_DNS:
|
||||||
|
r = ndisc_option_parse_encrypted_dns(&options, offset, length, opt);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
r = ndisc_option_parse_default(&options, offset, length, opt);
|
r = ndisc_option_parse_default(&options, offset, length, opt);
|
||||||
}
|
}
|
||||||
|
@ -1487,6 +1780,10 @@ int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr,
|
||||||
r = ndisc_option_build_prefix64(option, timestamp, &buf);
|
r = ndisc_option_build_prefix64(option, timestamp, &buf);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SD_NDISC_OPTION_ENCRYPTED_DNS:
|
||||||
|
r = ndisc_option_build_encrypted_dns(option, timestamp, &buf);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
#include "sd-ndisc-protocol.h"
|
#include "sd-ndisc-protocol.h"
|
||||||
|
#include "sd-dns-resolver.h"
|
||||||
|
|
||||||
#include "icmp6-packet.h"
|
#include "icmp6-packet.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
|
@ -66,6 +67,12 @@ typedef struct sd_ndisc_prefix64 {
|
||||||
usec_t valid_until;
|
usec_t valid_until;
|
||||||
} sd_ndisc_prefix64;
|
} sd_ndisc_prefix64;
|
||||||
|
|
||||||
|
typedef struct sd_ndisc_dnr {
|
||||||
|
sd_dns_resolver *resolver;
|
||||||
|
usec_t lifetime;
|
||||||
|
usec_t valid_until;
|
||||||
|
} sd_ndisc_dnr;
|
||||||
|
|
||||||
typedef struct sd_ndisc_option {
|
typedef struct sd_ndisc_option {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
|
@ -83,6 +90,7 @@ typedef struct sd_ndisc_option {
|
||||||
sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */
|
sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */
|
||||||
char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
|
char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
|
||||||
sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
|
sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
|
||||||
|
sd_ndisc_dnr encrypted_dns; /* SD_NDISC_OPTION_ENCRYPTED_DNS */
|
||||||
};
|
};
|
||||||
} sd_ndisc_option;
|
} sd_ndisc_option;
|
||||||
|
|
||||||
|
@ -327,4 +335,26 @@ static inline int ndisc_option_set_prefix64(
|
||||||
return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until);
|
return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ndisc_option_add_encrypted_dns_internal(
|
||||||
|
Set **options,
|
||||||
|
size_t offset,
|
||||||
|
sd_dns_resolver *res,
|
||||||
|
usec_t lifetime,
|
||||||
|
usec_t valid_until);
|
||||||
|
static inline int ndisc_option_add_encrypted_dns(
|
||||||
|
Set **options,
|
||||||
|
size_t offset,
|
||||||
|
sd_dns_resolver *res,
|
||||||
|
usec_t lifetime) {
|
||||||
|
return ndisc_option_add_encrypted_dns_internal(options, offset, res, lifetime, USEC_INFINITY);
|
||||||
|
}
|
||||||
|
static inline int ndisc_option_set_encrypted_dns(
|
||||||
|
Set **options,
|
||||||
|
size_t offset,
|
||||||
|
sd_dns_resolver *res,
|
||||||
|
usec_t lifetime,
|
||||||
|
usec_t valid_until) {
|
||||||
|
return ndisc_option_add_encrypted_dns_internal(options, 0, res, lifetime, valid_until);
|
||||||
|
}
|
||||||
|
|
||||||
int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp);
|
int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp);
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "network-internal.h"
|
#include "network-internal.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
#include "strv.h"
|
||||||
|
|
||||||
size_t serialize_in_addrs(FILE *f,
|
size_t serialize_in_addrs(FILE *f,
|
||||||
const struct in_addr *addresses,
|
const struct in_addr *addresses,
|
||||||
|
@ -131,6 +132,99 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int serialize_dnr(FILE *f, const sd_dns_resolver *dnr, size_t n_dnr, bool *with_leading_space) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
bool _space = false;
|
||||||
|
if (!with_leading_space)
|
||||||
|
with_leading_space = &_space;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
r = dns_resolvers_to_dot_strv(dnr, n_dnr, &names);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r > 0)
|
||||||
|
fputstrv(f, names, NULL, with_leading_space);
|
||||||
|
n += r;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coalesce_dnr(sd_dns_resolver *dnr, size_t n_dnr, int family, const char *auth_name,
|
||||||
|
union in_addr_union *addr) {
|
||||||
|
assert(dnr || n_dnr == 0);
|
||||||
|
assert(auth_name);
|
||||||
|
assert(addr);
|
||||||
|
|
||||||
|
/* Look through list of DNR for matching resolvers to add our addr to. Since DoT is assumed, no need
|
||||||
|
* to compare transports/dohpath/etc. */
|
||||||
|
FOREACH_ARRAY(res, dnr, n_dnr) {
|
||||||
|
if (family == res->family && streq(auth_name, res->auth_name)) {
|
||||||
|
if (!GREEDY_REALLOC(res->addrs, res->n_addrs + 1))
|
||||||
|
return -ENOMEM;
|
||||||
|
res->addrs[res->n_addrs++] = *addr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deserialized resolvers are assumed to offer DoT service. */
|
||||||
|
int deserialize_dnr(sd_dns_resolver **ret, const char *string) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(ret);
|
||||||
|
assert(string);
|
||||||
|
|
||||||
|
sd_dns_resolver *dnr = NULL;
|
||||||
|
size_t n = 0;
|
||||||
|
CLEANUP_ARRAY(dnr, n, dns_resolver_done_many);
|
||||||
|
int priority = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
_cleanup_free_ char *word = NULL;
|
||||||
|
|
||||||
|
r = extract_first_word(&string, &word, NULL, 0);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint16_t port;
|
||||||
|
int family;
|
||||||
|
_cleanup_free_ union in_addr_union *addr = new(union in_addr_union, 1);
|
||||||
|
_cleanup_free_ char *auth_name = NULL;
|
||||||
|
|
||||||
|
r = in_addr_port_ifindex_name_from_string_auto(word, &family, addr, &port, NULL, &auth_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = coalesce_dnr(dnr, n, family, auth_name, addr);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r > 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(dnr, n+1))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priority = n+1;
|
||||||
|
dnr[n++] = (sd_dns_resolver) {
|
||||||
|
.priority = priority, /* not serialized, but this will preserve the order */
|
||||||
|
.auth_name = TAKE_PTR(auth_name),
|
||||||
|
.family = family,
|
||||||
|
.addrs = TAKE_PTR(addr),
|
||||||
|
.n_addrs = 1,
|
||||||
|
.transports = SD_DNS_ALPN_DOT,
|
||||||
|
.port = port,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(dnr);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
|
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
|
||||||
assert(f);
|
assert(f);
|
||||||
assert(key);
|
assert(key);
|
||||||
|
|
|
@ -17,6 +17,9 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
|
||||||
bool *with_leading_space);
|
bool *with_leading_space);
|
||||||
int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
|
int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
|
||||||
|
|
||||||
|
int serialize_dnr(FILE *f, const sd_dns_resolver *dnr, size_t n_dnr, bool *with_leading_space);
|
||||||
|
int deserialize_dnr(sd_dns_resolver **ret, const char *string);
|
||||||
|
|
||||||
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
|
/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
|
||||||
struct sd_dhcp_route;
|
struct sd_dhcp_route;
|
||||||
struct sd_dhcp_lease;
|
struct sd_dhcp_lease;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "sd-dhcp-lease.h"
|
#include "sd-dhcp-lease.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dhcp-lease-internal.h"
|
#include "dhcp-lease-internal.h"
|
||||||
|
@ -26,6 +27,7 @@
|
||||||
#include "network-common.h"
|
#include "network-common.h"
|
||||||
#include "network-internal.h"
|
#include "network-internal.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
|
#include "sort-util.h"
|
||||||
#include "stdio-util.h"
|
#include "stdio-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
@ -229,6 +231,17 @@ int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **ret) {
|
||||||
return 0;
|
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) {
|
int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) {
|
||||||
assert_return(lease, -EINVAL);
|
assert_return(lease, -EINVAL);
|
||||||
assert_return(addr, -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++)
|
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
|
||||||
free(lease->servers[i].addr);
|
free(lease->servers[i].addr);
|
||||||
|
|
||||||
|
dns_resolver_done_many(lease->dnr, lease->n_dnr);
|
||||||
free(lease->static_routes);
|
free(lease->static_routes);
|
||||||
free(lease->classless_routes);
|
free(lease->classless_routes);
|
||||||
free(lease->vendor_specific);
|
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);
|
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) {
|
static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -879,6 +1020,15 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
||||||
break;
|
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:
|
case SD_DHCP_OPTION_VENDOR_SPECIFIC:
|
||||||
|
|
||||||
if (len <= 0)
|
if (len <= 0)
|
||||||
|
@ -1140,6 +1290,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
||||||
fputc('\n', f);
|
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);
|
r = sd_dhcp_lease_get_ntp(lease, &addresses);
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
fputs("NTP=", f);
|
fputs("NTP=", f);
|
||||||
|
@ -1248,6 +1406,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||||
*next_server = NULL,
|
*next_server = NULL,
|
||||||
*broadcast = NULL,
|
*broadcast = NULL,
|
||||||
*dns = NULL,
|
*dns = NULL,
|
||||||
|
*dnr = NULL,
|
||||||
*ntp = NULL,
|
*ntp = NULL,
|
||||||
*sip = NULL,
|
*sip = NULL,
|
||||||
*pop3 = NULL,
|
*pop3 = NULL,
|
||||||
|
@ -1285,6 +1444,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||||
"NEXT_SERVER", &next_server,
|
"NEXT_SERVER", &next_server,
|
||||||
"BROADCAST", &broadcast,
|
"BROADCAST", &broadcast,
|
||||||
"DNS", &dns,
|
"DNS", &dns,
|
||||||
|
"DNR", &dnr,
|
||||||
"NTP", &ntp,
|
"NTP", &ntp,
|
||||||
"SIP", &sip,
|
"SIP", &sip,
|
||||||
"POP3", &pop3,
|
"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;
|
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) {
|
if (ntp) {
|
||||||
r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
|
r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "dhcp6-internal.h"
|
#include "dhcp6-internal.h"
|
||||||
#include "dhcp6-lease-internal.h"
|
#include "dhcp6-lease-internal.h"
|
||||||
#include "network-common.h"
|
#include "network-common.h"
|
||||||
|
#include "sort-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "unaligned.h"
|
#include "unaligned.h"
|
||||||
|
|
||||||
|
@ -438,6 +439,98 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
||||||
return strv_length(lease->domains);
|
return strv_length(lease->domains);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(lease);
|
||||||
|
|
||||||
|
_cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
/* priority */
|
||||||
|
if (optlen - offset < sizeof(uint16_t))
|
||||||
|
return -EBADMSG;
|
||||||
|
res.priority = unaligned_read_be16(optval + offset);
|
||||||
|
offset += sizeof(uint16_t);
|
||||||
|
|
||||||
|
/* adn */
|
||||||
|
if (optlen - offset < sizeof(uint16_t))
|
||||||
|
return -EBADMSG;
|
||||||
|
size_t ilen = unaligned_read_be16(optval + offset);
|
||||||
|
offset += sizeof(uint16_t);
|
||||||
|
if (offset + ilen > optlen)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
offset += ilen;
|
||||||
|
|
||||||
|
/* RFC9463 § 3.1.6: adn only mode */
|
||||||
|
if (offset == optlen)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* addrs */
|
||||||
|
if (optlen - offset < sizeof(uint16_t))
|
||||||
|
return -EBADMSG;
|
||||||
|
ilen = unaligned_read_be16(optval + offset);
|
||||||
|
offset += sizeof(uint16_t);
|
||||||
|
if (offset + ilen > optlen)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
_cleanup_free_ struct in6_addr *addrs = NULL;
|
||||||
|
size_t n_addrs = 0;
|
||||||
|
|
||||||
|
r = dhcp6_option_parse_addresses(optval + offset, ilen, &addrs, &n_addrs);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (n_addrs == 0)
|
||||||
|
return -EBADMSG;
|
||||||
|
offset += ilen;
|
||||||
|
|
||||||
|
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 = {.in6 = addrs[i]};
|
||||||
|
/* RFC9463 § 6.2 client MUST discard multicast and host loopback addresses */
|
||||||
|
if (in_addr_is_multicast(AF_INET6, &addr) ||
|
||||||
|
in_addr_is_localhost(AF_INET6, &addr))
|
||||||
|
return -EBADMSG;
|
||||||
|
res.addrs[i] = addr;
|
||||||
|
}
|
||||||
|
res.n_addrs = n_addrs;
|
||||||
|
res.family = AF_INET6;
|
||||||
|
|
||||||
|
/* svc params */
|
||||||
|
r = dnr_parse_svc_params(optval + offset, optlen-offset, &res);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Append this resolver */
|
||||||
|
if (!GREEDY_REALLOC(lease->dnr, lease->n_dnr+1))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
lease->dnr[lease->n_dnr++] = TAKE_STRUCT(res);
|
||||||
|
|
||||||
|
typesafe_qsort(lease->dnr, lease->n_dnr, dns_resolver_prio_compare);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret) {
|
||||||
|
assert_return(lease, -EINVAL);
|
||||||
|
assert_return(ret, -EINVAL);
|
||||||
|
|
||||||
|
if (!lease->dnr)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
*ret = lease->dnr;
|
||||||
|
return lease->n_dnr;
|
||||||
|
}
|
||||||
|
|
||||||
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -851,6 +944,15 @@ static int dhcp6_lease_parse_message(
|
||||||
irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
|
irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SD_DHCP6_OPTION_V6_DNR:
|
||||||
|
r = dhcp6_lease_add_dnr(lease, optval, optlen);
|
||||||
|
if (r < 0)
|
||||||
|
return log_dhcp6_client_errno(client, r, "Failed to parse DNR option, ignoring: %m");
|
||||||
|
if (r == 0)
|
||||||
|
log_dhcp6_client(client, "Received ADN-only DNRv6 option, ignoring.");
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case SD_DHCP6_OPTION_VENDOR_OPTS:
|
case SD_DHCP6_OPTION_VENDOR_OPTS:
|
||||||
r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
|
r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -904,6 +1006,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||||
dhcp6_ia_free(lease->ia_na);
|
dhcp6_ia_free(lease->ia_na);
|
||||||
dhcp6_ia_free(lease->ia_pd);
|
dhcp6_ia_free(lease->ia_pd);
|
||||||
free(lease->dns);
|
free(lease->dns);
|
||||||
|
dns_resolver_done_many(lease->dnr, lease->n_dnr);
|
||||||
free(lease->fqdn);
|
free(lease->fqdn);
|
||||||
free(lease->captive_portal);
|
free(lease->captive_portal);
|
||||||
strv_free(lease->domains);
|
strv_free(lease->domains);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -8,9 +8,12 @@
|
||||||
#include "sd-ndisc.h"
|
#include "sd-ndisc.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "dns-domain.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
#include "ndisc-internal.h"
|
#include "ndisc-internal.h"
|
||||||
#include "ndisc-router-internal.h"
|
#include "ndisc-router-internal.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
|
#include "unaligned.h"
|
||||||
|
|
||||||
static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
||||||
if (!rt)
|
if (!rt)
|
||||||
|
@ -91,6 +94,7 @@ DEFINE_GET_TIMESTAMP(route_get_lifetime);
|
||||||
DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
|
DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
|
||||||
DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
|
DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
|
||||||
DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
|
DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
|
||||||
|
DEFINE_GET_TIMESTAMP(encrypted_dns_get_lifetime);
|
||||||
|
|
||||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||||
const struct nd_router_advert *a;
|
const struct nd_router_advert *a;
|
||||||
|
@ -342,3 +346,6 @@ DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
|
||||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
|
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
|
||||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
|
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
|
||||||
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
|
DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
|
||||||
|
|
||||||
|
DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, lifetime, uint64_t);
|
||||||
|
DEFINE_GETTER(encrypted_dns, SD_NDISC_OPTION_ENCRYPTED_DNS, resolver, sd_dns_resolver*);
|
||||||
|
|
|
@ -165,10 +165,7 @@ TEST(parse_domain) {
|
||||||
domain = mfree(domain);
|
domain = mfree(domain);
|
||||||
|
|
||||||
data = (uint8_t []) { 4, 't', 'e', 's', 't' };
|
data = (uint8_t []) { 4, 't', 'e', 's', 't' };
|
||||||
assert_se(dhcp6_option_parse_domainname(data, 5, &domain) >= 0);
|
assert_se(dhcp6_option_parse_domainname(data, 5, &domain) < 0);
|
||||||
assert_se(domain);
|
|
||||||
assert_se(streq(domain, "test"));
|
|
||||||
domain = mfree(domain);
|
|
||||||
|
|
||||||
data = (uint8_t []) { 0 };
|
data = (uint8_t []) { 0 };
|
||||||
assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0);
|
assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0);
|
||||||
|
@ -745,8 +742,8 @@ static const uint8_t msg_reply[] = {
|
||||||
0x00, SD_DHCP6_OPTION_DOMAIN, 0x00, 0x0b,
|
0x00, SD_DHCP6_OPTION_DOMAIN, 0x00, 0x0b,
|
||||||
0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
||||||
/* Client FQDN */
|
/* Client FQDN */
|
||||||
0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
|
0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x13,
|
||||||
0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
|
0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
||||||
/* Vendor specific options */
|
/* Vendor specific options */
|
||||||
0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
|
0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
|
||||||
0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
|
0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
|
||||||
|
@ -827,8 +824,8 @@ static const uint8_t msg_advertise[] = {
|
||||||
0x00, SD_DHCP6_OPTION_DOMAIN, 0x00, 0x0b,
|
0x00, SD_DHCP6_OPTION_DOMAIN, 0x00, 0x0b,
|
||||||
0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
||||||
/* Client FQDN */
|
/* Client FQDN */
|
||||||
0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
|
0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x13,
|
||||||
0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
|
0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
|
||||||
/* Vendor specific options */
|
/* Vendor specific options */
|
||||||
0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
|
0x00, SD_DHCP6_OPTION_VENDOR_OPTS, 0x00, 0x09,
|
||||||
0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
|
0x00, 0x00, 0x00, 0x20, 0x00, 0xf7, 0x00, 0x01, VENDOR_SUBOPTION_BYTES,
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
|
||||||
#include "conf-parser.h"
|
#include "conf-parser.h"
|
||||||
#include "dhcp-duid-internal.h"
|
#include "dhcp-duid-internal.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
#include "in-addr-util.h"
|
#include "in-addr-util.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
|
|
|
@ -1564,6 +1564,13 @@ static int dhcp4_configure(Link *link) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for SIP server: %m");
|
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for SIP server: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
|
||||||
|
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_V4_DNR);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for DNR: %m");
|
||||||
|
}
|
||||||
|
|
||||||
if (link->network->dhcp_use_captive_portal) {
|
if (link->network->dhcp_use_captive_portal) {
|
||||||
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL);
|
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -646,6 +646,12 @@ static int dhcp6_configure(Link *link) {
|
||||||
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request domains: %m");
|
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request domains: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
|
||||||
|
r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_V6_DNR);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to request DNR: %m");
|
||||||
|
}
|
||||||
|
|
||||||
if (link->network->dhcp6_use_captive_portal > 0) {
|
if (link->network->dhcp6_use_captive_portal > 0) {
|
||||||
r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CAPTIVE_PORTAL);
|
r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CAPTIVE_PORTAL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|
|
@ -94,6 +94,37 @@ bool link_get_use_dns(Link *link, NetworkConfigSource proto) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool link_get_use_dnr(Link *link, NetworkConfigSource proto) {
|
||||||
|
int n;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
if (!link->network)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (proto) {
|
||||||
|
case NETWORK_CONFIG_SOURCE_DHCP4:
|
||||||
|
n = link->network->dhcp_use_dnr;
|
||||||
|
break;
|
||||||
|
case NETWORK_CONFIG_SOURCE_DHCP6:
|
||||||
|
n = link->network->dhcp6_use_dnr;
|
||||||
|
break;
|
||||||
|
case NETWORK_CONFIG_SOURCE_NDISC:
|
||||||
|
n = link->network->ndisc_use_dnr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert_not_reached();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If set explicitly, use that */
|
||||||
|
if (n >= 0)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
/* Otherwise, default to the same as the UseDNS setting. After all,
|
||||||
|
* this is just another way for the server to tell us about DNS configuration. */
|
||||||
|
return link_get_use_dns(link, proto);
|
||||||
|
}
|
||||||
|
|
||||||
int config_parse_domains(
|
int config_parse_domains(
|
||||||
const char *unit,
|
const char *unit,
|
||||||
const char *filename,
|
const char *filename,
|
||||||
|
|
|
@ -17,6 +17,7 @@ typedef enum UseDomains {
|
||||||
|
|
||||||
UseDomains link_get_use_domains(Link *link, NetworkConfigSource proto);
|
UseDomains link_get_use_domains(Link *link, NetworkConfigSource proto);
|
||||||
bool link_get_use_dns(Link *link, NetworkConfigSource proto);
|
bool link_get_use_dns(Link *link, NetworkConfigSource proto);
|
||||||
|
bool link_get_use_dnr(Link *link, NetworkConfigSource proto);
|
||||||
|
|
||||||
const char* use_domains_to_string(UseDomains p) _const_;
|
const char* use_domains_to_string(UseDomains p) _const_;
|
||||||
UseDomains use_domains_from_string(const char *s) _pure_;
|
UseDomains use_domains_from_string(const char *s) _pure_;
|
||||||
|
|
|
@ -503,6 +503,117 @@ static int dns_append_json(Link *link, sd_json_variant **v) {
|
||||||
return json_variant_set_field_non_null(v, "DNS", array);
|
return json_variant_set_field_non_null(v, "DNS", array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int dnr_append_json_one(Link *link, const struct sd_dns_resolver *res, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *addrs_array = NULL;
|
||||||
|
_cleanup_strv_free_ char **transports = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(res);
|
||||||
|
assert(array);
|
||||||
|
|
||||||
|
FOREACH_ARRAY(addr, res->addrs, res->n_addrs) {
|
||||||
|
r = sd_json_variant_append_arrayb(
|
||||||
|
&addrs_array,
|
||||||
|
JSON_BUILD_IN_ADDR(addr, res->family));
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = dns_resolver_transports_to_strv(res->transports, &transports);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
//FIXME ifindex?
|
||||||
|
return sd_json_variant_append_arrayb(
|
||||||
|
array,
|
||||||
|
SD_JSON_BUILD_OBJECT(
|
||||||
|
SD_JSON_BUILD_PAIR_INTEGER("Family", res->family),
|
||||||
|
SD_JSON_BUILD_PAIR_INTEGER("Priority", res->priority),
|
||||||
|
JSON_BUILD_PAIR_VARIANT_NON_NULL("Addresses", addrs_array),
|
||||||
|
JSON_BUILD_PAIR_UNSIGNED_NON_ZERO("Port", res->port),
|
||||||
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("ServerName", res->auth_name),
|
||||||
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("DoHPath", res->dohpath),
|
||||||
|
JSON_BUILD_PAIR_STRV_NON_EMPTY("Transports", transports),
|
||||||
|
SD_JSON_BUILD_PAIR_STRING("ConfigSource", network_config_source_to_string(s)),
|
||||||
|
JSON_BUILD_PAIR_IN_ADDR_NON_NULL("ConfigProvider", p, res->family)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dnr_append_json(Link *link, sd_json_variant **v) {
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(v);
|
||||||
|
|
||||||
|
if (!link->network)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (link->dhcp_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
|
||||||
|
struct sd_dns_resolver *dnr;
|
||||||
|
union in_addr_union s;
|
||||||
|
int n_dnr;
|
||||||
|
|
||||||
|
r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &s.in);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
n_dnr = sd_dhcp_lease_get_dnr(link->dhcp_lease, &dnr);
|
||||||
|
if (n_dnr < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
FOREACH_ARRAY(res, dnr, n_dnr) {
|
||||||
|
r = dnr_append_json_one(link,
|
||||||
|
res,
|
||||||
|
NETWORK_CONFIG_SOURCE_DHCP4,
|
||||||
|
&s,
|
||||||
|
&array);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link->dhcp6_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
|
||||||
|
struct sd_dns_resolver *dnr;
|
||||||
|
union in_addr_union s;
|
||||||
|
int n_dnr;
|
||||||
|
|
||||||
|
r = sd_dhcp6_lease_get_server_address(link->dhcp6_lease, &s.in6);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
n_dnr = sd_dhcp6_lease_get_dnr(link->dhcp6_lease, &dnr);
|
||||||
|
if (n_dnr < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
FOREACH_ARRAY(res, dnr, n_dnr) {
|
||||||
|
r = dnr_append_json_one(link,
|
||||||
|
res,
|
||||||
|
NETWORK_CONFIG_SOURCE_DHCP6,
|
||||||
|
&s,
|
||||||
|
&array);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) {
|
||||||
|
NDiscDNR *a;
|
||||||
|
|
||||||
|
SET_FOREACH(a, link->ndisc_dnr) {
|
||||||
|
r = dnr_append_json_one(link,
|
||||||
|
&a->resolver,
|
||||||
|
NETWORK_CONFIG_SOURCE_NDISC,
|
||||||
|
&(union in_addr_union) { .in6 = a->router },
|
||||||
|
&array);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_variant_set_field_non_null(v, "DNR", array);
|
||||||
|
}
|
||||||
|
|
||||||
static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) {
|
static int server_append_json_one_addr(int family, const union in_addr_union *a, NetworkConfigSource s, const union in_addr_union *p, sd_json_variant **array) {
|
||||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||||
assert(a);
|
assert(a);
|
||||||
|
@ -1268,6 +1379,10 @@ int link_build_json(Link *link, sd_json_variant **ret) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
r = dnr_append_json(link, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
r = ntp_append_json(link, &v);
|
r = ntp_append_json(link, &v);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
|
@ -167,6 +167,7 @@ typedef struct Link {
|
||||||
Set *ndisc_captive_portals;
|
Set *ndisc_captive_portals;
|
||||||
Set *ndisc_pref64;
|
Set *ndisc_pref64;
|
||||||
Set *ndisc_redirects;
|
Set *ndisc_redirects;
|
||||||
|
Set *ndisc_dnr;
|
||||||
uint32_t ndisc_mtu;
|
uint32_t ndisc_mtu;
|
||||||
unsigned ndisc_messages;
|
unsigned ndisc_messages;
|
||||||
bool ndisc_configured:1;
|
bool ndisc_configured:1;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "networkd-route.h"
|
#include "networkd-route.h"
|
||||||
#include "networkd-state-file.h"
|
#include "networkd-state-file.h"
|
||||||
#include "networkd-sysctl.h"
|
#include "networkd-sysctl.h"
|
||||||
|
#include "sort-util.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
@ -1828,6 +1829,147 @@ static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NDiscDNR* ndisc_dnr_free(NDiscDNR *x) {
|
||||||
|
if (!x)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sd_dns_resolver_done(&x->resolver);
|
||||||
|
return mfree(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(NDiscDNR*, ndisc_dnr_free);
|
||||||
|
|
||||||
|
static int ndisc_dnr_compare_func(const NDiscDNR *a, const NDiscDNR *b) {
|
||||||
|
return CMP(a->resolver.priority, b->resolver.priority) ||
|
||||||
|
strcmp_ptr(a->resolver.auth_name, b->resolver.auth_name) ||
|
||||||
|
CMP(a->resolver.transports, b->resolver.transports) ||
|
||||||
|
CMP(a->resolver.port, b->resolver.port) ||
|
||||||
|
strcmp_ptr(a->resolver.dohpath, b->resolver.dohpath) ||
|
||||||
|
CMP(a->resolver.family, b->resolver.family) ||
|
||||||
|
CMP(a->resolver.n_addrs, b->resolver.n_addrs) ||
|
||||||
|
memcmp(a->resolver.addrs, b->resolver.addrs, sizeof(a->resolver.addrs[0]) * a->resolver.n_addrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ndisc_dnr_hash_func(const NDiscDNR *x, struct siphash *state) {
|
||||||
|
assert(x);
|
||||||
|
|
||||||
|
siphash24_compress_resolver(&x->resolver, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||||
|
ndisc_dnr_hash_ops,
|
||||||
|
NDiscDNR,
|
||||||
|
ndisc_dnr_hash_func,
|
||||||
|
ndisc_dnr_compare_func,
|
||||||
|
ndisc_dnr_free);
|
||||||
|
|
||||||
|
static int sd_dns_resolver_copy(const sd_dns_resolver *a, sd_dns_resolver *b) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
assert(b);
|
||||||
|
|
||||||
|
_cleanup_(sd_dns_resolver_done) sd_dns_resolver c = {
|
||||||
|
.priority = a->priority,
|
||||||
|
.transports = a->transports,
|
||||||
|
.port = a->port,
|
||||||
|
/* .auth_name */
|
||||||
|
.family = a->family,
|
||||||
|
/* .addrs */
|
||||||
|
/* .n_addrs */
|
||||||
|
/* .dohpath */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* auth_name */
|
||||||
|
r = strdup_to(&c.auth_name, a->auth_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* addrs, n_addrs */
|
||||||
|
c.addrs = newdup(union in_addr_union, a->addrs, a->n_addrs);
|
||||||
|
if (!c.addrs)
|
||||||
|
return r;
|
||||||
|
c.n_addrs = a->n_addrs;
|
||||||
|
|
||||||
|
/* dohpath */
|
||||||
|
r = strdup_to(&c.dohpath, a->dohpath);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*b = TAKE_STRUCT(c);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ndisc_router_process_encrypted_dns(Link *link, sd_ndisc_router *rt) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(link->network);
|
||||||
|
assert(rt);
|
||||||
|
|
||||||
|
struct in6_addr router;
|
||||||
|
usec_t lifetime_usec;
|
||||||
|
sd_dns_resolver *res;
|
||||||
|
_cleanup_(ndisc_dnr_freep) NDiscDNR *new_entry = NULL;
|
||||||
|
|
||||||
|
if (!link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = sd_ndisc_router_get_sender_address(rt, &router);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||||
|
|
||||||
|
r = sd_ndisc_router_encrypted_dns_get_lifetime_timestamp(rt, CLOCK_BOOTTIME, &lifetime_usec);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
|
||||||
|
|
||||||
|
r = sd_ndisc_router_encrypted_dns_get_resolver(rt, &res);
|
||||||
|
if (r < 0)
|
||||||
|
return log_link_warning_errno(link, r, "Failed to get encrypted dns resolvers: %m");
|
||||||
|
|
||||||
|
NDiscDNR *dnr, d = { .resolver = *res };
|
||||||
|
if (lifetime_usec == 0) {
|
||||||
|
dnr = set_remove(link->ndisc_dnr, &d);
|
||||||
|
if (dnr) {
|
||||||
|
ndisc_dnr_free(dnr);
|
||||||
|
link_dirty(link);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnr = set_get(link->ndisc_dnr, &d);
|
||||||
|
if (dnr) {
|
||||||
|
dnr->router = router;
|
||||||
|
dnr->lifetime_usec = lifetime_usec;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_entry = new(NDiscDNR, 1);
|
||||||
|
if (!new_entry)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
*new_entry = (NDiscDNR) {
|
||||||
|
.router = router,
|
||||||
|
/* .resolver, */
|
||||||
|
.lifetime_usec = lifetime_usec,
|
||||||
|
};
|
||||||
|
r = sd_dns_resolver_copy(res, &new_entry->resolver);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
/* Not sorted by priority */
|
||||||
|
r = set_ensure_put(&link->ndisc_dnr, &ndisc_dnr_hash_ops, new_entry);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
assert(r > 0);
|
||||||
|
TAKE_PTR(new_entry);
|
||||||
|
|
||||||
|
link_dirty(link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
|
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
|
||||||
size_t n_captive_portal = 0;
|
size_t n_captive_portal = 0;
|
||||||
int r;
|
int r;
|
||||||
|
@ -1879,6 +2021,9 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
|
||||||
case SD_NDISC_OPTION_PREF64:
|
case SD_NDISC_OPTION_PREF64:
|
||||||
r = ndisc_router_process_pref64(link, rt);
|
r = ndisc_router_process_pref64(link, rt);
|
||||||
break;
|
break;
|
||||||
|
case SD_NDISC_OPTION_ENCRYPTED_DNS:
|
||||||
|
r = ndisc_router_process_encrypted_dns(link, rt);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (r < 0 && r != -EBADMSG)
|
if (r < 0 && r != -EBADMSG)
|
||||||
return r;
|
return r;
|
||||||
|
@ -1891,6 +2036,7 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
|
||||||
NDiscRDNSS *rdnss;
|
NDiscRDNSS *rdnss;
|
||||||
NDiscCaptivePortal *cp;
|
NDiscCaptivePortal *cp;
|
||||||
NDiscPREF64 *p64;
|
NDiscPREF64 *p64;
|
||||||
|
NDiscDNR *dnr;
|
||||||
Address *address;
|
Address *address;
|
||||||
Route *route;
|
Route *route;
|
||||||
int r, ret = 0;
|
int r, ret = 0;
|
||||||
|
@ -1989,6 +2135,14 @@ static int ndisc_drop_outdated(Link *link, const struct in6_addr *router, usec_t
|
||||||
* the 'updated' flag. */
|
* the 'updated' flag. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SET_FOREACH(dnr, link->ndisc_dnr) {
|
||||||
|
if (dnr->lifetime_usec > timestamp_usec)
|
||||||
|
continue; /* The resolver is still valid */
|
||||||
|
|
||||||
|
ndisc_dnr_free(set_remove(link->ndisc_dnr, dnr));
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (updated)
|
if (updated)
|
||||||
link_dirty(link);
|
link_dirty(link);
|
||||||
|
|
||||||
|
@ -2016,6 +2170,7 @@ static int ndisc_setup_expire(Link *link) {
|
||||||
NDiscDNSSL *dnssl;
|
NDiscDNSSL *dnssl;
|
||||||
NDiscRDNSS *rdnss;
|
NDiscRDNSS *rdnss;
|
||||||
NDiscPREF64 *p64;
|
NDiscPREF64 *p64;
|
||||||
|
NDiscDNR *dnr;
|
||||||
Address *address;
|
Address *address;
|
||||||
Route *route;
|
Route *route;
|
||||||
int r;
|
int r;
|
||||||
|
@ -2068,6 +2223,9 @@ static int ndisc_setup_expire(Link *link) {
|
||||||
SET_FOREACH(p64, link->ndisc_pref64)
|
SET_FOREACH(p64, link->ndisc_pref64)
|
||||||
lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
|
lifetime_usec = MIN(lifetime_usec, p64->lifetime_usec);
|
||||||
|
|
||||||
|
SET_FOREACH(dnr, link->ndisc_dnr)
|
||||||
|
lifetime_usec = MIN(lifetime_usec, dnr->lifetime_usec);
|
||||||
|
|
||||||
if (lifetime_usec == USEC_INFINITY)
|
if (lifetime_usec == USEC_INFINITY)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -2324,6 +2482,14 @@ static int ndisc_neighbor_handle_router_message(Link *link, sd_ndisc_neighbor *n
|
||||||
p64->router = current_address;
|
p64->router = current_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NDiscDNR *dnr;
|
||||||
|
SET_FOREACH(dnr, link->ndisc_dnr) {
|
||||||
|
if (!in6_addr_equal(&dnr->router, &original_address))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dnr->router = current_address;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2507,7 +2673,7 @@ int ndisc_stop(Link *link) {
|
||||||
void ndisc_flush(Link *link) {
|
void ndisc_flush(Link *link) {
|
||||||
assert(link);
|
assert(link);
|
||||||
|
|
||||||
/* Remove all addresses, routes, RDNSS, DNSSL, and Captive Portal entries, without exception. */
|
/* Remove all addresses, routes, RDNSS, DNSSL, DNR, and Captive Portal entries, without exception. */
|
||||||
(void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
|
(void) ndisc_drop_outdated(link, /* router = */ NULL, /* timestamp_usec = */ USEC_INFINITY);
|
||||||
(void) ndisc_drop_redirect(link, /* router = */ NULL);
|
(void) ndisc_drop_redirect(link, /* router = */ NULL);
|
||||||
|
|
||||||
|
@ -2517,6 +2683,7 @@ void ndisc_flush(Link *link) {
|
||||||
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
|
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
|
||||||
link->ndisc_pref64 = set_free(link->ndisc_pref64);
|
link->ndisc_pref64 = set_free(link->ndisc_pref64);
|
||||||
link->ndisc_redirects = set_free(link->ndisc_redirects);
|
link->ndisc_redirects = set_free(link->ndisc_redirects);
|
||||||
|
link->ndisc_dnr = set_free(link->ndisc_dnr);
|
||||||
link->ndisc_mtu = 0;
|
link->ndisc_mtu = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "conf-parser.h"
|
#include "conf-parser.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
|
|
||||||
typedef struct Address Address;
|
typedef struct Address Address;
|
||||||
|
@ -49,6 +50,12 @@ typedef struct NDiscPREF64 {
|
||||||
struct in6_addr prefix;
|
struct in6_addr prefix;
|
||||||
} NDiscPREF64;
|
} NDiscPREF64;
|
||||||
|
|
||||||
|
typedef struct NDiscDNR {
|
||||||
|
struct in6_addr router;
|
||||||
|
usec_t lifetime_usec;
|
||||||
|
sd_dns_resolver resolver;
|
||||||
|
} NDiscDNR;
|
||||||
|
|
||||||
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
|
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
|
||||||
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
|
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,7 @@ NextHop.Group, config_parse_nexthop_group,
|
||||||
DHCPv4.RequestAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_request_address)
|
DHCPv4.RequestAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_request_address)
|
||||||
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
||||||
DHCPv4.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp_use_dns)
|
DHCPv4.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp_use_dns)
|
||||||
|
DHCPv4.UseDNR, config_parse_tristate, 0, offsetof(Network, dhcp_use_dnr)
|
||||||
DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
|
DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
|
||||||
DHCPv4.UseNTP, config_parse_tristate, 0, offsetof(Network, dhcp_use_ntp)
|
DHCPv4.UseNTP, config_parse_tristate, 0, offsetof(Network, dhcp_use_ntp)
|
||||||
DHCPv4.RoutesToNTP, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_ntp)
|
DHCPv4.RoutesToNTP, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_ntp)
|
||||||
|
@ -278,6 +279,7 @@ DHCPv4.RapidCommit, config_parse_tristate,
|
||||||
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
|
DHCPv6.UseAddress, config_parse_bool, 0, offsetof(Network, dhcp6_use_address)
|
||||||
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
|
DHCPv6.UseDelegatedPrefix, config_parse_bool, 0, offsetof(Network, dhcp6_use_pd_prefix)
|
||||||
DHCPv6.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp6_use_dns)
|
DHCPv6.UseDNS, config_parse_tristate, 0, offsetof(Network, dhcp6_use_dns)
|
||||||
|
DHCPv6.UseDNR, config_parse_tristate, 0, offsetof(Network, dhcp6_use_dnr)
|
||||||
DHCPv6.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp6_use_hostname)
|
DHCPv6.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp6_use_hostname)
|
||||||
DHCPv6.UseDomains, config_parse_use_domains, 0, offsetof(Network, dhcp6_use_domains)
|
DHCPv6.UseDomains, config_parse_use_domains, 0, offsetof(Network, dhcp6_use_domains)
|
||||||
DHCPv6.UseNTP, config_parse_tristate, 0, offsetof(Network, dhcp6_use_ntp)
|
DHCPv6.UseNTP, config_parse_tristate, 0, offsetof(Network, dhcp6_use_ntp)
|
||||||
|
@ -307,6 +309,7 @@ IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool,
|
||||||
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_onlink_prefix)
|
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_onlink_prefix)
|
||||||
IPv6AcceptRA.UsePREF64, config_parse_bool, 0, offsetof(Network, ndisc_use_pref64)
|
IPv6AcceptRA.UsePREF64, config_parse_bool, 0, offsetof(Network, ndisc_use_pref64)
|
||||||
IPv6AcceptRA.UseDNS, config_parse_tristate, 0, offsetof(Network, ndisc_use_dns)
|
IPv6AcceptRA.UseDNS, config_parse_tristate, 0, offsetof(Network, ndisc_use_dns)
|
||||||
|
IPv6AcceptRA.UseDNR, config_parse_tristate, 0, offsetof(Network, ndisc_use_dnr)
|
||||||
IPv6AcceptRA.UseDomains, config_parse_use_domains, 0, offsetof(Network, ndisc_use_domains)
|
IPv6AcceptRA.UseDomains, config_parse_use_domains, 0, offsetof(Network, ndisc_use_domains)
|
||||||
IPv6AcceptRA.UseMTU, config_parse_bool, 0, offsetof(Network, ndisc_use_mtu)
|
IPv6AcceptRA.UseMTU, config_parse_bool, 0, offsetof(Network, ndisc_use_mtu)
|
||||||
IPv6AcceptRA.UseHopLimit, config_parse_bool, 0, offsetof(Network, ndisc_use_hop_limit)
|
IPv6AcceptRA.UseHopLimit, config_parse_bool, 0, offsetof(Network, ndisc_use_hop_limit)
|
||||||
|
|
|
@ -388,6 +388,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||||
.dhcp_use_sip = true,
|
.dhcp_use_sip = true,
|
||||||
.dhcp_use_captive_portal = true,
|
.dhcp_use_captive_portal = true,
|
||||||
.dhcp_use_dns = -1,
|
.dhcp_use_dns = -1,
|
||||||
|
.dhcp_use_dnr = -1,
|
||||||
.dhcp_routes_to_dns = true,
|
.dhcp_routes_to_dns = true,
|
||||||
.dhcp_use_domains = _USE_DOMAINS_INVALID,
|
.dhcp_use_domains = _USE_DOMAINS_INVALID,
|
||||||
.dhcp_use_hostname = true,
|
.dhcp_use_hostname = true,
|
||||||
|
@ -407,6 +408,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||||
.dhcp6_use_address = true,
|
.dhcp6_use_address = true,
|
||||||
.dhcp6_use_pd_prefix = true,
|
.dhcp6_use_pd_prefix = true,
|
||||||
.dhcp6_use_dns = -1,
|
.dhcp6_use_dns = -1,
|
||||||
|
.dhcp6_use_dnr = -1,
|
||||||
.dhcp6_use_domains = _USE_DOMAINS_INVALID,
|
.dhcp6_use_domains = _USE_DOMAINS_INVALID,
|
||||||
.dhcp6_use_hostname = true,
|
.dhcp6_use_hostname = true,
|
||||||
.dhcp6_use_ntp = -1,
|
.dhcp6_use_ntp = -1,
|
||||||
|
@ -482,6 +484,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||||
.ndisc = -1,
|
.ndisc = -1,
|
||||||
.ndisc_use_redirect = true,
|
.ndisc_use_redirect = true,
|
||||||
.ndisc_use_dns = -1,
|
.ndisc_use_dns = -1,
|
||||||
|
.ndisc_use_dnr = -1,
|
||||||
.ndisc_use_gateway = true,
|
.ndisc_use_gateway = true,
|
||||||
.ndisc_use_captive_portal = true,
|
.ndisc_use_captive_portal = true,
|
||||||
.ndisc_use_route_prefix = true,
|
.ndisc_use_route_prefix = true,
|
||||||
|
|
|
@ -153,6 +153,7 @@ struct Network {
|
||||||
int dhcp_ipv6_only_mode;
|
int dhcp_ipv6_only_mode;
|
||||||
int dhcp_use_rapid_commit;
|
int dhcp_use_rapid_commit;
|
||||||
int dhcp_use_dns;
|
int dhcp_use_dns;
|
||||||
|
int dhcp_use_dnr;
|
||||||
bool dhcp_routes_to_dns;
|
bool dhcp_routes_to_dns;
|
||||||
int dhcp_use_ntp;
|
int dhcp_use_ntp;
|
||||||
bool dhcp_routes_to_ntp;
|
bool dhcp_routes_to_ntp;
|
||||||
|
@ -185,6 +186,7 @@ struct Network {
|
||||||
bool dhcp6_send_hostname;
|
bool dhcp6_send_hostname;
|
||||||
bool dhcp6_send_hostname_set;
|
bool dhcp6_send_hostname_set;
|
||||||
int dhcp6_use_dns;
|
int dhcp6_use_dns;
|
||||||
|
int dhcp6_use_dnr;
|
||||||
bool dhcp6_use_hostname;
|
bool dhcp6_use_hostname;
|
||||||
int dhcp6_use_ntp;
|
int dhcp6_use_ntp;
|
||||||
bool dhcp6_use_captive_portal;
|
bool dhcp6_use_captive_portal;
|
||||||
|
@ -343,6 +345,7 @@ struct Network {
|
||||||
|
|
||||||
/* NDisc support */
|
/* NDisc support */
|
||||||
int ndisc;
|
int ndisc;
|
||||||
|
int ndisc_use_dnr;
|
||||||
bool ndisc_use_redirect;
|
bool ndisc_use_redirect;
|
||||||
int ndisc_use_dns;
|
int ndisc_use_dns;
|
||||||
bool ndisc_use_gateway;
|
bool ndisc_use_gateway;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dns-domain.h"
|
#include "dns-domain.h"
|
||||||
|
#include "dns-resolver-internal.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
|
@ -113,6 +114,24 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link->dhcp_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4)) {
|
||||||
|
sd_dns_resolver *resolvers;
|
||||||
|
|
||||||
|
r = sd_dhcp_lease_get_dnr(link->dhcp_lease, &resolvers);
|
||||||
|
if (r >= 0) {
|
||||||
|
struct in_addr_full **dot_servers;
|
||||||
|
size_t n = 0;
|
||||||
|
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||||
|
|
||||||
|
r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
|
if (link->dhcp6_lease && link_get_use_dns(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
|
||||||
const struct in6_addr *addresses;
|
const struct in6_addr *addresses;
|
||||||
|
|
||||||
|
@ -124,6 +143,25 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link->dhcp6_lease && link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6)) {
|
||||||
|
sd_dns_resolver *resolvers;
|
||||||
|
|
||||||
|
r = sd_dhcp6_lease_get_dnr(link->dhcp6_lease, &resolvers);
|
||||||
|
if (r >= 0 ) {
|
||||||
|
struct in_addr_full **dot_servers;
|
||||||
|
size_t n = 0;
|
||||||
|
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||||
|
|
||||||
|
r = dns_resolvers_to_dot_addrs(resolvers, r, &dot_servers, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
|
if (link_get_use_dns(link, NETWORK_CONFIG_SOURCE_NDISC)) {
|
||||||
NDiscRDNSS *a;
|
NDiscRDNSS *a;
|
||||||
|
|
||||||
|
@ -134,6 +172,24 @@ static int link_put_dns(Link *link, OrderedSet **s) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) {
|
||||||
|
NDiscDNR *a;
|
||||||
|
|
||||||
|
SET_FOREACH(a, link->ndisc_dnr) {
|
||||||
|
struct in_addr_full **dot_servers = NULL;
|
||||||
|
size_t n = 0;
|
||||||
|
CLEANUP_ARRAY(dot_servers, n, in_addr_full_array_free);
|
||||||
|
|
||||||
|
r = dns_resolvers_to_dot_addrs(&a->resolver, 1, &dot_servers, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = ordered_set_put_dns_servers(s, link->ifindex, dot_servers, n);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -522,6 +578,60 @@ static void serialize_addresses(
|
||||||
fputc('\n', f);
|
fputc('\n', f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void serialize_resolvers(
|
||||||
|
FILE *f,
|
||||||
|
const char *lvalue,
|
||||||
|
bool *space,
|
||||||
|
sd_dhcp_lease *lease,
|
||||||
|
bool conditional,
|
||||||
|
sd_dhcp6_lease *lease6,
|
||||||
|
bool conditional6) {
|
||||||
|
|
||||||
|
bool _space = false;
|
||||||
|
if (!space)
|
||||||
|
space = &_space;
|
||||||
|
|
||||||
|
if (lvalue)
|
||||||
|
fprintf(f, "%s=", lvalue);
|
||||||
|
|
||||||
|
if (lease && conditional) {
|
||||||
|
sd_dns_resolver *resolvers;
|
||||||
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_dhcp_lease_get_dnr(lease, &resolvers);
|
||||||
|
if (r < 0)
|
||||||
|
return (void) log_warning_errno(r, "Failed to get DNR from DHCP lease, ignoring.");
|
||||||
|
|
||||||
|
r = dns_resolvers_to_dot_strv(resolvers, r, &names);
|
||||||
|
if (r < 0)
|
||||||
|
return (void) log_warning_errno(r, "Failed to get DoT servers from DHCP DNR, ignoring.");
|
||||||
|
if (r > 0)
|
||||||
|
fputstrv(f, names, NULL, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lease6 && conditional6) {
|
||||||
|
sd_dns_resolver *resolvers;
|
||||||
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = sd_dhcp6_lease_get_dnr(lease6, &resolvers);
|
||||||
|
if (r < 0)
|
||||||
|
return (void) log_warning_errno(r, "Failed to get DNR from DHCPv6 lease, ignoring.");
|
||||||
|
|
||||||
|
r = dns_resolvers_to_dot_strv(resolvers, r, &names);
|
||||||
|
if (r < 0)
|
||||||
|
return (void) log_warning_errno(r, "Failed to get DoT servers from DHCPv6 DNR, ignoring.");
|
||||||
|
if (r > 0)
|
||||||
|
fputstrv(f, names, NULL, space);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lvalue)
|
||||||
|
fputc('\n', f);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, UseDomains use_domains) {
|
static void link_save_domains(Link *link, FILE *f, OrderedSet *static_domains, UseDomains use_domains) {
|
||||||
bool space = false;
|
bool space = false;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
@ -665,6 +775,21 @@ static int link_save(Link *link) {
|
||||||
space = false;
|
space = false;
|
||||||
link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
|
link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
|
||||||
|
|
||||||
|
/* DNR resolvers are not required to provide Do53 service, however resolved doesn't
|
||||||
|
* know how to handle such a server so for now Do53 service is required, and
|
||||||
|
* assumed. */
|
||||||
|
serialize_resolvers(f, NULL, &space,
|
||||||
|
link->dhcp_lease,
|
||||||
|
link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP4),
|
||||||
|
link->dhcp6_lease,
|
||||||
|
link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_DHCP6));
|
||||||
|
|
||||||
|
if (link_get_use_dnr(link, NETWORK_CONFIG_SOURCE_NDISC)) {
|
||||||
|
NDiscDNR *dnr;
|
||||||
|
SET_FOREACH(dnr, link->ndisc_dnr)
|
||||||
|
serialize_dnr(f, &dnr->resolver, 1, &space);
|
||||||
|
}
|
||||||
|
|
||||||
serialize_addresses(f, NULL, &space,
|
serialize_addresses(f, NULL, &space,
|
||||||
NULL,
|
NULL,
|
||||||
link->dhcp_lease,
|
link->dhcp_lease,
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
#include "sd-daemon.h"
|
#include "sd-daemon.h"
|
||||||
|
|
||||||
#include "bpf-dlopen.h"
|
#include "bpf-dlopen.h"
|
||||||
|
#if HAVE_VMLINUX_H
|
||||||
#include "bpf-link.h"
|
#include "bpf-link.h"
|
||||||
|
#endif
|
||||||
#include "build-path.h"
|
#include "build-path.h"
|
||||||
#include "common-signal.h"
|
#include "common-signal.h"
|
||||||
#include "env-util.h"
|
#include "env-util.h"
|
||||||
|
|
|
@ -304,19 +304,6 @@ static SubvolumeFlags subvolume_flags_from_string(const char *s) {
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char* subvolume_flags_to_string(SubvolumeFlags flags) {
|
|
||||||
const char *l[CONST_LOG2U(_SUBVOLUME_FLAGS_MASK + 1) + 1]; /* one string per known flag at most */
|
|
||||||
size_t m = 0;
|
|
||||||
|
|
||||||
if (FLAGS_SET(flags, SUBVOLUME_RO))
|
|
||||||
l[m++] = "ro";
|
|
||||||
|
|
||||||
assert(m < ELEMENTSOF(l));
|
|
||||||
l[m] = NULL;
|
|
||||||
|
|
||||||
return strv_join((char**) l, ",");
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct Subvolume {
|
typedef struct Subvolume {
|
||||||
char *path;
|
char *path;
|
||||||
SubvolumeFlags flags;
|
SubvolumeFlags flags;
|
||||||
|
@ -2438,6 +2425,14 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
||||||
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
|
"SizeMinBytes=/SizeMaxBytes= cannot be used with Verity=%s.",
|
||||||
verity_mode_to_string(p->verity));
|
verity_mode_to_string(p->verity));
|
||||||
|
|
||||||
|
if (!ordered_hashmap_isempty(p->subvolumes) && arg_offline > 0)
|
||||||
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
|
"Subvolumes= cannot be used with --offline=yes.");
|
||||||
|
|
||||||
|
if (p->default_subvolume && arg_offline > 0)
|
||||||
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
|
"DefaultSubvolume= cannot be used with --offline=yes.");
|
||||||
|
|
||||||
if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
|
if (p->default_subvolume && !ordered_hashmap_contains(p->subvolumes, p->default_subvolume))
|
||||||
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
|
||||||
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
"DefaultSubvolume= must be one of the paths in Subvolumes=.");
|
||||||
|
@ -4286,7 +4281,7 @@ static int prepare_temporary_file(Context *context, PartitionTarget *t, uint64_t
|
||||||
|
|
||||||
static bool loop_device_error_is_fatal(const Partition *p, int r) {
|
static bool loop_device_error_is_fatal(const Partition *p, int r) {
|
||||||
assert(p);
|
assert(p);
|
||||||
return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r));
|
return arg_offline == 0 || (r != -ENOENT && !ERRNO_IS_PRIVILEGE(r)) || !ordered_hashmap_isempty(p->subvolumes) || p->default_subvolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int partition_target_prepare(
|
static int partition_target_prepare(
|
||||||
|
@ -5793,38 +5788,6 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int append_btrfs_subvols(char ***l, OrderedHashmap *subvolumes, const char *default_subvolume) {
|
|
||||||
Subvolume *subvolume;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
assert(l);
|
|
||||||
|
|
||||||
ORDERED_HASHMAP_FOREACH(subvolume, subvolumes) {
|
|
||||||
_cleanup_free_ char *s = NULL, *f = NULL;
|
|
||||||
|
|
||||||
s = strdup(subvolume->path);
|
|
||||||
if (!s)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
f = subvolume_flags_to_string(subvolume->flags);
|
|
||||||
if (!f)
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (streq_ptr(subvolume->path, default_subvolume) &&
|
|
||||||
!strextend_with_separator(&f, ",", "default"))
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
if (!isempty(f) && !strextend_with_separator(&s, ":", f))
|
|
||||||
return log_oom();
|
|
||||||
|
|
||||||
r = strv_extend_many(l, "--subvol", s);
|
|
||||||
if (r < 0)
|
|
||||||
return log_oom();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
static int finalize_extra_mkfs_options(const Partition *p, const char *root, char ***ret) {
|
||||||
_cleanup_strv_free_ char **sv = NULL;
|
_cleanup_strv_free_ char **sv = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -5838,18 +5801,6 @@ static int finalize_extra_mkfs_options(const Partition *p, const char *root, cha
|
||||||
"Failed to determine mkfs command line options for '%s': %m",
|
"Failed to determine mkfs command line options for '%s': %m",
|
||||||
p->format);
|
p->format);
|
||||||
|
|
||||||
if (partition_needs_populate(p) && root && streq(p->format, "btrfs")) {
|
|
||||||
r = append_btrfs_subvols(&sv, p->subvolumes, p->default_subvolume);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (p->suppressing) {
|
|
||||||
r = append_btrfs_subvols(&sv, p->suppressing->subvolumes, NULL);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ret = TAKE_PTR(sv);
|
*ret = TAKE_PTR(sv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2881,6 +2881,27 @@ size_t dns_packet_size_unfragmented(DnsPacket *p) {
|
||||||
return LESS_BY(p->fragsize, udp_header_size(p->family));
|
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] = {
|
static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
|
||||||
[DNS_RCODE_SUCCESS] = "SUCCESS",
|
[DNS_RCODE_SUCCESS] = "SUCCESS",
|
||||||
[DNS_RCODE_FORMERR] = "FORMERR",
|
[DNS_RCODE_FORMERR] = "FORMERR",
|
||||||
|
@ -2955,27 +2976,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);
|
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] = {
|
static const char* const dns_protocol_table[_DNS_PROTOCOL_MAX] = {
|
||||||
[DNS_PROTOCOL_DNS] = "dns",
|
[DNS_PROTOCOL_DNS] = "dns",
|
||||||
[DNS_PROTOCOL_MDNS] = "mdns",
|
[DNS_PROTOCOL_MDNS] = "mdns",
|
||||||
|
|
|
@ -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_;
|
const char* dns_protocol_to_string(DnsProtocol p) _const_;
|
||||||
DnsProtocol dns_protocol_from_string(const char *s) _pure_;
|
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_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 } })
|
#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 } })
|
||||||
|
|
||||||
|
@ -389,6 +370,25 @@ const char* format_dns_svc_param_key(uint16_t i, char buf[static DECIMAL_STR_MAX
|
||||||
|
|
||||||
extern const struct hash_ops dns_packet_hash_ops;
|
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(
|
static inline uint64_t SD_RESOLVED_FLAGS_MAKE(
|
||||||
DnsProtocol protocol,
|
DnsProtocol protocol,
|
||||||
int family,
|
int family,
|
||||||
|
|
|
@ -905,6 +905,75 @@ int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, boo
|
||||||
return out - buffer;
|
return out - buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Decode a domain name according to RFC 1035 Section 3.1, without compression */
|
||||||
|
int dns_name_from_wire_format(const uint8_t **data, size_t *len, char **ret) {
|
||||||
|
_cleanup_free_ char *domain = NULL;
|
||||||
|
const uint8_t *optval;
|
||||||
|
size_t optlen, n = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(data);
|
||||||
|
assert(len);
|
||||||
|
assert(*data || *len == 0);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
optval = *data;
|
||||||
|
optlen = *len;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *label;
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
/* Unterminated name */
|
||||||
|
if (optlen == 0)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
/* RFC 1035 § 3.1 total length of encoded name is limited to 255 octets */
|
||||||
|
if (*len - optlen > 255)
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
c = *optval;
|
||||||
|
optval++;
|
||||||
|
optlen--;
|
||||||
|
|
||||||
|
if (c == 0)
|
||||||
|
/* End label */
|
||||||
|
break;
|
||||||
|
if (c > DNS_LABEL_MAX)
|
||||||
|
return -EBADMSG;
|
||||||
|
if (c > optlen)
|
||||||
|
return -EMSGSIZE;
|
||||||
|
|
||||||
|
/* Literal label */
|
||||||
|
label = (const char*) optval;
|
||||||
|
optval += c;
|
||||||
|
optlen -= c;
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (n != 0)
|
||||||
|
domain[n++] = '.';
|
||||||
|
|
||||||
|
r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
n += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(domain, n + 1))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
domain[n] = '\0';
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(domain);
|
||||||
|
*data = optval;
|
||||||
|
*len = optlen;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
static bool srv_type_label_is_valid(const char *label, size_t n) {
|
static bool srv_type_label_is_valid(const char *label, size_t n) {
|
||||||
assert(label);
|
assert(label);
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,7 @@ bool dns_name_is_root(const char *name);
|
||||||
bool dns_name_is_single_label(const char *name);
|
bool dns_name_is_single_label(const char *name);
|
||||||
|
|
||||||
int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical);
|
int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len, bool canonical);
|
||||||
|
int dns_name_from_wire_format(const uint8_t **data, size_t *len, char **ret);
|
||||||
|
|
||||||
bool dns_srv_type_is_valid(const char *name);
|
bool dns_srv_type_is_valid(const char *name);
|
||||||
bool dnssd_srv_type_is_valid(const char *name);
|
bool dnssd_srv_type_is_valid(const char *name);
|
||||||
|
|
|
@ -347,6 +347,15 @@ struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
|
||||||
return mfree(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 in_addr_full_new(
|
||||||
int family,
|
int family,
|
||||||
const union in_addr_union *a,
|
const union in_addr_union *a,
|
||||||
|
|
|
@ -39,6 +39,7 @@ struct in_addr_full {
|
||||||
|
|
||||||
struct in_addr_full *in_addr_full_free(struct in_addr_full *a);
|
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);
|
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(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);
|
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);
|
const char* in_addr_full_to_string(struct in_addr_full *a);
|
||||||
|
|
|
@ -32,6 +32,7 @@ _SD_BEGIN_DECLARATIONS;
|
||||||
|
|
||||||
typedef struct sd_dhcp_lease sd_dhcp_lease;
|
typedef struct sd_dhcp_lease sd_dhcp_lease;
|
||||||
typedef struct sd_dhcp_route sd_dhcp_route;
|
typedef struct sd_dhcp_route sd_dhcp_route;
|
||||||
|
typedef struct sd_dns_resolver sd_dns_resolver;
|
||||||
|
|
||||||
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
|
sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
|
||||||
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
|
sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
|
||||||
|
@ -75,6 +76,7 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
|
||||||
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
|
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
|
||||||
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
|
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
|
||||||
int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **captive_portal);
|
int sd_dhcp_lease_get_captive_portal(sd_dhcp_lease *lease, const char **captive_portal);
|
||||||
|
int sd_dhcp_lease_get_dnr(sd_dhcp_lease *lease, sd_dns_resolver **ret_resolvers);
|
||||||
int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
||||||
int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
||||||
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
|
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
|
||||||
|
|
|
@ -171,7 +171,8 @@ enum {
|
||||||
SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */
|
SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */
|
||||||
/* option code 160 is unassigned [RFC7710][RFC8910] */
|
/* option code 160 is unassigned [RFC7710][RFC8910] */
|
||||||
SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */
|
SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */
|
||||||
/* option codes 162-174 are unassigned [RFC3942] */
|
SD_DHCP_OPTION_V4_DNR = 162, /* [RFC9463] */
|
||||||
|
/* option codes 163-174 are unassigned [RFC3942] */
|
||||||
/* option codes 175-177 are temporary assigned. */
|
/* option codes 175-177 are temporary assigned. */
|
||||||
/* option codes 178-207 are unassigned [RFC3942] */
|
/* option codes 178-207 are unassigned [RFC3942] */
|
||||||
SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */
|
SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
_SD_BEGIN_DECLARATIONS;
|
_SD_BEGIN_DECLARATIONS;
|
||||||
|
|
||||||
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
|
typedef struct sd_dhcp6_lease sd_dhcp6_lease;
|
||||||
|
typedef struct sd_dns_resolver sd_dns_resolver;
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
|
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
|
||||||
int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
|
int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
|
||||||
|
@ -74,6 +75,7 @@ int sd_dhcp6_lease_get_pd_lifetime_timestamp(
|
||||||
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
|
int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
|
||||||
|
|
||||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
||||||
|
int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret);
|
||||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
|
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
|
||||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
|
||||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
|
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
|
||||||
|
|
|
@ -165,8 +165,9 @@ enum {
|
||||||
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
|
SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */
|
||||||
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
|
SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */
|
||||||
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
|
SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */
|
||||||
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */
|
SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */
|
||||||
/* option codes 144-65535 are unassigned */
|
SD_DHCP6_OPTION_V6_DNR = 144 /* RFC 9463 */
|
||||||
|
/* option codes 145-65535 are unassigned */
|
||||||
};
|
};
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
_SD_END_DECLARATIONS;
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
_SD_BEGIN_DECLARATIONS;
|
_SD_BEGIN_DECLARATIONS;
|
||||||
|
|
||||||
typedef struct sd_ndisc_router sd_ndisc_router;
|
typedef struct sd_ndisc_router sd_ndisc_router;
|
||||||
|
typedef struct sd_dns_resolver sd_dns_resolver;
|
||||||
|
|
||||||
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
|
sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
|
||||||
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
|
sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);
|
||||||
|
@ -87,6 +88,11 @@ int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, uint8_t *ret);
|
||||||
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
|
int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
|
||||||
int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
|
int sd_ndisc_router_prefix64_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
|
||||||
|
|
||||||
|
/* Specific option access: SD_NDISC_OPTION_ENCRYPTED_DNS */
|
||||||
|
int sd_ndisc_router_encrypted_dns_get_resolver(sd_ndisc_router *rt, sd_dns_resolver **ret);
|
||||||
|
int sd_ndisc_router_encrypted_dns_get_lifetime(sd_ndisc_router *rt, uint64_t *ret);
|
||||||
|
int sd_ndisc_router_encrypted_dns_get_lifetime_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret);
|
||||||
|
|
||||||
_SD_END_DECLARATIONS;
|
_SD_END_DECLARATIONS;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -119,6 +119,95 @@ TEST(dns_name_to_wire_format) {
|
||||||
test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", out4, sizeof(out4), sizeof(out4));
|
test_dns_name_to_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", out4, sizeof(out4), sizeof(out4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_dns_name_from_wire_format_one(const char *expect, const uint8_t *what, size_t len, int ret) {
|
||||||
|
_cleanup_free_ char *name = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
log_info("%s, %s, %zu, →%d", what, strnull(expect), len, ret);
|
||||||
|
|
||||||
|
r = dns_name_from_wire_format(&what, &len, &name);
|
||||||
|
assert_se(r == ret);
|
||||||
|
|
||||||
|
if (r >= 0) {
|
||||||
|
assert(expect); /* for gcc */
|
||||||
|
assert_se(memcmp(name, expect, r) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(dns_name_from_wire_format) {
|
||||||
|
static const uint8_t in0[] = { 0 };
|
||||||
|
static const uint8_t in1[] = { 3, 'f', 'o', 'o', 0 };
|
||||||
|
static const uint8_t in2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
|
||||||
|
static const uint8_t in2_1[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 0, 'b', 'a', 'r', 0 };
|
||||||
|
static const uint8_t in3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 };
|
||||||
|
static const uint8_t in4[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
3, 'a', '1', '2', 0 }; /* 255 octets */
|
||||||
|
static const uint8_t in5[] = { 9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
9, 'a', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||||
|
3, 'a', '1', '2', 0 }; /* 265 octets */
|
||||||
|
|
||||||
|
test_dns_name_from_wire_format_one("", in0, sizeof(in0), strlen(""));
|
||||||
|
|
||||||
|
test_dns_name_from_wire_format_one("foo", in1, sizeof(in1), strlen("foo"));
|
||||||
|
test_dns_name_from_wire_format_one(NULL, in1, sizeof(in1) - 1, -EBADMSG);
|
||||||
|
|
||||||
|
test_dns_name_from_wire_format_one("hallo.foo.bar", in2, sizeof(in2), strlen("hallo.foo.bar"));
|
||||||
|
test_dns_name_from_wire_format_one("hallo.foo", in2_1, sizeof(in2_1), strlen("hallo.foo"));
|
||||||
|
|
||||||
|
test_dns_name_from_wire_format_one("\\032foo.bar", in3, sizeof(in3), strlen("\\032foo.bar"));
|
||||||
|
|
||||||
|
test_dns_name_from_wire_format_one(NULL, in5, sizeof(in5), -EMSGSIZE);
|
||||||
|
test_dns_name_from_wire_format_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", in4, sizeof(in4), 253);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
|
static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) {
|
||||||
char buffer[buffer_sz];
|
char buffer[buffer_sz];
|
||||||
const char *label;
|
const char *label;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -17,8 +17,10 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
import datetime
|
||||||
|
import enum
|
||||||
import errno
|
import errno
|
||||||
import itertools
|
import itertools
|
||||||
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
@ -27,6 +29,7 @@ import re
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import socket
|
import socket
|
||||||
|
import struct
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
@ -742,6 +745,54 @@ def stop_by_pid_file(pid_file):
|
||||||
print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
|
print(f"Unexpected exception when waiting for {pid} to die: {e.errno}")
|
||||||
rm_f(pid_file)
|
rm_f(pid_file)
|
||||||
|
|
||||||
|
def dnr_v4_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
|
||||||
|
b = bytes()
|
||||||
|
pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
|
||||||
|
pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
|
||||||
|
ipv4 = ipaddress.IPv4Address
|
||||||
|
class SvcParam(enum.Enum):
|
||||||
|
ALPN = 1
|
||||||
|
DOHPATH = 7
|
||||||
|
|
||||||
|
data = pyton(prio)
|
||||||
|
|
||||||
|
adn = adn.rstrip('.') + '.'
|
||||||
|
data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')))
|
||||||
|
|
||||||
|
if not addrs: # adn-only mode
|
||||||
|
return pack(data, 2)
|
||||||
|
|
||||||
|
data += pack(b.join(ipv4(addr).packed for addr in addrs))
|
||||||
|
data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
|
||||||
|
if dohpath is not None:
|
||||||
|
data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
|
||||||
|
|
||||||
|
return pack(data, 2)
|
||||||
|
|
||||||
|
def dnr_v6_instance_data(adn, addrs=None, prio=1, alpns=("dot",), dohpath=None):
|
||||||
|
b = bytes()
|
||||||
|
pack = lambda c, w=1: struct.pack('>' + "_BH_I"[w], len(c)) + c
|
||||||
|
pyton = lambda n, w=2: struct.pack('>' + "_BH_I"[w], n)
|
||||||
|
ipv6 = ipaddress.IPv6Address
|
||||||
|
class SvcParam(enum.Enum):
|
||||||
|
ALPN = 1
|
||||||
|
DOHPATH = 7
|
||||||
|
|
||||||
|
data = pyton(prio)
|
||||||
|
|
||||||
|
adn = adn.rstrip('.') + '.'
|
||||||
|
data += pack(b.join(pack(label.encode('ascii')) for label in adn.split('.')), 2)
|
||||||
|
|
||||||
|
if not addrs: # adn-only mode
|
||||||
|
return data
|
||||||
|
|
||||||
|
data += pack(b.join(ipv6(addr).packed for addr in addrs), 2)
|
||||||
|
data += pyton(SvcParam.ALPN.value) + pack(b.join(pack(alpn.encode('ascii')) for alpn in alpns), 2)
|
||||||
|
if dohpath is not None:
|
||||||
|
data += pyton(SvcParam.DOHPATH.value) + pack(dohpath.encode('utf-8'), 2)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
def start_dnsmasq(*additional_options, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
|
def start_dnsmasq(*additional_options, interface='veth-peer', ra_mode=None, ipv4_range='192.168.5.10,192.168.5.200', ipv4_router='192.168.5.1', ipv6_range='2600::10,2600::20'):
|
||||||
if ra_mode:
|
if ra_mode:
|
||||||
ra_mode = f',{ra_mode}'
|
ra_mode = f',{ra_mode}'
|
||||||
|
@ -7253,6 +7304,56 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
||||||
check(self, False, False, True)
|
check(self, False, False, True)
|
||||||
check(self, False, False, False)
|
check(self, False, False, False)
|
||||||
|
|
||||||
|
def test_dhcp_client_use_dnr(self):
|
||||||
|
def check(self, ipv4, ipv6):
|
||||||
|
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
||||||
|
with open(os.path.join(network_unit_dir, '25-dhcp-client.network.d/override.conf'), mode='w', encoding='utf-8') as f:
|
||||||
|
f.write('[DHCPv4]\nUseDNS=')
|
||||||
|
f.write('yes' if ipv4 else 'no')
|
||||||
|
f.write('\n[DHCPv6]\nUseDNS=')
|
||||||
|
f.write('yes' if ipv6 else 'no')
|
||||||
|
f.write('\n[IPv6AcceptRA]\nUseDNS=no')
|
||||||
|
|
||||||
|
networkctl_reload()
|
||||||
|
self.wait_online('veth99:routable')
|
||||||
|
|
||||||
|
# link becomes 'routable' when at least one protocol provide an valid address. Hence, we need to explicitly wait for both addresses.
|
||||||
|
self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 metric 1024 brd 192.168.5.255 scope global dynamic', ipv='-4')
|
||||||
|
self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
|
||||||
|
|
||||||
|
# make resolved re-read the link state file
|
||||||
|
resolvectl('revert', 'veth99')
|
||||||
|
|
||||||
|
output = resolvectl('dns', 'veth99')
|
||||||
|
print(output)
|
||||||
|
if ipv4:
|
||||||
|
self.assertIn('8.8.8.8#dns.google', output)
|
||||||
|
self.assertIn('0.7.4.2#homer.simpson', output)
|
||||||
|
else:
|
||||||
|
self.assertNotIn('8.8.8.8#dns.google', output)
|
||||||
|
if ipv6:
|
||||||
|
self.assertIn('2001:4860:4860::8888#dns.google', output)
|
||||||
|
else:
|
||||||
|
self.assertNotIn('2001:4860:4860::8888#dns.google', output)
|
||||||
|
|
||||||
|
check_json(networkctl_json())
|
||||||
|
|
||||||
|
copy_network_unit('25-veth.netdev', '25-dhcp-server-veth-peer.network', '25-dhcp-client.network', copy_dropins=False)
|
||||||
|
|
||||||
|
start_networkd()
|
||||||
|
self.wait_online('veth-peer:carrier')
|
||||||
|
dnr_v4 = dnr_v4_instance_data(adn = "dns.google", addrs = ["8.8.8.8", "8.8.4.4"])
|
||||||
|
dnr_v4 += dnr_v4_instance_data(adn = "homer.simpson", addrs = ["0.7.4.2"], alpns = ("dot","h2","h3"), dohpath = "/springfield{?dns}")
|
||||||
|
dnr_v6 = dnr_v6_instance_data(adn = "dns.google", addrs = ["2001:4860:4860::8888", "2001:4860:4860::8844"])
|
||||||
|
masq = lambda bs: ':'.join(f"{b:02x}" for b in bs)
|
||||||
|
start_dnsmasq(f'--dhcp-option=162,{masq(dnr_v4)}',
|
||||||
|
f'--dhcp-option=option6:144,{masq(dnr_v6)}')
|
||||||
|
|
||||||
|
check(self, True, True)
|
||||||
|
check(self, True, False)
|
||||||
|
check(self, False, True)
|
||||||
|
check(self, False, False)
|
||||||
|
|
||||||
def test_dhcp_client_use_captive_portal(self):
|
def test_dhcp_client_use_captive_portal(self):
|
||||||
def check(self, ipv4, ipv6):
|
def check(self, ipv4, ipv6):
|
||||||
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
os.makedirs(os.path.join(network_unit_dir, '25-dhcp-client.network.d'), exist_ok=True)
|
||||||
|
|
Loading…
Reference in New Issue