1
0
mirror of https://github.com/systemd/systemd synced 2025-09-30 17:24:46 +02:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Evgeny Vereshchagin
c39e36203f ci: switch back to meson-0.56.2
It seems there is another meson (0.57.0) regression preventing clang from
building systemd with --optimization=3 -Db_lto=true

By analogy with https://github.com/google/oss-fuzz/pull/5199 let's just switch
to 0.56.2 for the time being
2021-02-15 15:38:27 +09:00
Yu Watanabe
f24cbe56d7
Merge pull request #18588 from poettering/refuse-loops
resolved: try hard to never enter packet loops between resolved's stub and resolved's client
2021-02-15 11:10:32 +09:00
Yu Watanabe
fcf99d0ec7
Merge pull request #18587 from poettering/rr-count-workaround
two fixes for handling misformed packets more gracefully.
2021-02-15 10:55:10 +09:00
Lennart Poettering
d711322c13 resolvectl: add support for various new flags 2021-02-15 10:47:41 +09:00
Yu Watanabe
547f9b0dc5
Merge pull request #18563 from poettering/nss-resolve-no-valid
timesyncd: for off dnssec in resolved
2021-02-15 10:43:49 +09:00
Lennart Poettering
49ef064c8d resolved: refuse sending packets to our own stub listeners
A previous commit made sure that when one of our own packets is looped
back to us, we ignore it. But let's go one step further, and refuse
operation if we notice the server we talk to is our own. This way we
won't generate unnecessary traffic and can return a cleaner error.

Fixes: #17413
2021-02-14 23:12:22 +01:00
Lennart Poettering
a9fd8837d4 resolved: refuse packets looped back to us
Fixes: #17413
2021-02-14 23:12:22 +01:00
Lennart Poettering
94378145e6 resolved: rename manager_our_packet() → manager_packet_from_local_address()
Let's be more precise in naming this function, after all this doesn#t
actually check if the packet is really ours, but just that the source IP
address is a local one. Hence name it that way.

(This is preparation to add a helper that checks if packet belongs to
local transaction later on)
2021-02-14 23:12:22 +01:00
Lennart Poettering
ae49ce8761 resolved: tweak answer reserve/clone logic a bit
Let's add some overflow checks. Also, if 0 records are reserved, use
this as indication that a copy shall be done and do not grow the answer
beyond the current size.
2021-02-14 23:12:22 +01:00
Lennart Poettering
894c7b7708 resolved: gracefully handle trailing packet garbage
Similar to the previous commit: if we see trailing garbage in a DNS
packet, let's assume EDNS is borked too, and suppress it.
2021-02-14 23:05:38 +01:00
Lennart Poettering
18674159eb resolved: gracefully handle with packets with too large RR count
Apparently, there are plenty routers in place that report an incorrect
RR count in the packets: they declare more RRs than are actually
included.

Let's accept these responses, but let's downgrade them to baseline, i.e.
let's suppress OPT in this case: if they don't even get the RR count
right, let's operate on the absolute baseline, and not bother with
anything fancier such as EDNS.

Prompted-by: https://github.com/systemd/systemd/issues/12841#issuecomment-724063973

Fixes: #3980
Most likely fixes: #12841
2021-02-14 23:05:28 +01:00
Lennart Poettering
abf4e5c1d3 units: turn off DNSSEC validation when timesyncd resolves hostnames
We have a chicken and egg problem: validation of DNSSEC signatures
doesn't work without a correct clock, but to set the correct clock we
need to contact NTP servers which requires resolving a hostname, which
would normally require DNSSEC validation.

Let's break the cycle by excluding NTP hostname resolution from
validation for now.

Of course, this leaves NTP traffic unprotected. To cover that we need
NTPSEC support, which we can add later.

Fixes: #5873 #15607
2021-02-14 22:05:18 +01:00
Lennart Poettering
aee9d18c8d nss-resolve: allow turning off validation via env var 2021-02-14 22:01:09 +01:00
21 changed files with 323 additions and 38 deletions

View File

@ -95,7 +95,7 @@ apt-get -y install "${PACKAGES[@]}"
# support all the features we need (like --optimization=). Since the build-dep
# command above installs the distro versions, let's install the pip ones just
# locally and add the local bin directory to the $PATH.
pip3 install --user -U meson ninja
pip3 install --user -U meson==0.56.2 ninja
export PATH="$HOME/.local/bin:$PATH"
$CC --version

