Compare commits

...

6 Commits

Author SHA1 Message Date
Raul Cheleguini 4c39482e38
Merge 9173c11fe4 into 5261c521e3 2024-11-08 13:49:33 +01:00
Yu Watanabe 5261c521e3 mount-util: make path_get_mount_info() work arbitrary inode
Follow-up for d49d95df0a.
Replaces 9a032ec55a.
Fixes #35075.
2024-11-08 13:25:17 +01:00
Franck Bui 514d9e1665 test: install integration-test-setup.sh in testdata/
integration-test-setup.sh is an auxiliary script that tests rely on at
runtime. As such, install the script in testdata/.

Follow-up for af153e36ae.
2024-11-08 12:37:40 +01:00
Lennart Poettering b480a4c15e update TODO 2024-11-08 10:10:11 +01:00
Lennart Poettering af3baf174a fs-util: add comment about XO_NOCOW 2024-11-08 09:21:25 +01:00
Raul Cheleguini 9173c11fe4 resolved: Add basic support for DNS-over-HTTPS (RFC 8484)
Add support for DNS-over-HTTPS (DoH) to resolved. The feature can be enabled
through the option DNSOverHTTPS. The implementation takes advantage of the
curl-util used by import tool, for that we move the curl-util to src/basic and
adjust import tool build settings.
2024-11-07 00:48:51 -03:00
24 changed files with 496 additions and 83 deletions

25
TODO
View File

@ -129,6 +129,10 @@ Deprecations and removals:
Features:
* format-table: introduce new cell type for strings with ansi sequences in
them. display them in regular output mode (via strip_tab_ansi()), but
suppress them in json mode.
* machined: when registering a machine, also take a relative cgroup path,
relative to the machine's unit. This is useful when registering unpriv
machines, as they might sit down the cgroup tree, below a cgroup delegation
@ -217,12 +221,8 @@ Features:
services where mount propagation from the root fs is off, an still have
confext/sysext propagated in.
* support F_DUDFD_QUERY for comparing fds in same_fd (requires kernel 6.10)
* generic interface for varlink for setting log level and stuff that all our daemons can implement
* use pty ioctl to get peer wherever possible (TIOCGPTPEER)
* maybe teach repart.d/ dropins a new setting MakeMountNodes= or so, which is
just like MakeDirectories=, but uses an access mode of 0000 and sets the +i
chattr bit. This is useful as protection against early uses of /var/ or /tmp/
@ -253,8 +253,6 @@ Features:
* initrd: when transitioning from initrd to host, validate that
/lib/modules/`uname -r` exists, refuse otherwise
* tmpfiles: add "owning" flag for lines that limits effect of --purge
* signed bpf loading: to address need for signature verification for bpf
programs when they are loaded, and given the bpf folks don't think this is
realistic in kernel space, maybe add small daemon that facilitates this
@ -458,9 +456,6 @@ Features:
* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
64bit mount ids
* use udev rule networkd ownership property to take ownership of network
interfaces nspawn creates
* mountfsd/nsresourced
- userdb: maybe allow callers to map one uid to their own uid
- bpflsm: allow writes if resulting UID on disk would be userns' owner UID
@ -647,6 +642,7 @@ Features:
- openpt_allocate_in_namespace()
- unit_attach_pid_to_cgroup_via_bus()
- cg_attach() requires new kernel feature
- journald's process cache
* ddi must be listed as block device fstype
@ -1470,9 +1466,6 @@ Features:
* in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix)
* DynamicUser= + StateDirectory= → use uid mapping mounts, too, in order to
make dirs appear under right UID.
* systemd-sysext: optionally, run it in initrd already, before transitioning
into host, to open up possibility for services shipped like that.
@ -1644,14 +1637,6 @@ Features:
* maybe add kernel cmdline params: to force random seed crediting
* introduce a new per-process uuid, similar to the boot id, the machine id, the
invocation id, that is derived from process creds, specifically a hashed
combination of AT_RANDOM + getpid() + the starttime from
/proc/self/status. Then add these ids implicitly when logging. Deriving this
uuid from these three things has the benefit that it can be derived easily
from /proc/$PID/ in a stable, and unique way that changes on both fork() and
exec().
* let's not GC a unit while its ratelimits are still pending
* when killing due to service watchdog timeout maybe detect whether target

View File

