]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: correctly handle non-address RR types with /etc/hosts lookups (#4808)
authorMartin Pitt <martin.pitt@ubuntu.com>
Thu, 22 Dec 2016 06:58:02 +0000 (07:58 +0100)
committerGitHub <noreply@github.com>
Thu, 22 Dec 2016 06:58:02 +0000 (07:58 +0100)
Fix wrong condition test in manager_etc_hosts_lookup(), which caused it to
return an IPv4 answer when an IPv6 question was asked, and vice versa.
Also only return success if we actually found any A or AAAA record.

In systemd-resolved.service(8), point out that /etc/hosts mappings only
affect address-type lookups, not other types.

The test case currently disables DNSSEC in resolved, as there is a bug
where "-t MX" fails due to "DNSSEC validation failed" even after
"downgrading to non-DNSSEC mode". This should be dropped once that bug
gets fixed.

Fixes #4801

man/systemd-resolved.service.xml
src/resolve/resolved-etc-hosts.c
test/networkd-test.py

index 56f67960ce35f0e4d530c69c2455d0762e37bb60..6465193cc598f7ab76a7d80d10a38b4800d5b57f 100644 (file)
       current gateway, useful for referencing it independently of the
       current network configuration state.</para></listitem>
 
-      <listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their configured
-      addresses and back.</para></listitem>
+      <listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved
+      to their configured addresses and back, but they will not affect lookups for
+      non-address types (like MX).</para></listitem>
     </itemizedlist>
 
     <para>Lookup requests are routed to the available DNS servers
index 40d650949db4b3b8b87d5c5613a2b4a496a93293..0a284825a17c1704ffbfcb55f319e738896f4818 100644 (file)
@@ -431,8 +431,8 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
         for (i = 0; i < bn->n_items; i++) {
                 _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
 
-                if ((found_a && bn->items[i]->family != AF_INET) &&
-                    (found_aaaa && bn->items[i]->family != AF_INET6))
+                if ((!found_a && bn->items[i]->family == AF_INET) ||
+                    (!found_aaaa && bn->items[i]->family == AF_INET6))
                         continue;
 
                 r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
@@ -444,5 +444,5 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
                         return r;
         }
 
-        return 1;
+        return found_a || found_aaaa;
 }
index 39bd4f5b1b3c291ded470f06fdd8444fae468387..f178a144f060aa97f5499ac7cf12ebdddde3eea7 100755 (executable)
@@ -504,6 +504,61 @@ Domains= ~company ~lab''')
         self.assertRegex(general_log, 'query.*megasearch.net')
         self.assertNotIn('megasearch.net', vpn_log)
 
+    def test_resolved_etc_hosts(self):
+        '''resolved queries to /etc/hosts'''
+
+        # FIXME: -t MX query fails with enabled DNSSEC (even when using
+        # the known negative trust anchor .internal instead of .example)
+        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')
+        self.addCleanup(os.remove, conf)
+
+        # create /etc/hosts bind mount which resolves my.example for IPv4
+        hosts = os.path.join(self.workdir, 'hosts')
+        with open(hosts, 'w') as f:
+            f.write('172.16.99.99  my.example\n')
+        subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts'])
+        self.addCleanup(subprocess.call, ['umount', '/etc/hosts'])
+        subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service'])
+
+        # note: different IPv4 address here, so that it's easy to tell apart
+        # what resolved the query
+        self.create_iface(dnsmasq_opts=['--host-record=my.example,172.16.99.1,2600::99:99',
+                                        '--host-record=other.example,172.16.0.42,2600::42',
+                                        '--mx-host=example,mail.example'],
+                          ipv6=True)
+        self.do_test(coldplug=None, ipv6=True)
+
+        try:
+            # family specific queries
+            out = subprocess.check_output(['systemd-resolve', '-4', 'my.example'])
+            self.assertIn(b'my.example: 172.16.99.99', out)
+            # we don't expect an IPv6 answer; if /etc/hosts has any IP address,
+            # it's considered a sufficient source
+            self.assertNotEqual(subprocess.call(['systemd-resolve', '-6', 'my.example']), 0)
+            # "any family" query; IPv4 should come from /etc/hosts
+            out = subprocess.check_output(['systemd-resolve', 'my.example'])
+            self.assertIn(b'my.example: 172.16.99.99', out)
+            # IP → name lookup; again, takes the /etc/hosts one
+            out = subprocess.check_output(['systemd-resolve', '172.16.99.99'])
+            self.assertIn(b'172.16.99.99: my.example', out)
+
+            # non-address RRs should fall back to DNS
+            out = subprocess.check_output(['systemd-resolve', '--type=MX', 'example'])
+            self.assertIn(b'example IN MX 1 mail.example', out)
+
+            # other domains query DNS
+            out = subprocess.check_output(['systemd-resolve', 'other.example'])
+            self.assertIn(b'172.16.0.42', out)
+            out = subprocess.check_output(['systemd-resolve', '172.16.0.42'])
+            self.assertIn(b'172.16.0.42: other.example', out)
+        except (AssertionError, subprocess.CalledProcessError):
+            self.show_journal('systemd-resolved.service')
+            self.print_server_log()
+            raise
+
     def test_transient_hostname(self):
         '''networkd sets transient hostname from DHCP'''