From d0eae64c1f40e48952579a477a88fffdb05b31eb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2024 18:46:52 +0100 Subject: [PATCH 1/9] resolved: add new SD_RESOLVED_RELAX_SINGLE_LABEL resolver flag This new flag allows resolving single label names via public DNS. By default this is turned off, and this option allows excepting a lookup for this. --- man/org.freedesktop.resolve1.xml | 47 ++++++++++++++++---------------- src/resolve/resolved-bus.c | 1 + src/resolve/resolved-def.h | 4 +++ src/resolve/resolved-dns-query.c | 4 +-- src/resolve/resolved-dns-scope.c | 6 ++-- src/resolve/resolved-dns-scope.h | 2 +- src/resolve/resolved-varlink.c | 1 + 7 files changed, 37 insertions(+), 28 deletions(-) diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml index aa24d35123..c70e5c1cb3 100644 --- a/man/org.freedesktop.resolve1.xml +++ b/man/org.freedesktop.resolve1.xml @@ -440,35 +440,36 @@ node /org/freedesktop/resolve1 { and recommended. However, the following flags are defined to alter the look-up: /* Input+Output: Protocol/scope */ -#define SD_RESOLVED_DNS (UINT64_C(1) << 0) -#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) -#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) -#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3) -#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4) +#define SD_RESOLVED_DNS (UINT64_C(1) << 0) +#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) +#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) +#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) << 3) +#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) << 4) /* Input: Restrictions */ -#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) -#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) -#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) -#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8) -#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) << 10) -#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) << 11) -#define SD_RESOLVED_NO_CACHE (UINT64_C(1) << 12) -#define SD_RESOLVED_NO_ZONE (UINT64_C(1) << 13) -#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) << 14) -#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) << 15) -#define SD_RESOLVED_NO_STALE (UINT64_C(1) << 24) +#define SD_RESOLVED_NO_CNAME (UINT64_C(1) << 5) +#define SD_RESOLVED_NO_TXT (UINT64_C(1) << 6) +#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) << 7) +#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) << 8) +#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) << 10) +#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) << 11) +#define SD_RESOLVED_NO_CACHE (UINT64_C(1) << 12) +#define SD_RESOLVED_NO_ZONE (UINT64_C(1) << 13) +#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) << 14) +#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) << 15) +#define SD_RESOLVED_NO_STALE (UINT64_C(1) << 24) +#define SD_RESOLVED_RELAX_SINGLE_LABEL (UINT64_C(1) << 25) /* Output: Security */ -#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9) -#define SD_RESOLVED_CONFIDENTIAL (UINT64_C(1) << 18) +#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) << 9) +#define SD_RESOLVED_CONFIDENTIAL (UINT64_C(1) << 18) /* Output: Origin */ -#define SD_RESOLVED_SYNTHETIC (UINT64_C(1) << 19) -#define SD_RESOLVED_FROM_CACHE (UINT64_C(1) << 20) -#define SD_RESOLVED_FROM_ZONE (UINT64_C(1) << 21) -#define SD_RESOLVED_FROM_TRUST_ANCHOR (UINT64_C(1) << 22) -#define SD_RESOLVED_FROM_NETWORK (UINT64_C(1) << 23) +#define SD_RESOLVED_SYNTHETIC (UINT64_C(1) << 19) +#define SD_RESOLVED_FROM_CACHE (UINT64_C(1) << 20) +#define SD_RESOLVED_FROM_ZONE (UINT64_C(1) << 21) +#define SD_RESOLVED_FROM_TRUST_ANCHOR (UINT64_C(1) << 22) +#define SD_RESOLVED_FROM_NETWORK (UINT64_C(1) << 23) On input, the first five flags control the protocols to use for the look-up. They refer to diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index f83c654558..be2fdca21f 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -373,6 +373,7 @@ static int validate_and_mangle_flags( SD_RESOLVED_NO_TRUST_ANCHOR| SD_RESOLVED_NO_NETWORK| SD_RESOLVED_NO_STALE| + SD_RESOLVED_RELAX_SINGLE_LABEL| ok)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index b7a44f9571..f702a0a0ea 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -73,6 +73,10 @@ /* Input: Don't answer request with stale data */ #define SD_RESOLVED_NO_STALE (UINT64_C(1) << 24) +/* Input: Allow single-label lookups to Internet DNS servers */ +#define SD_RESOLVED_RELAX_SINGLE_LABEL \ + (UINT64_C(1) << 25) + #define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6) #define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6) #define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 938dd61a6a..cb2368c67a 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -741,7 +741,7 @@ int dns_query_go(DnsQuery *q) { LIST_FOREACH(scopes, s, q->manager->dns_scopes) { DnsScopeMatch match; - match = dns_scope_good_domain(s, q); + match = dns_scope_good_domain(s, q, q->flags); assert(match >= 0); if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one * that matches this well */ @@ -768,7 +768,7 @@ int dns_query_go(DnsQuery *q) { LIST_FOREACH(scopes, s, first->scopes_next) { DnsScopeMatch match; - match = dns_scope_good_domain(s, q); + match = dns_scope_good_domain(s, q, q->flags); assert(match >= 0); if (match < found) continue; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index e390715807..94c7674a8f 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -593,7 +593,8 @@ static DnsScopeMatch match_subnet_reverse_lookups( DnsScopeMatch dns_scope_good_domain( DnsScope *s, - DnsQuery *q) { + DnsQuery *q, + uint64_t query_flags) { DnsQuestion *question; const char *domain; @@ -707,7 +708,8 @@ DnsScopeMatch dns_scope_good_domain( /* If ResolveUnicastSingleLabel=yes and the query is single-label, then bump match result to prevent LLMNR monopoly among candidates. */ - if (s->manager->resolve_unicast_single_label && dns_name_is_single_label(domain)) + if ((s->manager->resolve_unicast_single_label || (query_flags & SD_RESOLVED_RELAX_SINGLE_LABEL)) && + dns_name_is_single_label(domain)) return DNS_SCOPE_YES_BASE + 1; /* Let's return the number of labels in the best matching result */ diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index ca33fd007a..e2eb023c1d 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -76,7 +76,7 @@ int dns_scope_emit_udp(DnsScope *s, int fd, int af, DnsPacket *p); int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address); int dns_scope_socket_udp(DnsScope *s, DnsServer *server); -DnsScopeMatch dns_scope_good_domain(DnsScope *s, DnsQuery *q); +DnsScopeMatch dns_scope_good_domain(DnsScope *s, DnsQuery *q, uint64_t query_flags); bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key); DnsServer *dns_scope_get_dns_server(DnsScope *s); diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index 6e6e973f94..cdd5ca41fd 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -162,6 +162,7 @@ static bool validate_and_mangle_flags( SD_RESOLVED_NO_TRUST_ANCHOR| SD_RESOLVED_NO_NETWORK| SD_RESOLVED_NO_STALE| + SD_RESOLVED_RELAX_SINGLE_LABEL| ok)) return false; From 36418a4792ace8d84ae5d4024d9cabb8d13bdbf6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2024 18:47:45 +0100 Subject: [PATCH 2/9] resolvectl: expose new SD_RESOLVED_RELAX_SINGLE_LABEL flag in resolvectl --- man/resolvectl.xml | 15 +++++++++++++++ src/resolve/resolvectl.c | 12 +++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/man/resolvectl.xml b/man/resolvectl.xml index dada3f51d7..afa4ca77eb 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -480,6 +480,21 @@ + + BOOL + + Takes a boolean parameter; used in conjunction with query. If + true, rules regarding routing of single-label names are relaxed. Defaults to false. By default, + lookups of single label names are assumed to refer to local hosts to be resolved via local resolution + such as LLMNR or via search domain qualification and are not routed to upstream servers as is. If + this option is enabled these rules are disabled and the queries are routed upstream anyway. Also see + the ResolveUnicastSingleLabel= option in + resolved.conf5 + which provides a system-wide option that controls this behaviour. + + + + diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 405e8ec215..f2e9e7a96b 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -3342,6 +3342,7 @@ static int native_help(void) { " --synthesize=BOOL Allow synthetic response (default: yes)\n" " --cache=BOOL Allow response from cache (default: yes)\n" " --stale-data=BOOL Allow response from cache with stale data (default: yes)\n" + " --relax-single-label=BOOL Allow single label lookups to go upstream (default: no)\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:\n" @@ -3701,7 +3702,8 @@ static int native_parse_argv(int argc, char *argv[]) { ARG_SEARCH, ARG_NO_PAGER, ARG_JSON, - ARG_STALE_DATA + ARG_STALE_DATA, + ARG_RELAX_SINGLE_LABEL, }; static const struct option options[] = { @@ -3726,6 +3728,7 @@ static int native_parse_argv(int argc, char *argv[]) { { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "json", required_argument, NULL, ARG_JSON }, { "stale-data", required_argument, NULL, ARG_STALE_DATA }, + { "relax-single-label", required_argument, NULL, ARG_RELAX_SINGLE_LABEL }, {} }; @@ -3912,6 +3915,13 @@ static int native_parse_argv(int argc, char *argv[]) { SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); break; + case ARG_RELAX_SINGLE_LABEL: + r = parse_boolean_argument("--relax-single-label=", optarg, NULL); + if (r < 0) + return r; + SET_FLAG(arg_flags, SD_RESOLVED_RELAX_SINGLE_LABEL, r > 0); + break; + case ARG_NO_PAGER: arg_pager_flags |= PAGER_DISABLE; break; From 718324c5e03f9892b5b416dc99b84f4131953129 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2024 18:48:16 +0100 Subject: [PATCH 3/9] resolved: use relaxed single label rules when proxying DNS queries When we use proxy mode when propagating DNS queries to upstream DNS servers, let's use the relaxed single label rules. This has the benefit that tools such "delv" work on the proxy stub 127.0.0.54. --- src/resolve/resolved-dns-stub.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 2a3c1edbba..3748496cf9 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -970,7 +970,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea SD_RESOLVED_NO_SEARCH| SD_RESOLVED_NO_VALIDATE| SD_RESOLVED_REQUIRE_PRIMARY| - SD_RESOLVED_CLAMP_TTL); + SD_RESOLVED_CLAMP_TTL| + SD_RESOLVED_RELAX_SINGLE_LABEL); else r = dns_query_new(m, &q, p->question, p->question, NULL, 0, protocol_flags| From 9c47b334445a2560a56def4f77e2c1fe1f7c965d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Mar 2024 18:49:49 +0100 Subject: [PATCH 4/9] resolved: enable DNS proxy mode if client wants DNSSEC So far we disabled DNSSEC if local clients asked for it via DO flag if DNSSEC=no is set. Let's instead switch to proxy mode in this case, and thus treat client requested DO mode as a way to force proxy mode. This means DNSSEC=no just controls whether resolved will do validation for regular looups, but it has no effect anymore on lookups from clients that indicated they want to do their own DNSSEC anyway. Fixes: #19227 #23737 #25105 --- src/resolve/resolved-dns-stub.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 3748496cf9..87f7aab6e9 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -958,8 +958,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea log_debug("Got request to DNS proxy address 127.0.0.54, enabling bypass logic."); bypass = true; protocol_flags = SD_RESOLVED_DNS|SD_RESOLVED_NO_ZONE; /* Turn off mDNS/LLMNR for proxy stub. */ - } else if ((DNS_PACKET_DO(p) && DNS_PACKET_CD(p))) { - log_debug("Got request with DNSSEC checking disabled, enabling bypass logic."); + } else if (DNS_PACKET_DO(p)) { + log_debug("Got request with DNSSEC enabled, enabling bypass logic."); bypass = true; } From df81adba8858eaad9786ad359341b6df3a16783b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Mar 2024 15:26:37 +0100 Subject: [PATCH 5/9] ci: disable test that is now answered by knot dig question with DNSSEC on will now be proxied upstream, i.e. to the test knot server. This leads to different results, but the result isn't tha tinteresting since we don't want to test knot, but resolved. Hence comment this test. There seems to be something wrong with the test though, as the upstream server refused recursion, but if so it is not suitable as an upstream server really, as resolved can only be client to a recursive resolver. --- test/units/testsuite-75.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index b92ebf6b33..96f31d23be 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -522,9 +522,9 @@ monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14" # Non-existing RR + CNAME chain -run dig +dnssec AAAA cname-chain.signed.test -grep -qF "status: NOERROR" "$RUN_OUT" -grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT" +#run dig +dnssec AAAA cname-chain.signed.test +#grep -qF "status: NOERROR" "$RUN_OUT" +#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT" : "--- ZONE: onlinesign.test (dynamic DNSSEC) ---" From 8841d1cef89116ba34738e58e8f035188fb68ecd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Mar 2024 09:48:35 +0100 Subject: [PATCH 6/9] resolved: do DNS RR type based routing So far we only looked at the domain name when routing requests to specific scopes. With this we'll also take the DNS RR type into account. This takes benefit of the fact that lookups for RRs such as SOA or NS or the various DNSSEC RR types never really make sense to be routed to LLMNR or mDNS, since they don't have concepts there. This hence refuses to route requests for those RR types to the LLMNR/mDNS scopes, which hence means they'll likely be routed to classic DNS instead. This should improve behaviour of tools that assumes it speaks to classic DNS only via 127.0.0.53, since it will now usually do that. --- src/resolve/resolved-dns-scope.c | 69 +++++++++++++++++++++++++++++++- src/resolve/resolved-dns-scope.h | 3 ++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 94c7674a8f..0b5e907bcf 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -599,7 +599,7 @@ DnsScopeMatch dns_scope_good_domain( DnsQuestion *question; const char *domain; uint64_t flags; - int ifindex; + int ifindex, r; /* This returns the following return values: * @@ -653,6 +653,11 @@ DnsScopeMatch dns_scope_good_domain( is_dns_proxy_stub_hostname(domain)) return DNS_SCOPE_NO; + /* Never send SOA or NS or DNSSEC request to LLMNR, where they make little sense. */ + r = dns_question_types_suitable_for_protocol(question, s->protocol); + if (r <= 0) + return DNS_SCOPE_NO; + switch (s->protocol) { case DNS_PROTOCOL_DNS: { @@ -1685,3 +1690,65 @@ int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret) { JSON_BUILD_PAIR_CONDITION(scope->link, "ifname", JSON_BUILD_STRING(scope->link ? scope->link->ifname : NULL)), JSON_BUILD_PAIR_VARIANT("cache", cache))); } + +int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol) { + + /* Tests whether it makes sense to route queries for the specified DNS RR types to the specified + * protocol. For classic DNS pretty much all RR types are suitable, but for LLMNR/mDNS let's + * allowlist only a few that make sense. We use this when routing queries so that we can more quickly + * return errors for queries that will almost certainly fail/time-out otherwise. For example, this + * ensures that SOA, NS, or DS/DNSKEY queries are never routed to mDNS/LLMNR where they simply make + * no sense. */ + + if (dns_type_is_obsolete(type)) + return false; + + if (!dns_type_is_valid_query(type)) + return false; + + switch (protocol) { + + case DNS_PROTOCOL_DNS: + return true; + + case DNS_PROTOCOL_LLMNR: + return IN_SET(type, + DNS_TYPE_ANY, + DNS_TYPE_A, + DNS_TYPE_AAAA, + DNS_TYPE_CNAME, + DNS_TYPE_PTR, + DNS_TYPE_TXT); + + case DNS_PROTOCOL_MDNS: + return IN_SET(type, + DNS_TYPE_ANY, + DNS_TYPE_A, + DNS_TYPE_AAAA, + DNS_TYPE_CNAME, + DNS_TYPE_PTR, + DNS_TYPE_TXT, + DNS_TYPE_SRV, + DNS_TYPE_NSEC, + DNS_TYPE_HINFO); + + default: + return -EPROTONOSUPPORT; + } +} + +int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol) { + DnsResourceKey *key; + int r; + + /* Tests whether the types in the specified question make any sense to be routed to the specified + * protocol, i.e. if dns_type_suitable_for_protocol() is true for any of the contained RR types */ + + DNS_QUESTION_FOREACH(key, q) { + r = dns_type_suitable_for_protocol(key->type, protocol); + if (r != 0) + return r; + } + + return false; +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index e2eb023c1d..4d39dc6f5d 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -112,3 +112,6 @@ int dns_scope_remove_dnssd_services(DnsScope *scope); bool dns_scope_is_default_route(DnsScope *scope); int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret); + +int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol); +int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol); From 40008b83a03f04812a7bfb2325c0e8006a7a25c8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Mar 2024 10:34:47 +0100 Subject: [PATCH 7/9] =?UTF-8?q?resolved:=20rename=20variable=20found=5F{a|?= =?UTF-8?q?aaaa}=20=E2=86=92=20question=5Ffor=5F{a|aaaa}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Te variables indicate what kind of RRs we are looking for, but the name so far suggests it was about what we already found. Let's rename the variables to make the purpose clearer. --- src/resolve/resolved-etc-hosts.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index 6af160a477..bd129cd2ca 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -491,7 +491,7 @@ static int etc_hosts_lookup_by_name( const char *name, DnsAnswer **answer) { - bool found_a = false, found_aaaa = false; + bool question_for_a = false, question_for_aaaa = false; const struct in_addr_data *a; EtcHostsItemByName *item; DnsResourceKey *t; @@ -513,6 +513,7 @@ static int etc_hosts_lookup_by_name( return 0; } + /* Determine whether we are looking for A and/or AAAA RRs */ DNS_QUESTION_FOREACH(t, q) { if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY)) continue; @@ -526,20 +527,20 @@ static int etc_hosts_lookup_by_name( continue; if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY)) - found_a = true; + question_for_a = true; if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) - found_aaaa = true; + question_for_aaaa = true; - if (found_a && found_aaaa) - break; + if (question_for_a && question_for_aaaa) + break; /* We are looking for both, no need to continue loop */ } SET_FOREACH(a, item ? item->addresses : NULL) { EtcHostsItemByAddress *item_by_addr; const char *canonical_name; - if ((!found_a && a->family == AF_INET) || - (!found_aaaa && a->family == AF_INET6)) + if ((!question_for_a && a->family == AF_INET) || + (!question_for_aaaa && a->family == AF_INET6)) continue; item_by_addr = hashmap_get(hosts->by_address, a); @@ -559,7 +560,7 @@ static int etc_hosts_lookup_by_name( return r; } - return found_a || found_aaaa; + return question_for_a || question_for_aaaa; } int manager_etc_hosts_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) { From 4b91896226b4285da94aad4b38b670036c7f23be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Mar 2024 10:35:40 +0100 Subject: [PATCH 8/9] resolved: make outselves authoritative for /etc/hosts entries in full If you query for an MX RR of a host listed in /etc/hosts, let's return an empty reply rather than NXDOMAIN, i.e. indicate that the name exists but has no MX RR assigned, thus making ourselves authoritative. The venerable "host" tool by default sends requests for A + AAAA + MX and ensures we never propagate queries further on. Fixes: #31223 --- src/resolve/resolved-etc-hosts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index bd129cd2ca..2d334d38dd 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -560,7 +560,7 @@ static int etc_hosts_lookup_by_name( return r; } - return question_for_a || question_for_aaaa; + return true; /* We consider ourselves authoritative for the whole name, all RR types, not just A/AAAA */ } int manager_etc_hosts_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) { From 6399be223b73ce520654242ad08de387b08b738a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 5 Mar 2024 13:48:59 +0100 Subject: [PATCH 9/9] resolved: make resolved authoritative in resolveing our local host name This is a kinda a follow-up for ce266330fc3bd6767451ac3400336cd9acebe9c1: it makes resolved authoritative on our local hostname, and never contacts DNS anymore for it. We effectively already were authoritative for it, except if the user queried for other RR types than just A/AAAA. This closes the gap and refuses routing other RR type queries to DNS. Fixes: #23662 --- docs/ENVIRONMENT.md | 3 ++- src/resolve/resolved-dns-query.c | 4 ++++ src/resolve/resolved-dns-scope.c | 5 +++++ src/resolve/resolved-dns-synthesize.c | 19 +++++++++++++++++-- src/resolve/resolved-dns-synthesize.h | 2 ++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 00492829bd..4d3b7a2636 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -357,7 +357,8 @@ All tools: `systemd-resolved`: * `$SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME` — if set to "0", `systemd-resolved` - won't synthesize system hostname on both regular and reverse lookups. + won't synthesize A/AAAA/PTR RRs for the system hostname on either regular nor + reverse lookups. `systemd-sysext`: diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index cb2368c67a..801bbe8007 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -672,6 +672,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC; *state = DNS_TRANSACTION_RCODE_FAILURE; + log_debug("Found synthetic NXDOMAIN response."); + return 0; } if (r <= 0) @@ -687,6 +689,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) { *state = DNS_TRANSACTION_SUCCESS; + log_debug("Found synthetic success response."); + return 1; } diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 0b5e907bcf..7f7e8626d1 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -12,6 +12,7 @@ #include "random-util.h" #include "resolved-dnssd.h" #include "resolved-dns-scope.h" +#include "resolved-dns-synthesize.h" #include "resolved-dns-zone.h" #include "resolved-llmnr.h" #include "resolved-mdns.h" @@ -653,6 +654,10 @@ DnsScopeMatch dns_scope_good_domain( is_dns_proxy_stub_hostname(domain)) return DNS_SCOPE_NO; + /* Don't look up the local host name via the network, unless user turned of local synthesis of it */ + if (manager_is_own_hostname(s->manager, domain) && shall_synthesize_own_hostname_rrs()) + return DNS_SCOPE_NO; + /* Never send SOA or NS or DNSSEC request to LLMNR, where they make little sense. */ r = dns_question_types_suitable_for_protocol(question, s->protocol); if (r <= 0) diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index 5bde29c704..6f483fdf0e 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -439,6 +439,20 @@ static int synthesize_gateway_ptr( return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address); } +bool shall_synthesize_own_hostname_rrs(void) { + static int cached = -1; + int r; + + if (cached >= 0) + return cached; + + r = secure_getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME: %m"); + + return (cached = r != 0); +} + int dns_synthesize_answer( Manager *m, DnsQuestion *q, @@ -479,8 +493,9 @@ int dns_synthesize_answer( } else if (manager_is_own_hostname(m, name)) { - if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0) + if (!shall_synthesize_own_hostname_rrs()) continue; + r = synthesize_system_hostname_rr(m, key, ifindex, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize system hostname RRs: %m"); @@ -530,7 +545,7 @@ int dns_synthesize_answer( } else if (dns_name_address(name, &af, &address) > 0) { int v, w, u; - if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0) + if (!shall_synthesize_own_hostname_rrs()) continue; v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer); diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h index bf271e862d..ca39e682b4 100644 --- a/src/resolve/resolved-dns-synthesize.h +++ b/src/resolve/resolved-dns-synthesize.h @@ -9,3 +9,5 @@ int dns_synthesize_family(uint64_t flags); DnsProtocol dns_synthesize_protocol(uint64_t flags); int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret); + +bool shall_synthesize_own_hostname_rrs(void);