View File

@ -262,6 +262,65 @@
returned.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--validate=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), DNSSEC validation is applied as usual — under the condition that it is enabled for the
network and for <filename>systemd-resolved.service</filename> as a whole. If false, DNSSEC validation
is disabled for the specific query, regardless of whether it is enabled for the network or in the
service. Note that setting this option to true does not force DNSSEC validation on systems/networks
where DNSSEC is turned off. This option is only suitable to turn off such validation where otherwise
enabled, not enable validation where otherwise disabled.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--synthesize=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), select domains are resolved on the local system, among them
<literal>localhost</literal> and <literal>_gateway</literal> or entries from
<filename>/etc/hosts</filename>. If false these domains are not resolved locally, and either fail (in
case of <literal>localhost</literal> or <literal>_gateway</literal> and suchlike) or go to the
network via regular DNS/mDNS/LLMNR lookups (in case of <filename>/etc/hosts</filename>
entries).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--cache=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups use the local DNS resource record cache. If false, lookups are routed to the
network instead, regardless if already available in the local cache.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--zone=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups are answered from locally registered LLMNR or mDNS resource records, if
defined. If false, locally registered LLMNR/mDNS records are not considered for the lookup
request.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--trust-anchor=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups for DS and DNSKEY are answered from the local DNSSEC trust anchors if
possible. If false, the local trust store is not considered for the lookup request.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--network=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If true
(the default), lookups are answered via DNS, LLMNR or mDNS network requests if they cannot be
synthesized locally, or be answered from the local cache, zone or trust anchors (see above). If false,
the request is not answered from the network and will thus fail if none of the indicated sources can
answer them.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--search=</option><replaceable>BOOL</replaceable></term>

View File

@ -76,6 +76,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SOURCE, ESRCH),
SD_BUS_ERROR_MAP(BUS_ERROR_STUB_LOOP, ELOOP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DNSSD_SERVICE, ENOENT),
SD_BUS_ERROR_MAP(BUS_ERROR_DNSSD_SERVICE_EXISTS, EEXIST),

View File

@ -74,6 +74,7 @@
#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
#define BUS_ERROR_NO_SOURCE "org.freedesktop.resolve1.NoSource"
#define BUS_ERROR_STUB_LOOP "org.freedesktop.resolve1.StubLoop"
#define BUS_ERROR_NO_SUCH_DNSSD_SERVICE "org.freedesktop.resolve1.NoSuchDnssdService"
#define BUS_ERROR_DNSSD_SERVICE_EXISTS "org.freedesktop.resolve1.DnssdServiceExists"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."

View File

@ -8,6 +8,7 @@
#include <sys/types.h>
#include <unistd.h>
#include "env-util.h"
#include "errno-util.h"
#include "in-addr-util.h"
#include "macro.h"
@ -184,6 +185,21 @@ static const JsonDispatch address_parameters_dispatch_table[] = {
{}
};
static uint64_t query_flags(void) {
uint64_t f = 0;
int r;
/* Allow callers to turn off validation, when we resolve via nss-resolve */
r = getenv_bool_secure("SYSTEMD_NSS_RESOLVE_VALIDATE");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_NSS_RESOLVE_VALIDATE value, ignoring.");
else if (r == 0)
f |= SD_RESOLVED_NO_VALIDATE;
return f;
}
enum nss_status _nss_resolve_gethostbyname4_r(
const char *name,
struct gaih_addrtuple **pat,
@ -215,7 +231,8 @@ enum nss_status _nss_resolve_gethostbyname4_r(
goto fail;
r = json_build(&cparams, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name))));
JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
if (r < 0)
goto fail;
@ -367,7 +384,8 @@ enum nss_status _nss_resolve_gethostbyname3_r(
goto fail;
r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)),
JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af))));
JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
if (r < 0)
goto fail;
@ -571,7 +589,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
goto fail;
r = json_build(&cparams, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("address", JSON_BUILD_BYTE_ARRAY(addr, len)),
JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af))));
JSON_BUILD_PAIR("family", JSON_BUILD_INTEGER(af)),
JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(query_flags()))));
if (r < 0)
goto fail;

