mirror of
https://github.com/systemd/systemd
synced 2025-09-30 17:24:46 +02:00
Compare commits
13 Commits
8c9c68b593
...
c39e36203f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c39e36203f | ||
![]() |
f24cbe56d7 | ||
![]() |
fcf99d0ec7 | ||
![]() |
d711322c13 | ||
![]() |
547f9b0dc5 | ||
![]() |
49ef064c8d | ||
![]() |
a9fd8837d4 | ||
![]() |
94378145e6 | ||
![]() |
ae49ce8761 | ||
![]() |
894c7b7708 | ||
![]() |
18674159eb | ||
![]() |
abf4e5c1d3 | ||
![]() |
aee9d18c8d |
2
.github/workflows/build_test.sh
vendored
2
.github/workflows/build_test.sh
vendored
@ -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
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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."
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user