Compare commits
3 Commits
9bf03b7e2e
...
b8bc23d241
Author | SHA1 | Date |
---|---|---|
Raul Cheleguini | b8bc23d241 | |
Ryan Wilson | d8091e1281 | |
Raul Cheleguini | 9173c11fe4 |
24
meson.build
24
meson.build
|
@ -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(' '),
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -14,7 +14,6 @@ systemd_pull_sources = files(
|
|||
'pull-tar.c',
|
||||
'pull-job.c',
|
||||
'pull-common.c',
|
||||
'curl-util.c',
|
||||
)
|
||||
|
||||
systemd_import_sources = files(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ? \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -132,10 +132,12 @@ testcase_unpriv() {
|
|||
return 0
|
||||
fi
|
||||
|
||||
# The kernel has a restriction for unprivileged user namespaces where they cannot mount a less restrictive
|
||||
# instance of /proc/. So if /proc/ is masked (e.g. /proc/kmsg is over-mounted with tmpfs as systemd-nspawn does),
|
||||
# then mounting a new /proc/ will fail and we will still see the host's /proc/. Thus, to allow tests to run in
|
||||
# a VM or nspawn, we mount a new proc on a temporary directory with no masking to bypass this kernel restriction.
|
||||
# IMPORTANT: For /proc/ to be remounted in pid namespace within an unprivileged user namespace, there needs to
|
||||
# be at least 1 unmasked procfs mount in ANY directory. Otherwise, if /proc/ is masked (e.g. /proc/scsi is
|
||||
# over-mounted with tmpfs), then mounting a new /proc/ will fail.
|
||||
#
|
||||
# Thus, to guarantee PrivatePIDs=yes tests for unprivileged users pass, we mount a new procfs on a temporary
|
||||
# directory with no masking. This will guarantee an unprivileged user can mount a new /proc/ successfully.
|
||||
mkdir -p /tmp/TEST-07-PID1-private-pids-proc
|
||||
mount -t proc proc /tmp/TEST-07-PID1-private-pids-proc
|
||||
|
||||
|
@ -146,7 +148,16 @@ testcase_unpriv() {
|
|||
umount /tmp/TEST-07-PID1-private-pids-proc
|
||||
rm -rf /tmp/TEST-07-PID1-private-pids-proc
|
||||
|
||||
# Now verify the behavior with masking - units should fail as PrivatePIDs=yes has no graceful fallback.
|
||||
# Now we will mask /proc/ by mounting tmpfs over /proc/scsi. This will guarantee that mounting /proc/ will fail
|
||||
# for unprivileged users when using PrivatePIDs=yes. Now units should fail as PrivatePIDs=yes has no graceful
|
||||
# fallback.
|
||||
#
|
||||
# Note some kernels do not have /proc/scsi so we verify the directory exists prior to running the test.
|
||||
if [ ! -d /proc/scsi ]; then
|
||||
echo "/proc/scsi does not exist, skipping unprivileged PrivatePIDs=yes test with masked /proc/"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ "$HAS_EXISTING_SCSI_MOUNT" == "no" ]]; then
|
||||
mount -t tmpfs tmpfs /proc/scsi
|
||||
fi
|
||||
|
|
Loading…
Reference in New Issue