View File

@ -2620,8 +2620,14 @@ static int native_help(void) {
" --service-address=BOOL Resolve address for services (default: yes)\n"
" --service-txt=BOOL Resolve TXT records for services (default: yes)\n"
" --cname=BOOL Follow CNAME redirects (default: yes)\n"
" --search=BOOL Use search domains for single-label names\n"
" (default: yes)\n"
" --validate=BOOL Allow DNSSEC validation (default: yes)\n"
" --synthesize=BOOL Allow synthetic response (default: yes)\n"
" --cache=BOOL Allow response from cache (default: yes)\n"
" --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
" records (default: yes)\n"
" --trust-anchor=BOOL Allow response from local trust anchor (default: yes)\n"
" --network=BOOL Allow response from network (default: yes)\n"
" --search=BOOL Use search domains for single-label names (default: yes)\n"
" --raw[=payload|packet] Dump the answer as binary data\n"
" --legend=BOOL Print headers and additional info (default: yes)\n"
"\nSee the %s for details.\n",
@ -2961,6 +2967,12 @@ static int native_parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_LEGEND,
ARG_CNAME,
ARG_VALIDATE,
ARG_SYNTHESIZE,
ARG_CACHE,
ARG_ZONE,
ARG_TRUST_ANCHOR,
ARG_NETWORK,
ARG_SERVICE_ADDRESS,
ARG_SERVICE_TXT,
ARG_RAW,
@ -2977,6 +2989,12 @@ static int native_parse_argv(int argc, char *argv[]) {
{ "interface", required_argument, NULL, 'i' },
{ "protocol", required_argument, NULL, 'p' },
{ "cname", required_argument, NULL, ARG_CNAME },
{ "validate", required_argument, NULL, ARG_VALIDATE },
{ "synthesize", required_argument, NULL, ARG_SYNTHESIZE },
{ "cache", required_argument, NULL, ARG_CACHE },
{ "zone", required_argument, NULL, ARG_ZONE },
{ "trust-anchor", required_argument, NULL, ARG_TRUST_ANCHOR },
{ "network", required_argument, NULL, ARG_NETWORK },
{ "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
{ "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
{ "raw", optional_argument, NULL, ARG_RAW },
@ -3100,6 +3118,48 @@ static int native_parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0);
break;
case ARG_VALIDATE:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --validate= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_VALIDATE, r == 0);
break;
case ARG_SYNTHESIZE:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --synthesize= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_SYNTHESIZE, r == 0);
break;
case ARG_CACHE:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --cache= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_CACHE, r == 0);
break;
case ARG_ZONE:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --zone= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_ZONE, r == 0);
break;
case ARG_TRUST_ANCHOR:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --trust-anchor= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_TRUST_ANCHOR, r == 0);
break;
case ARG_NETWORK:
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --network= argument.");
SET_FLAG(arg_flags, SD_RESOLVED_NO_NETWORK, r == 0);
break;
case ARG_SERVICE_ADDRESS:
r = parse_boolean(optarg);
if (r < 0)

View File

@ -104,6 +104,9 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_NO_SOURCE:
return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SOURCE, "All suitable resolution sources turned off");
case DNS_TRANSACTION_STUB_LOOP:
return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_STUB_LOOP, "Configured DNS server loops back to us");
case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;

View File

