From 84d194f1e8bf2bb82ee22a3078201b6460fcd5e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2022 12:09:33 +0100 Subject: [PATCH 1/4] resolved: in dns stub always report "lo" as interface for "localhost" Previously, we'd return the ifindex the user asked on, and if none was specified "lo". Let's always return "lo". This should be a better choice usually, since localhost addresses are typically not reachable over arbitrary interfaces once SO_BINDTODEVICE or so is used. Hence, let's report the interface that is always right for these addresses. --- src/resolve/resolved-dns-synthesize.c | 43 +++++++++------------------ src/resolve/resolved-dns-synthesize.h | 1 - 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index b3442ad906..8e09ef20d5 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -7,20 +7,6 @@ #include "missing_network.h" #include "resolved-dns-synthesize.h" -int dns_synthesize_ifindex(int ifindex) { - - /* When the caller asked for resolving on a specific - * interface, we synthesize the answer for that - * interface. However, if nothing specific was claimed and we - * only return localhost RRs, we synthesize the answer for - * localhost. */ - - if (ifindex > 0) - return ifindex; - - return LOOPBACK_IFINDEX; -} - int dns_synthesize_family(uint64_t flags) { /* Picks an address family depending on set flags. This is @@ -57,7 +43,7 @@ DnsProtocol dns_synthesize_protocol(uint64_t flags) { return DNS_PROTOCOL_DNS; } -static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { +static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, DnsAnswer **answer) { int r; assert(m); @@ -77,7 +63,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if rr->a.in_addr.s_addr = htobe32(INADDR_LOOPBACK); - r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL); + r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL); if (r < 0) return r; } @@ -91,7 +77,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if rr->aaaa.in6_addr = in6addr_loopback; - r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED, NULL); + r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL); if (r < 0) return r; } @@ -113,7 +99,7 @@ static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, return dns_answer_add(*answer, rr, ifindex, flags, NULL); } -static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) { +static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, DnsAnswer **answer) { int r; assert(m); @@ -125,7 +111,7 @@ static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int i if (r < 0) return r; - r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; } @@ -225,20 +211,19 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, if (n == 0) { struct local_address buffer[2]; - /* If we have no local addresses then use ::1 - * and 127.0.0.2 as local ones. */ + /* If we have no local addresses then use ::1 and 127.0.0.2 as local ones. */ if (IN_SET(af, AF_INET, AF_UNSPEC)) buffer[n++] = (struct local_address) { .family = AF_INET, - .ifindex = dns_synthesize_ifindex(ifindex), + .ifindex = LOOPBACK_IFINDEX, .address.in.s_addr = htobe32(0x7F000002), }; if (IN_SET(af, AF_INET6, AF_UNSPEC) && socket_ipv6_is_enabled()) buffer[n++] = (struct local_address) { .family = AF_INET6, - .ifindex = dns_synthesize_ifindex(ifindex), + .ifindex = LOOPBACK_IFINDEX, .address.in6 = in6addr_loopback, }; @@ -268,19 +253,19 @@ static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_add if (r < 0) return r; - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->full_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->llmnr_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", m->mdns_hostname, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; - r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; @@ -405,7 +390,7 @@ int dns_synthesize_answer( } else if (is_localhost(name)) { - r = synthesize_localhost_rr(m, key, ifindex, &answer); + r = synthesize_localhost_rr(m, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize localhost RRs: %m"); @@ -440,7 +425,7 @@ int dns_synthesize_answer( } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) || dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) { - r = synthesize_localhost_ptr(m, key, ifindex, &answer); + r = synthesize_localhost_ptr(m, key, &answer); if (r < 0) return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h index fb624589d7..bf271e862d 100644 --- a/src/resolve/resolved-dns-synthesize.h +++ b/src/resolve/resolved-dns-synthesize.h @@ -5,7 +5,6 @@ #include "resolved-dns-question.h" #include "resolved-manager.h" -int dns_synthesize_ifindex(int ifindex); int dns_synthesize_family(uint64_t flags); DnsProtocol dns_synthesize_protocol(uint64_t flags); From 68d449997f8abeb6c315c33c8f8ef4f63bab3f9f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2022 12:13:20 +0100 Subject: [PATCH 2/4] resolved: don't filter _gateway/_outbound twice We already denied being responsible for these names further up, no need to test for them again. --- src/resolve/resolved-dns-scope.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b586d2c56f..22e4bfd963 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -764,8 +764,6 @@ DnsScopeMatch dns_scope_good_domain( return DNS_SCOPE_MAYBE; if ((dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */ - !is_gateway_hostname(domain) && /* don't resolve "_gateway" with LLMNR, let local synthesizing logic handle that */ - !is_outbound_hostname(domain) && /* similar for "_outbound" */ dns_name_equal(domain, "local") == 0 && /* don't resolve "local" with LLMNR, it's the top-level domain of mDNS after all, see above */ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative From 17f244e8f9de008ea1c6e0880bdc924b95a66e2b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2022 12:15:56 +0100 Subject: [PATCH 3/4] resolved: introduce the _localdnsstub and _localdnsproxy special hostnames for 127.0.0.54 + 127.0.0.53 Let's give these special IP addresses names. After all name resolution is our job here. Fixes: #23623 --- man/resolvectl.xml | 11 +-- man/systemd-resolved.service.xml | 6 ++ src/basic/hostname-util.h | 8 ++ src/resolve/resolvectl.c | 6 +- src/resolve/resolved-dns-scope.c | 7 +- src/resolve/resolved-dns-synthesize.c | 110 +++++++++++++++++++++++++- test/units/testsuite-75.sh | 11 +++ 7 files changed, 147 insertions(+), 12 deletions(-) diff --git a/man/resolvectl.xml b/man/resolvectl.xml index 2cb855c360..c966ca67bd 100644 --- a/man/resolvectl.xml +++ b/man/resolvectl.xml @@ -323,11 +323,12 @@ Takes a boolean parameter; used in conjunction with query. If true (the default), select domains are resolved on the local system, among them - localhost, _gateway and _outbound, or - entries from /etc/hosts. If false these domains are not resolved locally, and - either fail (in case of localhost, _gateway or - _outbound and suchlike) or go to the network via regular DNS/mDNS/LLMNR lookups - (in case of /etc/hosts entries). + localhost, _gateway, _outbound, + _localdnsstub and _localdnsproxy or entries from + /etc/hosts. If false these domains are not resolved locally, and either fail (in + case of localhost, _gateway or _outbound and + suchlike) or go to the network via regular DNS/mDNS/LLMNR lookups (in case of + /etc/hosts entries). diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 7f30fa6536..c006c03b53 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -118,6 +118,12 @@ local default gateway configured. This assigns a stable hostname to the local outbound IP addresses, useful for referencing them independently of the current network configuration state. + The hostname _localdnsstub is resolved to the IP address 127.0.0.53, + i.e. the address the local DNS stub (see above) is listening on. + + The hostname _localdnsproxy is resolved to the IP address 127.0.0.54, + i.e. the address the local DNS proxy (see above) is listening on. + The mappings defined in /etc/hosts are resolved to their configured addresses and back, but they will not affect lookups for non-address types (like MX). Support for /etc/hosts may be disabled with ReadEtcHosts=no, diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index a00b852395..bcac3d9fb0 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -60,4 +60,12 @@ static inline bool is_outbound_hostname(const char *hostname) { return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } +static inline bool is_dns_stub_hostname(const char *hostname) { + return STRCASE_IN_SET(hostname, "_localdnsstub", "_localdnsstub."); +} + +static inline bool is_dns_proxy_stub_hostname(const char *hostname) { + return STRCASE_IN_SET(hostname, "_localdnsproxy", "_localdnsproxy."); +} + int get_pretty_hostname(char **ret); diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index ff645fc0d7..5889bd772f 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -480,7 +480,11 @@ static bool single_label_nonsynthetic(const char *name) { if (!dns_name_is_single_label(name)) return false; - if (is_localhost(name) || is_gateway_hostname(name)) + if (is_localhost(name) || + is_gateway_hostname(name) || + is_outbound_hostname(name) || + is_dns_stub_hostname(name) || + is_dns_proxy_stub_hostname(name)) return false; r = resolve_system_hostname(NULL, &first_label); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 22e4bfd963..4d33c64a0d 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -635,8 +635,11 @@ DnsScopeMatch dns_scope_good_domain( if (dns_name_dont_resolve(domain)) return DNS_SCOPE_NO; - /* Never go to network for the _gateway or _outbound domain — they're something special, synthesized locally. */ - if (is_gateway_hostname(domain) || is_outbound_hostname(domain)) + /* Never go to network for the _gateway, _outbound, _localdnsstub, _localdnsproxy domain — they're something special, synthesized locally. */ + if (is_gateway_hostname(domain) || + is_outbound_hostname(domain) || + is_dns_stub_hostname(domain) || + is_dns_proxy_stub_hostname(domain)) return DNS_SCOPE_NO; switch (s->protocol) { diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index 8e09ef20d5..0a78078b7d 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -341,7 +341,90 @@ static int synthesize_gateway_rr( return 1; /* > 0 means: we have some gateway */ } -static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { +static int synthesize_dns_stub_rr( + Manager *m, + const DnsResourceKey *key, + in_addr_t addr, + DnsAnswer **answer) { + + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + int r; + + assert(m); + assert(key); + assert(answer); + + if (!IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) + return 1; /* we still consider ourselves the owner of this name */ + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key)); + if (!rr) + return -ENOMEM; + + rr->a.in_addr.s_addr = htobe32(addr); + + r = dns_answer_add(*answer, rr, LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED, NULL); + if (r < 0) + return r; + + return 1; +} + +static int synthesize_dns_stub_ptr( + Manager *m, + int af, + const union in_addr_union *address, + DnsAnswer **answer) { + + int r; + + assert(m); + assert(address); + assert(answer); + + if (af != AF_INET) + return 0; + + if (address->in.s_addr == htobe32(INADDR_DNS_STUB)) { + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "53.0.0.127.in-addr.arpa", "_localdnsstub", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + return 1; + } + + if (address->in.s_addr == htobe32(INADDR_DNS_PROXY_STUB)) { + + r = dns_answer_reserve(answer, 1); + if (r < 0) + return r; + + r = answer_add_ptr(answer, "54.0.0.127.in-addr.arpa", "_localdnsproxy", LOOPBACK_IFINDEX, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + return 1; + } + + return 0; +} + +static int synthesize_gateway_ptr( + Manager *m, + int af, + const union in_addr_union *address, + int ifindex, + DnsAnswer **answer) { + _cleanup_free_ struct local_address *addresses = NULL; int n; @@ -422,7 +505,22 @@ int dns_synthesize_answer( continue; } - } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0) || + } else if (is_dns_stub_hostname(name)) { + + r = synthesize_dns_stub_rr(m, key, INADDR_DNS_STUB, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize local DNS stub RRs: %m"); + + } else if (is_dns_proxy_stub_hostname(name)) { + + r = synthesize_dns_stub_rr(m, key, INADDR_DNS_PROXY_STUB, &answer); + if (r < 0) + return log_error_errno(r, "Failed to synthesize local DNS stub RRs: %m"); + + } else if ((dns_name_endswith(name, "127.in-addr.arpa") > 0 && + dns_name_equal(name, "2.0.0.127.in-addr.arpa") == 0 && + dns_name_equal(name, "53.0.0.127.in-addr.arpa") == 0 && + dns_name_equal(name, "54.0.0.127.in-addr.arpa") == 0) || dns_name_equal(name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) { r = synthesize_localhost_ptr(m, key, &answer); @@ -430,7 +528,7 @@ int dns_synthesize_answer( return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m"); } else if (dns_name_address(name, &af, &address) > 0) { - int v, w; + int v, w, u; if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0) continue; @@ -443,7 +541,11 @@ int dns_synthesize_answer( if (w < 0) return log_error_errno(w, "Failed to synthesize gateway hostname PTR RR: %m"); - if (v == 0 && w == 0) /* This IP address is neither a local one nor a gateway */ + u = synthesize_dns_stub_ptr(m, af, &address, &answer); + if (u < 0) + return log_error_errno(u, "Failed to synthesize local stub hostname PTR PR: %m"); + + if (v == 0 && w == 0 && u == 0) /* This IP address is neither a local one, nor a gateway, nor a stub address */ continue; /* Note that we never synthesize reverse PTR for _outbound, since those are local diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index 1a656fcdc1..0c68e0636f 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -56,6 +56,17 @@ echo nameserver 10.0.3.3 10.0.3.4 | "$RESOLVCONF" -a hoge.foo.dhcp assert_in '10.0.3.1 10.0.3.2' "$(resolvectl dns hoge)" assert_in '10.0.3.3 10.0.3.4' "$(resolvectl dns hoge.foo)" +# Tests for _localdnsstub and _localdnsproxy +assert_in '127.0.0.53' "$(resolvectl query _localdnsstub)" +assert_in '_localdnsstub' "$(resolvectl query 127.0.0.53)" +assert_in '127.0.0.54' "$(resolvectl query _localdnsproxy)" +assert_in '_localdnsproxy' "$(resolvectl query 127.0.0.54)" + +assert_in '127.0.0.53' "$(dig @127.0.0.53 _localdnsstub)" +assert_in '_localdnsstub' "$(dig @127.0.0.53 -x 127.0.0.53)" +assert_in '127.0.0.54' "$(dig @127.0.0.53 _localdnsproxy)" +assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)" + # Tests for mDNS and LLMNR settings mkdir -p /run/systemd/resolved.conf.d { From 056c398bdacb11775f163cbf51268bc79b6b612b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 25 Nov 2022 14:50:59 +0100 Subject: [PATCH 4/4] resolved: introduce common macro for 127.0.0.2 IP address --- src/nss-myhostname/nss-myhostname.c | 3 ++- src/resolve/resolved-dns-synthesize.c | 4 ++-- src/shared/resolve-util.h | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 120e76be45..3af1d2f0c1 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -12,6 +12,7 @@ #include "local-addresses.h" #include "macro.h" #include "nss-util.h" +#include "resolve-util.h" #include "signal-util.h" #include "socket-util.h" #include "string-util.h" @@ -21,7 +22,7 @@ * IPv6 we use ::1 which unfortunately will not translate back to the * hostname but instead something like "localhost" or so. */ -#define LOCALADDRESS_IPV4 (htobe32(0x7F000002)) +#define LOCALADDRESS_IPV4 (htobe32(INADDR_LOCALADDRESS)) #define LOCALADDRESS_IPV6 &in6addr_loopback NSS_GETHOSTBYNAME_PROTOTYPES(myhostname); diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index 0a78078b7d..51e06bb91e 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -217,7 +217,7 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, buffer[n++] = (struct local_address) { .family = AF_INET, .ifindex = LOOPBACK_IFINDEX, - .address.in.s_addr = htobe32(0x7F000002), + .address.in.s_addr = htobe32(INADDR_LOCALADDRESS), }; if (IN_SET(af, AF_INET6, AF_UNSPEC) && socket_ipv6_is_enabled()) @@ -245,7 +245,7 @@ static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_add assert(address); assert(answer); - if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) { + if (af == AF_INET && address->in.s_addr == htobe32(INADDR_LOCALADDRESS)) { /* Always map the IPv4 address 127.0.0.2 to the local hostname, in addition to "localhost": */ diff --git a/src/shared/resolve-util.h b/src/shared/resolve-util.h index e58173d864..7c9008c705 100644 --- a/src/shared/resolve-util.h +++ b/src/shared/resolve-util.h @@ -11,6 +11,9 @@ /* 127.0.0.54 in native endian (The IP address we listen on we only implement "proxy" mode) */ #define INADDR_DNS_PROXY_STUB ((in_addr_t) 0x7f000036U) +/* 127.0.0.2 is an address we always map to the local hostname. This is different from 127.0.0.1 which maps to "localhost" */ +#define INADDR_LOCALADDRESS ((in_addr_t) 0x7f000002U) + typedef enum DnsCacheMode DnsCacheMode; enum DnsCacheMode {