resolved: bind .local domains to mDNS with DNS_SCOPE_YES, similar LLMNR
authorLennart Poettering <lennart@poettering.net>
Mon, 3 Dec 2018 21:27:19 +0000 (22:27 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Dec 2018 10:41:45 +0000 (11:41 +0100)
Previously, we'd return DNS_SCOPE_MAYBE for all domain lookups matching
LLMNR or mDNS. Let's upgrade this to DNS_SCOPE_YES, to make the binding
stronger.

The effect of this is that even if "local" is defined as routing domain
on some iface, we'll still lookup domains in local via mDNS — if mDNS is
turned on. This should not be limiting, as people who don't want such
lookups should turn off mDNS altogether, as it is useless if nothing is
routed to it.

This also has the nice benefit that mDNS/LLMR continue to work if people
use "~." as routing domain on some interface.

Similar for LLMNR and single label names.

Similar also for the link local IPv4 and IPv6 reverse lookups.

Fixes: #10125

src/resolve/resolved-dns-scope.c
test/networkd-test.py

index 59f76b0..c2e4d55 100644 (file)
@@ -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");
index 176d52a..7011abc 100755 (executable)
@@ -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