@ -740,16 +740,21 @@ int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
if ((*a)->n_ref > 1)
return -EBUSY;
ns = (*a)->n_rrs + n_free;
if (ns > UINT16_MAX) /* Maximum number of RRs we can stick into a DNS packet section */
ns = (*a)->n_rrs;
assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
if (n_free > UINT16_MAX - ns) /* overflow check */
ns = UINT16_MAX;
else
ns += n_free;
if ((*a)->n_allocated >= ns)
return 0;
/* Allocate more than we need */
ns *= 2;
if (ns > UINT16_MAX)
/* Allocate more than we need, but not more than UINT16_MAX */
if (ns <= UINT16_MAX/2)
ns *= 2;
else
ns = UINT16_MAX;
/* This must be done before realloc() below. Otherwise, the original DnsAnswer object
@ -780,33 +785,50 @@ int dns_answer_reserve(DnsAnswer **a, size_t n_free) {
}
int dns_answer_reserve_or_clone(DnsAnswer **a, size_t n_free) {
_cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
int r;
assert(a);
/* Tries to extend the DnsAnswer object. And if that's not
* possible, since we are not the sole owner, then allocate a
* new, appropriately sized one. Either way, after this call
* the object will only have a single reference, and has room
* for at least the specified number of RRs. */
/* Tries to extend the DnsAnswer object. And if that's not possible, since we are not the sole owner,
* then allocate a new, appropriately sized one. Either way, after this call the object will only
* have a single reference, and has room for at least the specified number of RRs. */
r = dns_answer_reserve(a, n_free);
if (r != -EBUSY)
return r;
if (*a && (*a)->n_ref > 1) {
_cleanup_(dns_answer_unrefp) DnsAnswer *n = NULL;
size_t ns;
assert(*a);
ns = (*a)->n_rrs;
assert(ns <= UINT16_MAX); /* Maximum number of RRs we can stick into a DNS packet section */
n = dns_answer_new(((*a)->n_rrs + n_free) * 2);
if (!n)
return -ENOMEM;
if (n_free > UINT16_MAX - ns) /* overflow check */
ns = UINT16_MAX;
else if (n_free > 0) { /* Increase size and double the result, just in case — except if the
* increase is specified as 0, in which case we just allocate the
* exact amount as before, under the assumption this is just a request
* to copy the answer. */
ns += n_free;
r = dns_answer_add_raw_all(n, *a);
if (r < 0)
return r;
if (ns <= UINT16_MAX/2) /* overflow check */
ns *= 2;
else
ns = UINT16_MAX;
}
dns_answer_unref(*a);
*a = TAKE_PTR(n);
n = dns_answer_new(ns);
if (!n)
return -ENOMEM;
r = dns_answer_add_raw_all(n, *a);
if (r < 0)
return r;
dns_answer_unref(*a);
assert_se(*a = TAKE_PTR(n));
} else if (n_free > 0) {
r = dns_answer_reserve(a, n_free);
if (r < 0)
return r;
}
return 0;
}

View File

