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/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/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 e194ef588f..82f0c8f621 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) { @@ -764,8 +767,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 diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index b3442ad906..51e06bb91e 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), - .address.in.s_addr = htobe32(0x7F000002), + .ifindex = LOOPBACK_IFINDEX, + .address.in.s_addr = htobe32(INADDR_LOCALADDRESS), }; 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, }; @@ -260,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": */ @@ -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; @@ -356,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; @@ -405,7 +473,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"); @@ -437,15 +505,30 @@ 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, ifindex, &answer); + r = synthesize_localhost_ptr(m, key, &answer); if (r < 0) 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; @@ -458,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/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); 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 { 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 {