diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 59f76b0ae9..c2e4d55a32 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -459,6 +459,21 @@ int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *add return dns_scope_socket(s, SOCK_STREAM, family, address, server, port, ret_socket_address); } +static DnsScopeMatch accept_link_local_reverse_lookups(const char *domain) { + assert(domain); + + if (dns_name_endswith(domain, "254.169.in-addr.arpa") > 0) + return DNS_SCOPE_YES_BASE + 4; /* 4 labels match */ + + if (dns_name_endswith(domain, "8.e.f.ip6.arpa") > 0 || + dns_name_endswith(domain, "9.e.f.ip6.arpa") > 0 || + dns_name_endswith(domain, "a.e.f.ip6.arpa") > 0 || + dns_name_endswith(domain, "b.e.f.ip6.arpa") > 0) + return DNS_SCOPE_YES_BASE + 5; /* 5 labels match */ + + return _DNS_SCOPE_MATCH_INVALID; +} + DnsScopeMatch dns_scope_good_domain( DnsScope *s, int ifindex, @@ -561,25 +576,48 @@ DnsScopeMatch dns_scope_good_domain( return DNS_SCOPE_NO; } - case DNS_PROTOCOL_MDNS: + case DNS_PROTOCOL_MDNS: { + DnsScopeMatch m; + + m = accept_link_local_reverse_lookups(domain); + if (m >= 0) + return m; + if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || - (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || - (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0)) + return DNS_SCOPE_MAYBE; + + if ((dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ dns_name_equal(domain, "local") == 0 && /* but not the single-label "local" name itself */ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via mDNS */ - return DNS_SCOPE_MAYBE; + return DNS_SCOPE_YES_BASE + 1; /* Return +1, as the top-level .local domain matches, i.e. one label */ return DNS_SCOPE_NO; + } + + case DNS_PROTOCOL_LLMNR: { + DnsScopeMatch m; + + m = accept_link_local_reverse_lookups(domain); + if (m >= 0) + return m; - case DNS_PROTOCOL_LLMNR: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || - (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || - (dns_name_is_single_label(domain) && /* only resolve single label names via LLMNR */ + (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0)) + 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 nss-myhostname handle this */ manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */ - return DNS_SCOPE_MAYBE; + return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative for + * single-label names, i.e. one label. This is particular + * relevant as it means a "." route on some other scope won't + * pull all traffic away from us. (If people actually want to + * pull traffic away from us they should turn off LLMNR on the + * link) */ return DNS_SCOPE_NO; + } default: assert_not_reached("Unknown scope protocol"); diff --git a/test/networkd-test.py b/test/networkd-test.py index 176d52a028..7011abc965 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -652,7 +652,7 @@ Domains= ~company ~lab''') conf = '/run/systemd/resolved.conf.d/test-disable-dnssec.conf' os.makedirs(os.path.dirname(conf), exist_ok=True) with open(conf, 'w') as f: - f.write('[Resolve]\nDNSSEC=no') + f.write('[Resolve]\nDNSSEC=no\nLLMNR=no\nMulticastDNS=no\n') self.addCleanup(os.remove, conf) # create /etc/hosts bind mount which resolves my.example for IPv4