@ -2253,6 +2253,18 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
bool cache_flush = false;
size_t start;
if (p->rindex == p->size) {
/* If we reached the end of the packet already, but there are still more RRs
* declared, then that's a corrupt packet. Let's accept the packet anyway, since it's
* apparently a common bug in routers. Let's however suppress OPT support in this
* case, so that we force the rest of the logic into lowest DNS baseline support. Or
* to say this differently: if the DNS server doesn't even get the RR counts right,
* it's highly unlikely it gets EDNS right. */
log_debug("More resource records declared in packet than included, suppressing OPT.");
bad_opt = true;
break;
}
r = dns_packet_read_rr(p, &rr, &cache_flush, &start);
if (r < 0)
return r;
@ -2352,8 +2364,10 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) {
previous = dns_resource_record_ref(rr);
}
if (bad_opt)
if (bad_opt) {
p->opt = dns_resource_record_unref(p->opt);
p->opt_start = p->opt_size = SIZE_MAX;
}
*ret_answer = TAKE_PTR(answer);
@ -2380,6 +2394,12 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
return r;
if (p->rindex < p->size) {
log_debug("Trailing garbage in packet, suppressing OPT.");
p->opt = dns_resource_record_unref(p->opt);
p->opt_start = p->opt_size = SIZE_MAX;
}
p->question = TAKE_PTR(question);
p->answer = TAKE_PTR(answer);
@ -2535,6 +2555,10 @@ static int dns_packet_compare_func(const DnsPacket *x, const DnsPacket *y) {
DEFINE_HASH_OPS(dns_packet_hash_ops, DnsPacket, dns_packet_hash_func, dns_packet_compare_func);
bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b) {
return dns_packet_compare_func(a, b) == 0;
}
static const char* const dns_rcode_table[_DNS_RCODE_MAX_DEFINED] = {
[DNS_RCODE_SUCCESS] = "SUCCESS",
[DNS_RCODE_FORMERR] = "FORMERR",

View File

@ -227,6 +227,8 @@ void dns_packet_rewind(DnsPacket *p, size_t idx);
int dns_packet_skip_question(DnsPacket *p);
int dns_packet_extract(DnsPacket *p);
bool dns_packet_equal(const DnsPacket *a, const DnsPacket *b);
/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
enum {
DNS_RCODE_SUCCESS = 0,

View File

@ -1126,7 +1126,7 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) {
return;
}
if (manager_our_packet(scope->manager, p))
if (manager_packet_from_local_address(scope->manager, p))
return;
r = dns_packet_extract(p);

View File

@ -85,6 +85,15 @@ DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p) {
return mfree(p);
}
uint16_t dns_stub_listener_extra_port(DnsStubListenerExtra *p) {
assert(p);
if (p->port > 0)
return p->port;
return 53;
}
static int dns_stub_collect_answer_by_question(
DnsAnswer **reply,
DnsAnswer *answer,
@ -639,6 +648,7 @@ static void dns_stub_query_complete(DnsQuery *q) {
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
case DNS_TRANSACTION_NETWORK_DOWN:
case DNS_TRANSACTION_NO_SOURCE:
case DNS_TRANSACTION_STUB_LOOP:
(void) dns_stub_send_reply(q, DNS_RCODE_SERVFAIL);
break;
@ -688,6 +698,11 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
return;
}
if (manager_packet_from_our_transaction(m, p)) {
log_debug("Got our own packet looped back, ignoring.");
return;
}
r = dns_packet_extract(p);
if (r < 0) {
log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
@ -951,13 +966,13 @@ static int manager_dns_stub_fd_extra(Manager *m, DnsStubListenerExtra *l, int ty
if (l->family == AF_INET)
sa = (union sockaddr_union) {
.in.sin_family = l->family,
.in.sin_port = htobe16(l->port != 0 ? l->port : 53U),
.in.sin_port = htobe16(dns_stub_listener_extra_port(l)),
.in.sin_addr = l->address.in,
};
else
sa = (union sockaddr_union) {
.in6.sin6_family = l->family,
.in6.sin6_port = htobe16(l->port != 0 ? l->port : 53U),
.in6.sin6_port = htobe16(dns_stub_listener_extra_port(l)),
.in6.sin6_addr = l->address.in6,
};

View File

@ -33,6 +33,7 @@ extern const struct hash_ops dns_stub_listener_extra_hash_ops;
int dns_stub_listener_extra_new(Manager *m, DnsStubListenerExtra **ret);
DnsStubListenerExtra *dns_stub_listener_extra_free(DnsStubListenerExtra *p);
uint16_t dns_stub_listener_extra_port(DnsStubListenerExtra *p);
void manager_dns_stub_stop(Manager *m);
int manager_dns_stub_start(Manager *m);

View File

@ -317,8 +317,9 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) {
assert(t);
assert(p);
assert(t->scope->protocol == DNS_PROTOCOL_LLMNR);
if (manager_our_packet(t->scope->manager, p) != 0)
if (manager_packet_from_local_address(t->scope->manager, p) != 0)
return;
(void) in_addr_to_string(p->family, &p->sender, &pretty);
@ -639,6 +640,9 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
if (r < 0)
return r;
if (manager_server_is_stub(t->scope->manager, t->server))
return -ELOOP;
if (!t->bypass) {
if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(dns_transaction_key(t)->type))
return -EOPNOTSUPP;
@ -1303,6 +1307,9 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
if (r < 0)
return r;
if (manager_server_is_stub(t->scope->manager, t->server))
return -ELOOP;
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! */
@ -1845,7 +1852,23 @@ int dns_transaction_go(DnsTransaction *t) {
if (IN_SET(r, -EMSGSIZE, -EAGAIN))
r = dns_transaction_emit_tcp(t);
}
if (r == -ELOOP) {
if (t->scope->protocol != DNS_PROTOCOL_DNS)
return r;
/* One of our own stub listeners */
log_debug_errno(r, "Detected that specified DNS server is our own extra listener, switching DNS servers.");
dns_scope_next_dns_server(t->scope);
if (dns_scope_get_dns_server(t->scope) == t->server) {
log_debug_errno(r, "Still pointing to extra listener after switching DNS servers, refusing operation.");
dns_transaction_complete(t, DNS_TRANSACTION_STUB_LOOP);
return 0;
}
return dns_transaction_go(t);
}
if (r == -ESRCH) {
/* No servers to send this to? */
dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS);
@ -3386,6 +3409,7 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
[DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
[DNS_TRANSACTION_NOT_FOUND] = "not-found",
[DNS_TRANSACTION_NO_SOURCE] = "no-source",
[DNS_TRANSACTION_STUB_LOOP] = "stub-loop",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);

View File

@ -33,6 +33,7 @@ enum DnsTransactionState {
DNS_TRANSACTION_NETWORK_DOWN,
DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
DNS_TRANSACTION_NO_SOURCE, /* All suitable DnsTransactionSource turned off */
DNS_TRANSACTION_STUB_LOOP,
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -EINVAL,
};

View File

@ -83,7 +83,7 @@ static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *u
if (r <= 0)
return r;
if (manager_our_packet(m, p))
if (manager_packet_from_local_address(m, p))
return 0;
scope = manager_find_scope(m, p);

View File

@ -1255,13 +1255,31 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad
return NULL;
}
bool manager_our_packet(Manager *m, DnsPacket *p) {
bool manager_packet_from_local_address(Manager *m, DnsPacket *p) {
assert(m);
assert(p);
/* Let's see if this packet comes from an IP address we have on any local interface */
return !!manager_find_link_address(m, p->family, &p->sender);
}
bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p) {
DnsTransaction *t;
assert(m);
assert(p);
/* Let's see if we have a transaction with a query message with the exact same binary contents as the
* one we just got. If so, it's almost definitely a packet loop of some kind. */
t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (!t)
return false;
return t->sent && dns_packet_equal(t->sent, p);
}
DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
Link *l;
@ -1600,3 +1618,27 @@ bool manager_next_dnssd_names(Manager *m) {
return tried;
}
bool manager_server_is_stub(Manager *m, DnsServer *s) {
DnsStubListenerExtra *l;
assert(m);
assert(s);
/* Safety check: we generally already skip the main stub when parsing configuration. But let's be
* extra careful, and check here again */
if (s->family == AF_INET &&
s->address.in.s_addr == htobe32(INADDR_DNS_STUB) &&
dns_server_port(s) == 53)
return true;
/* Main reason to call this is to check server data against the extra listeners, and filter things
* out. */
ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners)
if (s->family == l->family &&
in_addr_equal(s->family, &s->address, &l->address) &&
dns_server_port(s) == dns_stub_listener_extra_port(l))
return true;
return false;
}

View File

@ -165,7 +165,9 @@ LinkAddress* manager_find_link_address(Manager *m, int family, const union in_ad
void manager_refresh_rrs(Manager *m);
int manager_next_hostname(Manager *m);
bool manager_our_packet(Manager *m, DnsPacket *p);
bool manager_packet_from_local_address(Manager *m, DnsPacket *p);
bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p);
DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
void manager_verify_all(Manager *m);
@ -195,3 +197,5 @@ void manager_reset_server_features(Manager *m);
void manager_cleanup_saved_user(Manager *m);
bool manager_next_dnssd_names(Manager *m);
bool manager_server_is_stub(Manager *m, DnsServer *s);

View File

@ -254,7 +254,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
if (r <= 0)
return r;
if (manager_our_packet(m, p))
if (manager_packet_from_local_address(m, p))
return 0;
scope = manager_find_scope(m, p);

View File

@ -60,6 +60,9 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_NO_SOURCE:
return varlink_error(q->varlink_request, "io.systemd.Resolve.NoSource", NULL);
case DNS_TRANSACTION_STUB_LOOP:
return varlink_error(q->varlink_request, "io.systemd.Resolve.StubLoop", NULL);
case DNS_TRANSACTION_NOT_FOUND:
/* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
* thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */

View File

@ -22,6 +22,10 @@ Wants=time-set.target
AmbientCapabilities=CAP_SYS_TIME
BusName=org.freedesktop.timesync1
CapabilityBoundingSet=CAP_SYS_TIME
# Turn off DNSSEC validation for hostname look-ups, since those need the
# correct time to work, but we likely won't acquire that without NTP. Let's
# break this chicken-and-egg cycle here.
Environment=SYSTEMD_NSS_RESOLVE_VALIDATE=0
ExecStart=!!@rootlibexecdir@/systemd-timesyncd
LockPersonality=yes
MemoryDenyWriteExecute=yes