@ -1579,6 +1579,29 @@ conf.set('DEFAULT_DNS_OVER_TLS_MODE',
'DNS_OVER_TLS_' + default_dns_over_tls.underscorify().to_upper())
conf.set_quoted('DEFAULT_DNS_OVER_TLS_MODE_STR', default_dns_over_tls)
dns_over_https = get_option('dns-over-https')
if dns_over_https != 'false'
have = true
if conf.get('HAVE_LIBCURL') == 0
message('DNS-over-HTTPS support depends on libcurl, but dependencies are not available')
have = false
endif
if conf.get('HAVE_OPENSSL') == 0
message('openssl required, but not available')
have = false
endif
endif
conf.set10('ENABLE_DNS_OVER_HTTPS', have)
default_dns_over_https = get_option('default-dns-over-https')
if default_dns_over_https != 'no' and conf.get('ENABLE_DNS_OVER_HTTPS') == 0
message('default-dns-over-https cannot be enabled. Setting default-dns-over-https to no.')
default_dns_over_https = 'no'
endif
conf.set('DEFAULT_DNS_OVER_HTTPS_MODE',
'DNS_OVER_HTTPS_' + default_dns_over_https.underscorify().to_upper())
conf.set_quoted('DEFAULT_DNS_OVER_HTTPS_MODE_STR', default_dns_over_https)
default_mdns = get_option('default-mdns')
conf.set('DEFAULT_MDNS_MODE',
'RESOLVE_SUPPORT_' + default_mdns.to_upper())
@ -2991,6 +3014,7 @@ summary({
'default compression method' : compression,
'default DNSSEC mode' : default_dnssec,
'default DNS-over-TLS mode' : default_dns_over_tls,
'default DNS-over-HTTPS mode' : default_dns_over_https,
'default mDNS mode' : default_mdns,
'default LLMNR mode' : default_llmnr,
'default DNS servers' : dns_servers.split(' '),

View File

@ -353,6 +353,10 @@ option('default-dns-over-tls', type : 'combo',
description : 'default DNS-over-TLS mode',
choices : ['yes', 'opportunistic', 'no'],
value : 'no')
option('default-dns-over-https', type : 'combo',
description : 'default DNS-over-HTTPS mode',
choices : ['yes', 'no'],
value : 'no')
option('default-mdns', type : 'combo',
choices : ['yes', 'resolve', 'no'],
description : 'default MulticastDNS mode',
@ -363,6 +367,8 @@ option('default-llmnr', type : 'combo',
value : 'yes')
option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
description : 'DNS-over-TLS support')
option('dns-over-https', type : 'combo', choices : ['true', 'false'],
description : 'DNS-over-HTTPS support')
option('dns-servers', type : 'string',
description : 'space-separated list of default DNS servers',
value : '1.1.1.1#cloudflare-dns.com 8.8.8.8#dns.google 1.0.0.1#cloudflare-dns.com 8.8.4.4#dns.google 2606:4700:4700::1111#cloudflare-dns.com 2001:4860:4860::8888#dns.google 2606:4700:4700::1001#cloudflare-dns.com 2001:4860:4860::8844#dns.google')

View File

@ -1131,6 +1131,8 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
* If O_CREAT is used with XO_LABEL, any created file will be immediately relabelled.
*
* If the path is specified NULL or empty, behaves like fd_reopen().
*
* If XO_NOCOW is specified will turn on the NOCOW btrfs flag on the file, if available.
*/
if (isempty(path)) {

View File

@ -14,7 +14,6 @@ systemd_pull_sources = files(
'pull-tar.c',
'pull-job.c',
'pull-common.c',
'curl-util.c',
)
systemd_import_sources = files(

View File

@ -117,6 +117,10 @@ if conf.get('ENABLE_DNS_OVER_TLS') == 1
endif
endif
if conf.get('ENABLE_DNS_OVER_HTTPS') == 1
systemd_resolved_dependencies += libcurl
endif
link_with = [
libshared,
libsystemd_resolve_core,

View File

@ -422,10 +422,17 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
/* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything
* better than EDNS0, hence don't even try. */
if (dns_server_get_dnssec_mode(s) != DNSSEC_NO)
if (dns_server_get_dnssec_mode(s) != DNSSEC_NO) {
best = dns_server_get_dns_over_tls_mode(s) == DNS_OVER_TLS_NO ?
DNS_SERVER_FEATURE_LEVEL_DO :
DNS_SERVER_FEATURE_LEVEL_TLS_DO;
/* TODO: Add HTTPS_PLAIN_DO too? */
#if ENABLE_DNS_OVER_HTTPS
best = dns_server_get_dns_over_https_mode(s) == DNS_OVER_HTTPS_NO ?
DNS_SERVER_FEATURE_LEVEL_DO :
DNS_SERVER_FEATURE_LEVEL_HTTPS_PLAIN;
#endif
}
else
best = dns_server_get_dns_over_tls_mode(s) == DNS_OVER_TLS_NO ?
DNS_SERVER_FEATURE_LEVEL_EDNS0 :
@ -493,7 +500,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
} else if (s->packet_bad_opt &&
DNS_SERVER_FEATURE_LEVEL_IS_EDNS0(s->possible_feature_level) &&
dns_server_get_dnssec_mode(s) != DNSSEC_YES &&
dns_server_get_dns_over_tls_mode(s) != DNS_OVER_TLS_YES) {
dns_server_get_dns_over_tls_mode(s) != DNS_OVER_TLS_YES &&
dns_server_get_dns_over_https_mode(s) != DNS_OVER_HTTPS_YES) {
/* A reply to one of our EDNS0 queries didn't carry a valid OPT RR, then downgrade to
* below EDNS0 levels. After all, some servers generate different responses with and
@ -962,6 +970,12 @@ DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s) {
return manager_get_dns_over_tls_mode(s->manager);
}
DnsOverHttpsMode dns_server_get_dns_over_https_mode(DnsServer *s) {
assert(s);
return manager_get_dns_over_https_mode(s->manager);
}
void dns_server_flush_cache(DnsServer *s) {
DnsServer *current;
DnsScope *scope;
@ -1095,12 +1109,13 @@ static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(dns_server_type, DnsServerType);
static const char* const dns_server_feature_level_table[_DNS_SERVER_FEATURE_LEVEL_MAX] = {
[DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
[DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
[DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN] = "TLS+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
[DNS_SERVER_FEATURE_LEVEL_TLS_DO] = "TLS+EDNS0+DO",
[DNS_SERVER_FEATURE_LEVEL_TCP] = "TCP",
[DNS_SERVER_FEATURE_LEVEL_UDP] = "UDP",
[DNS_SERVER_FEATURE_LEVEL_EDNS0] = "UDP+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN] = "TLS+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_HTTPS_PLAIN] = "HTTPS+EDNS0",
[DNS_SERVER_FEATURE_LEVEL_DO] = "UDP+EDNS0+DO",
[DNS_SERVER_FEATURE_LEVEL_TLS_DO] = "TLS+EDNS0+DO",
};
DEFINE_STRING_TABLE_LOOKUP(dns_server_feature_level, DnsServerFeatureLevel);

View File

@ -35,6 +35,7 @@ typedef enum DnsServerFeatureLevel {
DNS_SERVER_FEATURE_LEVEL_UDP,
DNS_SERVER_FEATURE_LEVEL_EDNS0,
DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN,
DNS_SERVER_FEATURE_LEVEL_HTTPS_PLAIN,
DNS_SERVER_FEATURE_LEVEL_DO,
DNS_SERVER_FEATURE_LEVEL_TLS_DO,
_DNS_SERVER_FEATURE_LEVEL_MAX,
@ -46,6 +47,7 @@ typedef enum DnsServerFeatureLevel {
#define DNS_SERVER_FEATURE_LEVEL_IS_EDNS0(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_EDNS0)
#define DNS_SERVER_FEATURE_LEVEL_IS_TLS(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN, DNS_SERVER_FEATURE_LEVEL_TLS_DO)
#define DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_DO)
#define DNS_SERVER_FEATURE_LEVEL_IS_HTTPS(x) ((x) == DNS_SERVER_FEATURE_LEVEL_HTTPS_PLAIN)
#define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO)
const char* dns_server_feature_level_to_string(DnsServerFeatureLevel i) _const_;
@ -164,6 +166,7 @@ void manager_next_dns_server(Manager *m, DnsServer *if_current);
DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s);
DnsOverHttpsMode dns_server_get_dns_over_https_mode(DnsServer *s);
size_t dns_server_get_mtu(DnsServer *s);

View File

@ -4,11 +4,13 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dns-domain.h"
#include "errno-list.h"
#include "errno-util.h"
#include "fd-util.h"
#include "glyph-util.h"
#include "hexdecoct.h"
#include "random-util.h"
#include "resolved-dns-cache.h"
#include "resolved-dns-transaction.h"
@ -16,6 +18,10 @@
#include "resolved-llmnr.h"
#include "string-table.h"
#if ENABLE_DNS_OVER_HTTPS
#include "curl-util.h"
#endif
#define TRANSACTIONS_MAX 4096
#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC)
@ -682,7 +688,13 @@ static uint16_t dns_transaction_port(DnsTransaction *t) {
if (t->server->port > 0)
return t->server->port;
return DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53;
if (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level))
return 853;
if (DNS_SERVER_FEATURE_LEVEL_IS_HTTPS(t->current_feature_level))
return 443;
return 53;
}
static int dns_transaction_emit_tcp(DnsTransaction *t) {
@ -1518,6 +1530,9 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP || DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level))
return -EAGAIN; /* Sorry, can't do UDP, try TCP! */
if (DNS_SERVER_FEATURE_LEVEL_IS_HTTPS(t->current_feature_level))
return -EAGAIN; /* Direct request logic to HTTPS */
if (!t->bypass && !dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(dns_transaction_key(t)->type))
return -EOPNOTSUPP;
@ -1984,6 +1999,223 @@ static int mdns_make_dummy_packet(DnsTransaction *t, DnsPacket **ret_packet, Set
return add_known_answers;
}
#if ENABLE_DNS_OVER_HTTPS
static size_t dns_transaction_curl_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
_cleanup_free_ char *content_header = NULL;
DnsTransaction *t = ASSERT_PTR(userdata);
size_t sz = size * nmemb;
CURLcode code;
long status;
int r;
assert(contents);
code = curl_easy_getinfo(t->curl, CURLINFO_RESPONSE_CODE, &status);
if (code != CURLE_OK)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve response code: %s", curl_easy_strerror(code));
if (status >= 200 && status <= 299) {
r = curl_header_strdup(contents, sz, "Content-Type:", &content_header);
if (r < 0) {
log_oom();
return 0;
}
if (r > 0) {
r = strcmp("application/dns-message", content_header);
if (r == 0)
t->valid_dns_message = true;
return sz;
}
}
return sz;
}
static size_t dns_transaction_curl_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
DnsTransaction *t = ASSERT_PTR(userdata);
size_t sz = size * nmemb;
int r;
t->payload = memdup(contents, sz);
if (!t->payload) {
log_debug("Failed to extract HTTP payload to further processing");
r = log_oom();
goto fail;
}
t->payload_size += sz;
return sz;
fail:
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return r;
}
static int dns_transaction_curl_recv(DnsTransaction *t, DnsPacket **p) {
size_t ms;
int r;
ms = t->payload_size;
if (t->payload_size < 1) {
log_debug("Received HTTP payload unexpected size %zu", t->payload_size);
return -1;
}
r = dns_packet_new(p, DNS_PROTOCOL_DNS, ms, DNS_PACKET_SIZE_MAX);
if (r < 0)
return r;
log_debug("Received HTTP payload of size %zu", t->payload_size);
return 0;
}
static int dns_transaction_curl_make_url(DnsTransaction *t, char **url) {
_cleanup_free_ char *base64_string = NULL;
uint8_t *packet_to_send = DNS_PACKET_DATA(t->sent);
int r;
/* Let's zero the query ID according to the RFC */
packet_to_send[0] = 0;
packet_to_send[1] = 0;
r = base64mem_full(packet_to_send, t->sent->size, MAX_URL_LENGTH, &base64_string);
if (r < 0) {
log_debug_errno(r, "Failed to encode DNS packet to base64");
return r;
}
/* Remove base64 trailing characters */
delete_trailing_chars(base64_string, "=");
/* Build the DoH's wire format request URL */
r = asprintf(url, "https://%s/dns-query?dns=%s", t->server->server_string, base64_string);
if (r < 0) {
log_debug("Failed to allocate and set the url for transaction %" PRIu16 ".", t->id);
return r;
}
return 0;
}
static void dns_transaction_curl_on_response(CurlGlue *g, CURL *curl, CURLcode result) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t = NULL;
int status;
int r;
assert(g);
assert(curl);
curl_easy_getinfo(curl, CURLINFO_PRIVATE, &t);
if (result != CURLE_OK) {
log_error_errno(SYNTHETIC_ERRNO(EIO), "HTTP request failed: %s", curl_easy_strerror(result));
status = DNS_TRANSACTION_INVALID_REPLY;
goto finish;
}
if (!t->valid_dns_message) {
log_debug("Received invalid HTTP payload, expected content type of application/dns-message");
status = DNS_TRANSACTION_INVALID_REPLY;
goto finish;
}
r = dns_transaction_curl_recv(t, &p);
if (r < 0) {
log_debug_errno(r, "HTTP payload receive failure");
dns_transaction_complete_errno(t, r);
return;
}
/* Transfer the received payload to transaction/packet struct */
uint8_t *p_data = DNS_PACKET_DATA(p);
memcpy(p_data, t->payload, t->payload_size);
p->size = t->payload_size;
r = dns_packet_validate_reply(p);
if (r < 0)
log_debug_errno(r, "Received invalid DNS packet as response, ignoring: %m");
if (r == 0)
log_debug("Received inappropriate DNS packet as response, ignoring");
dns_transaction_process_reply(t, p, false);
return;
finish:
dns_transaction_complete(t, status);
}
static int dns_transaction_emit_curl(DnsTransaction *t) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_free_ char *rule = NULL;
int r;
assert(t);
assert(t->sent);
dns_transaction_close_connection(t, true);
if (t->scope->protocol == DNS_PROTOCOL_DNS) {
r = dns_transaction_pick_server(t);
if (r < 0)
return r;
if (manager_server_is_stub(t->scope->manager, t->server))
return -ELOOP;
r = curl_glue_new(&t->glue, e);
if (r < 0)
return r;
t->glue->on_finished = dns_transaction_curl_on_response;
r = dns_transaction_curl_make_url(t, &t->url);
if (r < 0)
return r;
r = curl_glue_make(&t->curl, t->url, t);
if (r < 0)
return r;
if (curl_easy_setopt(t->curl, CURLOPT_HEADERFUNCTION, dns_transaction_curl_header_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(t->curl, CURLOPT_HEADERDATA, t) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(t->curl, CURLOPT_WRITEFUNCTION, dns_transaction_curl_write_callback) != CURLE_OK)
return -EIO;
if (curl_easy_setopt(t->curl, CURLOPT_WRITEDATA, t) != CURLE_OK)
return -EIO;
// Prevents libcurl's native name lookups
r = asprintf(&rule, "%s:443:%s", t->server->server_string, t->server->server_string);
if (r < 0) {
log_debug("Failed to compound IP resolution to CURLOPT_RESOLVE parameter");
return r;
}
t->glue->resolve_rules = curl_slist_append(NULL, rule);
if (curl_easy_setopt(t->curl, CURLOPT_RESOLVE, t->glue->resolve_rules) != CURLE_OK)
return -EIO;
log_debug("Emitting HTTPS request via curl for transaction %" PRIu16, t->id);
r = curl_glue_add(t->glue, t->curl);
if (r < 0)
return r;
} else
/* TODO: Is this the right error code here? */
return -ELOOP;
return 0;
}
#endif
static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *dummy = NULL;
_cleanup_set_free_ Set *keys = NULL;
@ -2172,10 +2404,20 @@ int dns_transaction_go(DnsTransaction *t) {
r = dns_transaction_emit_udp(t);
if (r == -EMSGSIZE)
log_debug("Sending query via TCP since it is too large.");
#if ENABLE_DNS_OVER_HTTPS
else if ((r == -EAGAIN &&
(DNS_SERVER_FEATURE_LEVEL_IS_HTTPS(t->current_feature_level))))
log_debug("Sending query via HTTPS.");
#endif
else if (r == -EAGAIN)
log_debug("Sending query via TCP since UDP isn't supported or DNS-over-TLS is selected.");
else if (r == -EPERM)
log_debug("Sending query via TCP since UDP is blocked.");
#if ENABLE_DNS_OVER_HTTPS
if ((r == -EAGAIN &&
(DNS_SERVER_FEATURE_LEVEL_IS_HTTPS(t->current_feature_level))))
r = dns_transaction_emit_curl(t);
#endif
if (IN_SET(r, -EMSGSIZE, -EAGAIN, -EPERM))
r = dns_transaction_emit_tcp(t);
}

View File

@ -4,6 +4,11 @@
#include "sd-event.h"
#include "in-addr-util.h"
#if ENABLE_DNS_OVER_HTTPS
#include <curl/curl.h>
#include "curl-util.h"
#endif
typedef struct DnsTransaction DnsTransaction;
typedef struct DnsTransactionFinder DnsTransactionFinder;
typedef enum DnsTransactionState DnsTransactionState;
@ -92,7 +97,15 @@ struct DnsTransaction {
/* TCP connection logic, if we need it */
DnsStream *stream;
#if ENABLE_DNS_OVER_HTTPS
/* HTTPS connection logic, if we need it */
CurlGlue *glue;
CURL *curl;
char *url;
uint8_t *payload;
size_t payload_size;
bool valid_dns_message;
#endif
/* The active server */
DnsServer *server;
@ -219,6 +232,9 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_;
/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */
#define MDNS_TRANSACTION_ATTEMPTS_MAX 3
/* Maximum URL length for HTTP GET request, see RFC ... */
#define MAX_URL_LENGTH 2048
#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? \
LLMNR_TRANSACTION_ATTEMPTS_MAX : \
(p) == DNS_PROTOCOL_MDNS ? \

View File

@ -26,6 +26,7 @@ Resolve.LLMNR, config_parse_resolve_support, 0,
Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
Resolve.DNSOverHTTPS, config_parse_dns_over_https_mode, 0, offsetof(Manager, dns_over_https_mode)
Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)

View File

@ -1670,6 +1670,15 @@ DnsOverTlsMode manager_get_dns_over_tls_mode(Manager *m) {
return DNS_OVER_TLS_NO;
}
DnsOverHttpsMode manager_get_dns_over_https_mode(Manager *m) {
assert(m);
if (m->dns_over_https_mode != _DNS_OVER_HTTPS_MODE_INVALID)
return m->dns_over_https_mode;
return DNS_OVER_HTTPS_NO;
}
void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
assert(verdict >= 0);

View File

@ -40,6 +40,7 @@ struct Manager {
ResolveSupport mdns_support;
DnssecMode dnssec_mode;
DnsOverTlsMode dns_over_tls_mode;
DnsOverHttpsMode dns_over_https_mode;
DnsCacheMode enable_cache;
bool cache_from_localhost;
DnsStubListenerMode dns_stub_listener_mode;
@ -207,6 +208,8 @@ bool manager_dnssec_supported(Manager *m);
DnsOverTlsMode manager_get_dns_over_tls_mode(Manager *m);
DnsOverHttpsMode manager_get_dns_over_https_mode(Manager *m);
void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
bool manager_routable(Manager *m);

View File

@ -26,6 +26,7 @@
#Domains=
#DNSSEC={{DEFAULT_DNSSEC_MODE_STR}}
#DNSOverTLS={{DEFAULT_DNS_OVER_TLS_MODE_STR}}
#DNSOverHTTPS={{DEFAULT_DNS_OVER_HTTPS_MODE_STR}}
#MulticastDNS={{DEFAULT_MDNS_MODE_STR}}
#LLMNR={{DEFAULT_LLMNR_MODE_STR}}
#Cache=yes

View File

@ -311,6 +311,9 @@ void curl_glue_remove_and_free(CurlGlue *g, CURL *c) {
if (g->curl)
curl_multi_remove_handle(g->curl, c);
if (g->resolve_rules)
curl_slist_free_all(g->resolve_rules);
curl_easy_cleanup(c);
}

View File

@ -20,6 +20,7 @@ struct CurlGlue {
void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code);
void *userdata;
struct curl_slist *resolve_rules;;
};
int curl_glue_new(CurlGlue **glue, sd_event *event);

View File

@ -256,6 +256,10 @@ if conf.get('HAVE_TPM2') == 1 and conf.get('HAVE_LIBCRYPTSETUP') == 1
shared_sources += files('cryptsetup-tpm2.c')
endif
if conf.get('HAVE_LIBCURL') == 1
shared_sources += files('curl-util.c')
endif
generate_ip_protocol_list = find_program('generate-ip-protocol-list.sh')
ip_protocol_list_txt = custom_target(
'ip-protocol-list.txt',
@ -337,6 +341,11 @@ libshared_deps = [threads,
libxz_cflags,
libzstd_cflags]
# Is this correct?
if conf.get('HAVE_LIBCURL') == 1
libshared_deps += [libcurl]
endif
libshared_sym_path = meson.current_source_dir() / 'libshared.sym'
libshared_build_dir = meson.current_build_dir()

View File

@ -1808,63 +1808,81 @@ char* umount_and_unlink_and_free(char *p) {
return mfree(p);
}
static int path_get_mount_info(
static int path_get_mount_info_at(
int dir_fd,
const char *path,
char **ret_fstype,
char **ret_options) {
_cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL;
_cleanup_free_ char *fstype = NULL, *options = NULL;
struct libmnt_fs *fs;
int r;
_cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL;
int r, mnt_id;
assert(path);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
table = mnt_new_table();
if (!table)
return -ENOMEM;
r = mnt_table_parse_mtab(table, /* filename = */ NULL);
r = path_get_mnt_id_at(dir_fd, path, &mnt_id);
if (r < 0)
return r;
return log_debug_errno(r, "Failed to get mount ID: %m");
fs = mnt_table_find_mountpoint(table, path, MNT_ITER_FORWARD);
if (!fs)
return -EINVAL;
r = libmount_parse("/proc/self/mountinfo", NULL, &table, &iter);
if (r < 0)
return log_debug_errno(r, "Failed to parse /proc/self/mountinfo: %m");
if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
if (!fstype)
return -ENOMEM;
for (;;) {
struct libmnt_fs *fs;
r = mnt_table_next_fs(table, iter, &fs);
if (r == 1)
break; /* EOF */
if (r < 0)
return log_debug_errno(r, "Failed to get next entry from /proc/self/mountinfo: %m");
if (mnt_fs_get_id(fs) != mnt_id)
continue;
_cleanup_free_ char *fstype = NULL, *options = NULL;
if (ret_fstype) {
fstype = strdup(strempty(mnt_fs_get_fstype(fs)));
if (!fstype)
return log_oom_debug();
}
if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
if (!options)
return log_oom_debug();
}
if (ret_fstype)
*ret_fstype = TAKE_PTR(fstype);
if (ret_options)
*ret_options = TAKE_PTR(options);
return 0;
}
if (ret_options) {
options = strdup(strempty(mnt_fs_get_options(fs)));
if (!options)
return -ENOMEM;
}
if (ret_fstype)
*ret_fstype = TAKE_PTR(fstype);
if (ret_options)
*ret_options = TAKE_PTR(options);
return 0;
return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Cannot find mount ID %i from /proc/self/mountinfo.", mnt_id);
}
int path_is_network_fs_harder(const char *path) {
int path_is_network_fs_harder_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = xopenat(dir_fd, path, O_PATH | O_CLOEXEC | O_NOFOLLOW);
if (fd < 0)
return fd;
r = fd_is_network_fs(fd);
if (r != 0)
return r;
_cleanup_free_ char *fstype = NULL, *options = NULL;
int r, ret;
assert(path);
ret = path_is_network_fs(path);
if (ret > 0)
return true;
r = path_get_mount_info(path, &fstype, &options);
r = path_get_mount_info_at(fd, /* path = */ NULL, &fstype, &options);
if (r < 0)
return RET_GATHER(ret, r);
return r;
if (fstype_is_network(fstype))
return true;

View File

@ -181,4 +181,7 @@ int mount_credentials_fs(const char *path, size_t size, bool ro);
int make_fsmount(int error_log_level, const char *what, const char *type, unsigned long flags, const char *options, int userns_fd);
int path_is_network_fs_harder(const char *path);
int path_is_network_fs_harder_at(int dir_fd, const char *path);
static inline int path_is_network_fs_harder(const char *path) {
return path_is_network_fs_harder_at(AT_FDCWD, path);
}

View File

@ -7,6 +7,7 @@
DEFINE_CONFIG_PARSE_ENUM(config_parse_resolve_support, resolve_support, ResolveSupport);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dnssec_mode, dnssec_mode, DnssecMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_over_tls_mode, dns_over_tls_mode, DnsOverTlsMode);
DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_over_https_mode, dns_over_https_mode, DnsOverHttpsMode);
static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = {
[RESOLVE_SUPPORT_NO] = "no",
@ -29,6 +30,12 @@ static const char* const dns_over_tls_mode_table[_DNS_OVER_TLS_MODE_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, DNS_OVER_TLS_YES);
static const char* const dns_over_https_mode_table[_DNS_OVER_HTTPS_MODE_MAX] = {
[DNS_OVER_HTTPS_NO] = "no",
[DNS_OVER_HTTPS_YES] = "yes",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_https_mode, DnsOverHttpsMode, DNS_OVER_HTTPS_YES);
bool dns_server_address_valid(int family, const union in_addr_union *sa) {
/* Refuses the 0 IP addresses as well as 127.0.0.53/127.0.0.54 (which is our own DNS stub) */

View File

@ -27,6 +27,7 @@ enum DnsCacheMode {
typedef enum ResolveSupport ResolveSupport;
typedef enum DnssecMode DnssecMode;
typedef enum DnsOverTlsMode DnsOverTlsMode;
typedef enum DnsOverHttpsMode DnsOverHttpsMode;
/* Do not change the order, see link_get_llmnr_support() or link_get_mdns_support(). */
enum ResolveSupport {
@ -70,9 +71,21 @@ enum DnsOverTlsMode {
_DNS_OVER_TLS_MODE_INVALID = -EINVAL,
};
enum DnsOverHttpsMode {
/* No connection is made for DNS-over-HTTPS */
DNS_OVER_HTTPS_NO,
/* Enforce DNS-over-HTTPS */
DNS_OVER_HTTPS_YES,
_DNS_OVER_HTTPS_MODE_MAX,
_DNS_OVER_HTTPS_MODE_INVALID = -EINVAL,
};
CONFIG_PARSER_PROTOTYPE(config_parse_resolve_support);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dns_over_tls_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dns_over_https_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dns_cache_mode);
const char* resolve_support_to_string(ResolveSupport p) _const_;
@ -84,6 +97,9 @@ DnssecMode dnssec_mode_from_string(const char *s) _pure_;
const char* dns_over_tls_mode_to_string(DnsOverTlsMode p) _const_;
DnsOverTlsMode dns_over_tls_mode_from_string(const char *s) _pure_;
const char* dns_over_https_mode_to_string(DnsOverHttpsMode p) _const_;
DnsOverHttpsMode dns_over_https_mode_from_string(const char *s) _pure_;
bool dns_server_address_valid(int family, const union in_addr_union *sa);
const char* dns_cache_mode_to_string(DnsCacheMode p) _const_;

View File

@ -538,9 +538,53 @@ TEST(bind_mount_submounts) {
}
TEST(path_is_network_fs_harder) {
ASSERT_OK_ZERO(path_is_network_fs_harder("/dev"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/sys"));
ASSERT_OK_ZERO(path_is_network_fs_harder("/run"));
_cleanup_close_ int dir_fd = -EBADF;
int r;
ASSERT_OK(dir_fd = open("/", O_PATH | O_CLOEXEC));
FOREACH_STRING(s,
"/", "/dev/", "/proc/", "/run/", "/sys/", "/tmp/", "/usr/", "/var/tmp/",
"", ".", "../../../", "/this/path/should/not/exist/for/test-mount-util/") {
r = path_is_network_fs_harder(s);
log_debug("path_is_network_fs_harder(%s) → %i: %s", s, r, r < 0 ? STRERROR(r) : yes_no(r));
const char *q = path_startswith(s, "/") ?: s;
r = path_is_network_fs_harder_at(dir_fd, q);
log_debug("path_is_network_fs_harder_at(root, %s) → %i: %s", q, r, r < 0 ? STRERROR(r) : yes_no(r));
}
if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) {
(void) log_tests_skipped("not running privileged");
return;
}
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
assert_se(mkdtemp_malloc("/tmp/test-mount-util.path_is_network_fs_harder.XXXXXXX", &t) >= 0);
r = safe_fork("(make_mount-point)",
FORK_RESET_SIGNALS |
FORK_CLOSE_ALL_FDS |
FORK_DEATHSIG_SIGTERM |
FORK_WAIT |
FORK_REOPEN_LOG |
FORK_LOG |
FORK_NEW_MOUNTNS |
FORK_MOUNTNS_SLAVE,
NULL);
ASSERT_OK(r);
if (r == 0) {
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, NULL));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));
ASSERT_OK(mount_nofollow_verbose(LOG_INFO, "tmpfs", t, "tmpfs", 0, "x-systemd-growfs,x-systemd-automount"));
ASSERT_OK_ZERO(path_is_network_fs_harder(t));
ASSERT_OK_ERRNO(umount(t));
_exit(EXIT_SUCCESS);
}
}
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -142,11 +142,13 @@ endif
############################################################
if install_tests
foreach script : ['integration-test-setup.sh', 'run-unit-tests.py']
install_data(script,
install_mode : 'rwxr-xr-x',
install_dir : testsdir)
endforeach
install_data('run-unit-tests.py',
install_mode : 'rwxr-xr-x',
install_dir : testsdir)
install_data('integration-test-setup.sh',
install_mode : 'rwxr-xr-x',
install_dir : testdata_dir)
endif
############################################################

View File

@ -7,9 +7,9 @@ Before=getty-pre.target
[Service]
ExecStartPre=rm -f /failed /testok
ExecStartPre=/usr/lib/systemd/tests/integration-test-setup.sh setup
ExecStartPre=/usr/lib/systemd/tests/testdata/integration-test-setup.sh setup
ExecStart=@command@
ExecStopPost=/usr/lib/systemd/tests/integration-test-setup.sh finalize
ExecStopPost=/usr/lib/systemd/tests/testdata/integration-test-setup.sh finalize
Type=oneshot
MemoryAccounting=@memory-accounting@
StateDirectory=%N