summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el3
-rw-r--r--.gitignore2
-rw-r--r--CODING_STYLE2
-rw-r--r--CONTRIBUTING.md38
-rw-r--r--Makefile-man.am2
-rw-r--r--Makefile.am14
-rw-r--r--README1
-rw-r--r--README.md13
-rw-r--r--catalog/systemd.catalog40
-rw-r--r--man/systemd-resolve.xml272
-rw-r--r--man/systemd-resolved.service.xml27
-rw-r--r--src/basic/fd-util.h3
-rw-r--r--src/basic/missing.h11
-rw-r--r--src/core/execute.c27
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c1
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h1
-rw-r--r--src/login/logind.c2
-rw-r--r--src/machine/machined-dbus.c2
l---------src/resolve-host/Makefile1
-rw-r--r--src/resolve/dns-type.c19
-rw-r--r--src/resolve/dns-type.h1
-rw-r--r--src/resolve/resolve-tool.c (renamed from src/resolve-host/resolve-host.c)20
-rw-r--r--src/resolve/resolved-bus.c60
-rw-r--r--src/resolve/resolved-dns-cache.c33
-rw-r--r--src/resolve/resolved-dns-dnssec.c55
-rw-r--r--src/resolve/resolved-dns-dnssec.h15
-rw-r--r--src/resolve/resolved-dns-packet.c19
-rw-r--r--src/resolve/resolved-dns-query.c473
-rw-r--r--src/resolve/resolved-dns-query.h1
-rw-r--r--src/resolve/resolved-dns-scope.c19
-rw-r--r--src/resolve/resolved-dns-scope.h2
-rw-r--r--src/resolve/resolved-dns-server.c18
-rw-r--r--src/resolve/resolved-dns-server.h5
-rw-r--r--src/resolve/resolved-dns-synthesize.c413
-rw-r--r--src/resolve/resolved-dns-synthesize.h30
-rw-r--r--src/resolve/resolved-dns-transaction.c182
-rw-r--r--src/resolve/resolved-dns-transaction.h5
-rw-r--r--src/resolve/resolved-etc-hosts.c448
-rw-r--r--src/resolve/resolved-etc-hosts.h28
-rw-r--r--src/resolve/resolved-link-bus.c2
-rw-r--r--src/resolve/resolved-link.c11
-rw-r--r--src/resolve/resolved-link.h1
-rw-r--r--src/resolve/resolved-manager.c21
-rw-r--r--src/resolve/resolved-manager.h9
-rw-r--r--src/systemd/sd-messages.h1
-rw-r--r--src/udev/udev-builtin-net_id.c9
46 files changed, 1779 insertions, 583 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index eee106213b..9388bd66c2 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -10,4 +10,5 @@
(eval . (c-set-offset 'statement-case-open 0))
(eval . (c-set-offset 'case-label 0))
(eval . (c-set-offset 'arglist-intro '++))
- (eval . (c-set-offset 'arglist-close 0)))))
+ (eval . (c-set-offset 'arglist-close 0))))
+ (nxml-mode . ((nxml-child-indent . 2))))
diff --git a/.gitignore b/.gitignore
index 549dc8357f..c045ea4378 100644
--- a/.gitignore
+++ b/.gitignore
@@ -109,7 +109,7 @@
/systemd-remount-api-vfs
/systemd-remount-fs
/systemd-reply-password
-/systemd-resolve-host
+/systemd-resolve
/systemd-resolved
/systemd-rfkill
/systemd-run
diff --git a/CODING_STYLE b/CODING_STYLE
index 0064303203..46e366898e 100644
--- a/CODING_STYLE
+++ b/CODING_STYLE
@@ -7,7 +7,7 @@
- Don't break code lines too eagerly. We do *not* force line breaks at
80ch, all of today's screens should be much larger than that. But
- then again, don't overdo it, ~140ch should be enough really.
+ then again, don't overdo it, ~119ch should be enough really.
- Variables and functions *must* be static, unless they have a
prototype, and are supposed to be exported.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..ba4080cb6e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,38 @@
+# Contributing
+
+We welcome contributions from everyone. However, please follow the following guidelines when posting a GitHub Pull
+Request or filing a GitHub Issue on the systemd project:
+
+## Filing Issues
+
+* We use GitHub Issues **exclusively** for tracking **bugs** and **feature** **requests** of systemd. If you are
+ looking for help, please contact our [mailing list](http://lists.freedesktop.org/mailman/listinfo/systemd-devel)
+ instead.
+* We only track bugs in the **two** **most** **recently** **released** **versions** of systemd in the GitHub Issue
+ tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead.
+* When filing an issue, specify the **systemd** **version** you are experiencing the issue with. Also, indicate which
+ **distribution** you are using.
+* Please include an explanation how to reproduce the issue you are pointing out.
+
+Following these guidelines makes it easier for us to process your issue, and ensures we won't close your issue
+right-away for being misfiled.
+
+## Posting Pull Requests
+
+* Make sure to post PRs only relative to a very recent git master.
+* Follow our [Coding Style](https://raw.githubusercontent.com/systemd/systemd/master/CODING_STYLE) when contributing
+ code. This is a requirement for all code we merge.
+* Make sure to run "make check" locally, before posting your PR. We use a CI system, meaning we don't even look at your
+ PR, if the build and tests don't pass.
+
+* If you need to update the code in a existing PR, please consider opening a new PR (mentioning in it which old PR it
+ replaces) and closing the old PR. This is much preferable over force-pushing a new patch set into the PR's branch, as
+ commit comments aren't lost that way. That said, we don't follow this rule ourselves quite often, hence this is
+ really just a say as we say, not say as we do...
+
+## Final Words
+
+We'd like to apologize in advance if we are not able to process and reply to your issue or PR right-away. We have a lot
+of work to do, but we are trying our best!
+
+Thank you very much for your contributions!
diff --git a/Makefile-man.am b/Makefile-man.am
index 98769fbee8..bcb67166ba 100644
--- a/Makefile-man.am
+++ b/Makefile-man.am
@@ -120,6 +120,7 @@ MANPAGES += \
man/systemd-nspawn.1 \
man/systemd-path.1 \
man/systemd-remount-fs.service.8 \
+ man/systemd-resolve.1 \
man/systemd-run.1 \
man/systemd-sleep.conf.5 \
man/systemd-socket-proxyd.8 \
@@ -2601,6 +2602,7 @@ EXTRA_DIST += \
man/systemd-quotacheck.service.xml \
man/systemd-random-seed.service.xml \
man/systemd-remount-fs.service.xml \
+ man/systemd-resolve.xml \
man/systemd-resolved.service.xml \
man/systemd-rfkill.service.xml \
man/systemd-run.xml \
diff --git a/Makefile.am b/Makefile.am
index 264a769f71..4218a6dcdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5188,6 +5188,8 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-dns-packet.c \
src/resolve/resolved-dns-query.h \
src/resolve/resolved-dns-query.c \
+ src/resolve/resolved-dns-synthesize.h \
+ src/resolve/resolved-dns-synthesize.c \
src/resolve/resolved-dns-transaction.h \
src/resolve/resolved-dns-transaction.c \
src/resolve/resolved-dns-scope.h \
@@ -5206,6 +5208,8 @@ systemd_resolved_SOURCES = \
src/resolve/resolved-dns-dnssec.c \
src/resolve/resolved-dns-trust-anchor.h \
src/resolve/resolved-dns-trust-anchor.c \
+ src/resolve/resolved-etc-hosts.h \
+ src/resolve/resolved-etc-hosts.c \
src/resolve/dns-type.c \
src/resolve/dns-type.h
@@ -5265,8 +5269,8 @@ libnss_resolve_la_LIBADD = \
lib_LTLIBRARIES += \
libnss_resolve.la
-systemd_resolve_host_SOURCES = \
- src/resolve-host/resolve-host.c \
+systemd_resolve_SOURCES = \
+ src/resolve/resolve-tool.c \
src/resolve/resolved-dns-packet.c \
src/resolve/resolved-dns-packet.h \
src/resolve/resolved-dns-rr.c \
@@ -5278,15 +5282,15 @@ systemd_resolve_host_SOURCES = \
src/resolve/dns-type.c \
src/resolve/dns-type.h
-nodist_systemd_resolve_host_SOURCES = \
+nodist_systemd_resolve_SOURCES = \
src/resolve/dns_type-from-name.h \
src/resolve/dns_type-to-name.h
-systemd_resolve_host_LDADD = \
+systemd_resolve_LDADD = \
libshared.la
rootlibexec_PROGRAMS += \
- systemd-resolve-host
+ systemd-resolve
tests += \
test-dns-domain \
diff --git a/README b/README
index 2fd7f7d5ce..c46ac7e5de 100644
--- a/README
+++ b/README
@@ -15,7 +15,6 @@ GITWEB:
MAILING LIST:
http://lists.freedesktop.org/mailman/listinfo/systemd-devel
- http://lists.freedesktop.org/mailman/listinfo/systemd-commits
IRC:
#systemd on irc.freenode.org
diff --git a/README.md b/README.md
index 5abe2073c2..dcd14ba94f 100644
--- a/README.md
+++ b/README.md
@@ -5,5 +5,14 @@
## Details
- * General information about systemd can be found in the [systemd Wiki](http://www.freedesktop.org/wiki/Software/systemd)
- * Information about build requirements are provided in the [README file](../master/README)
+General information about systemd can be found in the [systemd Wiki](http://www.freedesktop.org/wiki/Software/systemd).
+
+Information about build requirements are provided in the [README file](../master/README).
+
+Consult our [NEWS file](../master/NEWS) for information about what's new in the most recent systemd versions.
+
+Please see our [Contribution Guidelines](../master/CONTRIBUTING.md) for more information about filing GitHub Issues and posting GitHub Pull Requests.
+
+When preparing patches for systemd, please follow our [Coding Style Guidelines](../master/CODING_STYLE).
+
+If you are looking for support, please contact our [mailing list](http://lists.freedesktop.org/mailman/listinfo/systemd-devel) or join our [IRC channel](irc://irc.freenode.org/%23systemd).
diff --git a/catalog/systemd.catalog b/catalog/systemd.catalog
index 4488c835a8..696f4ed618 100644
--- a/catalog/systemd.catalog
+++ b/catalog/systemd.catalog
@@ -1,3 +1,4 @@
+# -*- fill-column: 79; indent-tabs-mode: nil -*-
# This file is part of systemd.
#
# Copyright 2012 Lennart Poettering
@@ -278,3 +279,42 @@ Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
The virtual machine @NAME@ with its leader PID @LEADER@ has been
shut down.
+
+-- 36db2dfa5a9045e1bd4af5f93e1cf057
+Subject: DNSSEC mode has been turned off, as server doesn't support it
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+
+The resolver service (systemd-resolved.service) has detected that the
+configured DNS server does not support DNSSEC, and DNSSEC validation has been
+turned off as result.
+
+This event will take place if DNSSEC=allow-downgrade is configured in
+resolved.conf and the configured DNS server is incompatible with DNSSEC. Note
+that using this mode permits DNSSEC downgrade attacks, as an attacker might be
+able turn off DNSSEC validation on the system by inserting DNS replies in the
+communication channel that result in a downgrade like this.
+
+This event might be indication that the DNS server is indeed incompatible with
+DNSSEC or that an attacker has successfully managed to stage such a downgrade
+attack.
+
+-- 1675d7f172174098b1108bf8c7dc8f5d
+Subject: DNSSEC validation failed
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+A DNS query or resource record set failed DNSSEC validation. This is usually
+indication that the communication channel used was tampered with.
+
+-- 4d4408cfd0d144859184d1e65d7c8a65
+Subject: A DNSSEC trust anchor has been revoked
+Defined-By: systemd
+Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
+Documentation: man:systemd-resolved.service(8)
+
+A DNSSEC trust anchor has been revoked. A new trust anchor has to be
+configured, or the operating system needs to be updated, to provide an updated
+DNSSEC trust anchor.
diff --git a/man/systemd-resolve.xml b/man/systemd-resolve.xml
new file mode 100644
index 0000000000..c5ad6a0e3e
--- /dev/null
+++ b/man/systemd-resolve.xml
@@ -0,0 +1,272 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="systemd-resolve"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-resolve</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Lennart</firstname>
+ <surname>Poettering</surname>
+ <email>lennart@poettering.net</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-resolve</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-resolve</refname>
+ <refpurpose>Resolve domain names, IPV4 and IPv6 addresses, DNS resource records, and services</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain" rep="repeat"><replaceable>HOSTNAME</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain" rep="repeat"><replaceable>ADDRESS</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <command>--type=<replaceable>TYPE</replaceable></command>
+ <arg choice="plain" rep="repeat"><replaceable>RRDOMAIN</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <command>--service</command>
+ <arg choice="plain"><arg choice="opt"><arg choice="opt"><replaceable>NAME</replaceable></arg>
+ <replaceable>TYPE</replaceable></arg> <replaceable>DOMAIN</replaceable></arg>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <command>--statistics</command>
+ </cmdsynopsis>
+
+ <cmdsynopsis>
+ <command>systemd-resolve</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <command>--reset-statistics</command>
+ </cmdsynopsis>
+
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-resolve</command> may be used to resolve domain names, IPv4 and IPv6 addresses, DNS resource
+ records and services with the
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ resolver service. By default, the specified list of parameters will be resolved as hostnames, retrieving their IPv4
+ and IPv6 addresses. If the parameters specified are formatted as IPv4 or IPv6 operation the reverse operation is
+ done, and a hostname is retrieved for the specified addresses.</para>
+
+ <para>The <option>--type=</option> switch may be used to specify a DNS resource record type (A, AAAA, SOA, MX, ...) in
+ order to request a specific DNS resource record, instead of the address or reverse address lookups.</para>
+
+ <para>The <option>--service</option> switch may be used to resolve <ulink
+ url="https://tools.ietf.org/html/rfc2782">SRV</ulink> and <ulink
+ url="https://tools.ietf.org/html/rfc6763">DNS-SD</ulink> services (see below). In this mode, between one and three
+ arguments are required. If three parameters are passed the first is assumed to be the DNS-SD service name, the
+ second the SRV service type, and the third the domain to search in. In this case a full DNS-SD style SRV and TXT
+ lookup is executed. If only two parameters are specified, the first is assumed to be the SRV service type, and the
+ second the domain to look in. In this case no TXT RR is requested. Finally, if only one parameter is specified, it
+ is assumed to be a domain name, that is already prefixed with an SRV type, and an SRV lookup is done (no
+ TXT).</para>
+
+ <para>The <option>--statistics</option> switch may be used to show resolver statistics, including information about
+ the number of succesful and failed DNSSEC validations.</para>
+
+ <para>The <option>--reset-statistics</option> may be used to reset various statistics counters maintained the
+ resolver, including those shown in the <option>--statistics</option> output. This operation requires root
+ privileges.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Options</title>
+ <variablelist>
+ <varlistentry>
+ <term><option>-4</option></term>
+ <term><option>-6</option></term>
+
+ <listitem><para>By default, when resolving a hostname, both IPv4 and IPv6
+ addresses are acquired. By specifying <option>-4</option> only IPv4 addresses are requested, by specifying
+ <option>-6</option> only IPv6 addresses are requested.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-i</option> <replaceable>INTERFACE</replaceable></term>
+ <term><option>--interface=</option><replaceable>INTERFACE</replaceable></term>
+
+ <listitem><para>Specifies the network interface to execute the query on. This may either be specified as numeric
+ interface index or as network interface string (e.g. <literal>en0</literal>). Note that this option has no
+ effect if system-wide DNS configuration (as configured in <filename>/etc/resolv.conf</filename> or
+ <filename>/etc/systemd/resolve.conf</filename>) in place of per-link configuration is used.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-p</option> <replaceable>PROTOCOL</replaceable></term>
+ <term><option>--protocol=</option><replaceable>PROTOCOL</replaceable></term>
+
+ <listitem><para>Specifies the network protocol for the query. May be one of <literal>dns</literal>
+ (i.e. classic unicast DNS), <literal>llmnr</literal> (<ulink
+ url="https://tools.ietf.org/html/rfc4795">Link-Local Multicast Name Resolution</ulink>),
+ <literal>llmr-ipv4</literal>, <literal>llmnr-ipv6</literal> (LLMNR via the indicated underlying IP
+ protocols). By default the lookup is done via all protocols suitable for the lookup. If used, limits the set of
+ protocols that may be used. Use this option multiple times to enable resolving via multiple protocols at the
+ same time. The setting <literal>llmnr</literal> is identical to specifying this switch once with
+ <literal>llmnr-ipv4</literal> and once via <literal>llmnr-ipv6</literal>. Note that this option does not force
+ the service to resolve the operation with the specified protocol, as that might require a suitable network
+ interface and configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>-t</option> <replaceable>TYPE</replaceable></term>
+ <term><option>--type=</option><replaceable>TYPE</replaceable></term>
+ <term><option>-c</option> <replaceable>CLASS</replaceable></term>
+ <term><option>--class=</option><replaceable>CLASS</replaceable></term>
+
+ <listitem><para>Specifies the DNS resource record type (e.g. A, AAAA, MX, …) and class (e.g. IN, ANY, …) to
+ look up. If these options are used a DNS resource record set matching the specified class and type is
+ requested. The class defaults to IN if only a type is specified.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--service</option></term>
+
+ <listitem><para>Enables service resolution. This enables DNS-SD and simple SRV service resolution, dependending
+ on the specified list of parameters (see above).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--service-address=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean parameter. If true (the default), when doing a service lookup with
+ <option>--service</option> the hostnames contained in the SRV resource records are resolved as well.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--service-txt=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean parameter. If true (the default), when doing a DNS-SD service lookup with
+ <option>--service</option> the TXT service metadata record is resolved as well.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--cname=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean parameter. If true (the default), DNS CNAME or DNAME redirections are
+ followed. Otherwise, if a CNAME or DNAME record is encountered while resolving, an error is
+ returned.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--search=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean parameter. If true (the default), any specified single-label hostnames will be
+ searched in the domains configured in the search domain list, if it is non-empty. Otherwise, the search domain
+ logic is disabled.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--legend=</option><replaceable>BOOL</replaceable></term>
+
+ <listitem><para>Takes a boolean parameter. If true (the default), column headers and meta information about the
+ query response are shown. Otherwise, this output is suppressed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--statistics</option></term>
+
+ <listitem><para>If specified general resolver statistics are shown, including information whether DNSSEC is
+ enabled and available, as well as resolution and validation statistics.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--reset-statistics</option></term>
+
+ <listitem><para>Resets the statistics counters shown in <option>--statistics</option> to zero.</para></listitem>
+ </varlistentry>
+
+ <xi:include href="standard-options.xml" xpointer="help" />
+ <xi:include href="standard-options.xml" xpointer="version" />
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <example>
+ <title>Retrieve the addresses of the <literal>www.0pointer.net</literal> domain</title>
+
+ <programlisting>$ systemd-resolve www.0pointer.net</programlisting>
+ </example>
+
+ <example>
+ <title>Retrieve the domain of the <literal>85.214.157.71</literal> IP address</title>
+
+ <programlisting>$ systemd-resolve 85.214.157.71</programlisting>
+ </example>
+
+ <example>
+ <title>Retrieve the MX record of the <literal>0pointer.net</literal> domain</title>
+
+ <programlisting>$ systemd-resolve -t MX 0pointer.net</programlisting>
+ </example>
+
+ <example>
+ <title>Resolve an SRV service</title>
+
+ <programlisting>$ systemd-resolve --service _xmpp-server._tcp gmail.com</programlisting>
+ </example>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+</refentry>
diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml
index 8e1ca1c092..ba36c5d9f0 100644
--- a/man/systemd-resolved.service.xml
+++ b/man/systemd-resolved.service.xml
@@ -56,15 +56,15 @@
<refsect1>
<title>Description</title>
- <para><command>systemd-resolved</command> is a system service that
- manages network name resolution. It implements a caching DNS stub
- resolver and an LLMNR resolver and responder. It also generates
- <filename>/run/systemd/resolve/resolv.conf</filename> for
- compatibility which may be symlinked from
- <filename>/etc/resolv.conf</filename>. The glibc NSS module
- <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- is necessary to allow libc's NSS resolver functions to resolve
- host names via <command>systemd-resolved</command>.</para>
+ <para><command>systemd-resolved</command> is a system service that provides network name resolution to local
+ applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR resolver and
+ responder. In addition it maintains the <filename>/run/systemd/resolve/resolv.conf</filename> file for
+ compatibility with traditional Linux programs. This file may be symlinked from
+ <filename>/etc/resolv.conf</filename>.</para>
+
+ <para>The glibc NSS module
+ <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry> is required to
+ permit glibc's NSS resolver functions to resolve host names via <command>systemd-resolved</command>.</para>
<para>The DNS servers contacted are determined from the global
settings in <filename>/etc/systemd/resolved.conf</filename>, the
@@ -104,7 +104,7 @@
<itemizedlist>
<listitem><para>Lookups for the special hostname
<literal>localhost</literal> are never routed to the
- network.</para></listitem>
+ network. (A few other, special domains are handled the same way.)</para></listitem>
<listitem><para>Single-label names are routed to all local
interfaces capable of IP multicasting, using the LLMNR
@@ -133,10 +133,8 @@
per-interface domains are exclusively routed to the matching
interfaces.</para>
- <para>Note that
- <filename>/run/systemd/resolve/resolv.conf</filename> should not
- be used directly, but only through a symlink from
- <filename>/etc/resolv.conf</filename>.</para>
+ <para>Note that <filename>/run/systemd/resolve/resolv.conf</filename> should not be used directly by applications,
+ but only through a symlink from <filename>/etc/resolv.conf</filename>.</para>
</refsect1>
<refsect1>
@@ -146,6 +144,7 @@
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-resolve</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 973413ff42..20890e3279 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -74,5 +74,6 @@ void cmsg_close_all(struct msghdr *mh);
bool fdname_is_valid(const char *s);
+/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
- IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE)
+ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
diff --git a/src/basic/missing.h b/src/basic/missing.h
index c187afa287..6ed2133ed1 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -1149,3 +1149,14 @@ static inline key_serial_t request_key(const char *type, const char *description
#ifndef PR_CAP_AMBIENT_CLEAR_ALL
#define PR_CAP_AMBIENT_CLEAR_ALL 4
#endif
+
+/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway,
+ * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with
+ * linux/if.h. */
+#ifndef IF_OPER_UNKNOWN
+#define IF_OPER_UNKNOWN 0
+#endif
+
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
diff --git a/src/core/execute.c b/src/core/execute.c
index d70ba2be17..b9de2617a9 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -814,8 +814,7 @@ static int setup_pam(
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
pam_handle_t *handle = NULL;
sigset_t old_ss;
- int pam_code = PAM_SUCCESS;
- int err = 0;
+ int pam_code = PAM_SUCCESS, r;
char **e = NULL;
bool close_session = false;
pid_t pam_pid = 0, parent_pid;
@@ -832,8 +831,8 @@ static int setup_pam(
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
- err = barrier_create(&barrier);
- if (err < 0)
+ r = barrier_create(&barrier);
+ if (r < 0)
goto fail;
if (log_get_max_level() < LOG_DEBUG)
@@ -875,12 +874,13 @@ static int setup_pam(
parent_pid = getpid();
pam_pid = fork();
- if (pam_pid < 0)
+ if (pam_pid < 0) {
+ r = -errno;
goto fail;
+ }
if (pam_pid == 0) {
- int sig;
- int r = EXIT_PAM;
+ int sig, ret = EXIT_PAM;
/* The child's job is to reset the PAM session on
* termination */
@@ -945,11 +945,11 @@ static int setup_pam(
goto child_finish;
}
- r = 0;
+ ret = 0;
child_finish:
pam_end(handle, pam_code | flags);
- _exit(r);
+ _exit(ret);
}
barrier_set_role(&barrier, BARRIER_PARENT);
@@ -978,10 +978,9 @@ static int setup_pam(
fail:
if (pam_code != PAM_SUCCESS) {
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
- err = -EPERM; /* PAM errors do not map to errno */
- } else {
- err = log_error_errno(err < 0 ? err : errno, "PAM failed: %m");
- }
+ r = -EPERM; /* PAM errors do not map to errno */
+ } else
+ log_error_errno(r, "PAM failed: %m");
if (handle) {
if (close_session)
@@ -993,7 +992,7 @@ fail:
strv_free(e);
closelog();
- return err;
+ return r;
}
#endif
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index 9ddc9b5aaf..e344b3b77b 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -80,6 +80,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_RR_TYPE_UNSUPPORTED, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_LINK, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_LINK_BUSY, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_DOWN, ENETDOWN),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO),
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index e93b6ac448..130779e8e3 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -79,6 +79,7 @@
#define BUS_ERROR_RR_TYPE_UNSUPPORTED "org.freedesktop.resolve1.ResourceRecordTypeUnsupported"
#define BUS_ERROR_NO_SUCH_LINK "org.freedesktop.resolve1.NoSuchLink"
#define BUS_ERROR_LINK_BUSY "org.freedesktop.resolve1.LinkBusy"
+#define BUS_ERROR_NETWORK_DOWN "org.freedesktop.resolve1.NetworkDown"
#define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError."
#define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer"
diff --git a/src/login/logind.c b/src/login/logind.c
index 79ea5ddfcb..9723e2f4e0 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -70,7 +70,7 @@ static Manager *manager_new(void) {
m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
- m->user_tasks_max = UINT64_C(4096);
+ m->user_tasks_max = UINT64_C(12288);
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index e448dd2035..28134f61bf 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -1325,7 +1325,7 @@ int manager_start_scope(
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", 8192);
+ r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", UINT64_C(16384));
if (r < 0)
return bus_log_create_error(r);
diff --git a/src/resolve-host/Makefile b/src/resolve-host/Makefile
deleted file mode 120000
index d0b0e8e008..0000000000
--- a/src/resolve-host/Makefile
+++ /dev/null
@@ -1 +0,0 @@
-../Makefile \ No newline at end of file
diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c
index 058d14009a..56720646ca 100644
--- a/src/resolve/dns-type.c
+++ b/src/resolve/dns-type.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
+
#include "dns-type.h"
#include "string-util.h"
@@ -183,6 +185,23 @@ bool dns_type_is_obsolete(uint16_t type) {
DNS_TYPE_NULL);
}
+int dns_type_to_af(uint16_t t) {
+ switch (t) {
+
+ case DNS_TYPE_A:
+ return AF_INET;
+
+ case DNS_TYPE_AAAA:
+ return AF_INET6;
+
+ case DNS_TYPE_ANY:
+ return AF_UNSPEC;
+
+ default:
+ return -EINVAL;
+ }
+}
+
const char *dns_class_to_string(uint16_t class) {
switch (class) {
diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h
index 78ff71b06e..60ff160383 100644
--- a/src/resolve/dns-type.h
+++ b/src/resolve/dns-type.h
@@ -133,6 +133,7 @@ bool dns_type_is_dnssec(uint16_t type);
bool dns_type_is_obsolete(uint16_t type);
bool dns_type_may_wildcard(uint16_t type);
bool dns_type_apex_only(uint16_t type);
+int dns_type_to_af(uint16_t t);
bool dns_class_is_pseudo(uint16_t class);
bool dns_class_is_valid_rr(uint16_t class);
diff --git a/src/resolve-host/resolve-host.c b/src/resolve/resolve-tool.c
index 54a060ea5a..fdaeb8d926 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve/resolve-tool.c
@@ -867,11 +867,11 @@ static int show_statistics(sd_bus *bus) {
if (r < 0)
return bus_log_parse_error(r);
- printf("\n%sDNSSEC%s\n"
- " Secure RRsets: %" PRIu64 "\n"
- " Insecure RRsets: %" PRIu64 "\n"
- " Bogus RRsets: %" PRIu64 "\n"
- "Indeterminate RRsets: %" PRIu64 "\n",
+ printf("\n%sDNSSEC Verdicts%s\n"
+ " Secure: %" PRIu64 "\n"
+ " Insecure: %" PRIu64 "\n"
+ " Bogus: %" PRIu64 "\n"
+ " Indeterminate: %" PRIu64 "\n",
ansi_highlight(),
ansi_normal(),
n_dnssec_secure,
@@ -929,7 +929,7 @@ static void help_dns_classes(void) {
static void help(void) {
printf("%s [OPTIONS...] NAME...\n"
"%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
- "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+ "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" -4 Resolve IPv4 addresses\n"
@@ -943,7 +943,7 @@ static void help(void) {
" --service-txt=BOOL Do [not] resolve TXT records for services\n"
" --cname=BOOL Do [not] follow CNAME redirects\n"
" --search=BOOL Do [not] use search domains\n"
- " --legend=BOOL Do [not] print column headers\n"
+ " --legend=BOOL Do [not] print column headers and meta information\n"
" --statistics Show resolver statistics\n"
" --reset-statistics Reset resolver statistics\n"
, program_invocation_short_name, program_invocation_short_name);
@@ -1175,7 +1175,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_HOST:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1203,7 +1203,7 @@ int main(int argc, char **argv) {
case MODE_RESOLVE_RECORD:
if (optind >= argc) {
- log_error("No arguments passed");
+ log_error("No arguments passed.");
r = -EINVAL;
goto finish;
}
@@ -1232,7 +1232,7 @@ int main(int argc, char **argv) {
else if (argc == optind + 3)
r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
else {
- log_error("Too many arguments");
+ log_error("Too many arguments.");
r = -EINVAL;
goto finish;
}
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 9110ea52a6..d8e8638327 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -43,8 +43,8 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_INVALID_REPLY:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
- case DNS_TRANSACTION_RESOURCES:
- return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
+ case DNS_TRANSACTION_ERRNO:
+ return sd_bus_reply_method_errnof(q->request, q->answer_errno, "Lookup failed due to system error: %m");
case DNS_TRANSACTION_ABORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
@@ -59,6 +59,14 @@ static int reply_query_state(DnsQuery *q) {
case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
+ case DNS_TRANSACTION_NETWORK_DOWN:
+ return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NETWORK_DOWN, "Network is down");
+
+ case DNS_TRANSACTION_NOT_FOUND:
+ /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we
+ * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */
+ return sd_bus_reply_method_errorf(q->request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
+
case DNS_TRANSACTION_RCODE_FAILURE: {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -66,7 +74,7 @@ static int reply_query_state(DnsQuery *q) {
sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
else {
const char *rc, *n;
- char p[3]; /* the rcode is 4 bits long */
+ char p[DECIMAL_STR_MAX(q->answer_rcode)];
rc = dns_rcode_to_string(q->answer_rcode);
if (!rc) {
@@ -133,8 +141,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ DnsResourceRecord *rr;
unsigned added = 0;
- int r;
+ int ifindex, r;
assert(q);
@@ -161,30 +170,25 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
if (r < 0)
goto finish;
- if (q->answer) {
- DnsResourceRecord *rr;
- int ifindex;
-
- DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
- DnsQuestion *question;
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ DnsQuestion *question;
- question = dns_query_question_for_protocol(q, q->answer_protocol);
+ question = dns_query_question_for_protocol(q, q->answer_protocol);
- r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
+ r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
- r = append_address(reply, rr, ifindex);
- if (r < 0)
- goto finish;
+ r = append_address(reply, rr, ifindex);
+ if (r < 0)
+ goto finish;
- if (!canonical)
- canonical = dns_resource_record_ref(rr);
+ if (!canonical)
+ canonical = dns_resource_record_ref(rr);
- added ++;
- }
+ added ++;
}
if (added <= 0) {
@@ -1293,10 +1297,10 @@ static int bus_property_get_dnssec_statistics(
assert(m);
return sd_bus_message_append(reply, "(tttt)",
- (uint64_t) m->n_dnssec_secure,
- (uint64_t) m->n_dnssec_insecure,
- (uint64_t) m->n_dnssec_bogus,
- (uint64_t) m->n_dnssec_indeterminate);
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_SECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INSECURE],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_BOGUS],
+ (uint64_t) m->n_dnssec_verdict[DNSSEC_INDETERMINATE]);
}
static int bus_property_get_dnssec_supported(
@@ -1327,7 +1331,7 @@ static int bus_method_reset_statistics(sd_bus_message *message, void *userdata,
s->cache.n_hit = s->cache.n_miss = 0;
m->n_transactions_total = 0;
- m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
+ zero(m->n_dnssec_verdict);
return sd_bus_reply_method_return(message, NULL);
}
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index fdb34d11df..9267b67f79 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -579,6 +579,28 @@ static void dns_cache_remove_previous(
}
}
+static bool rr_eligible(DnsResourceRecord *rr) {
+ assert(rr);
+
+ /* When we see an NSEC/NSEC3 RR, we'll only cache it if it is from the lower zone, not the upper zone, since
+ * that's where the interesting bits are (with exception of DS RRs). Of course, this way we cannot derive DS
+ * existence from any cached NSEC/NSEC3, but that should be fine. */
+
+ switch (rr->key->type) {
+
+ case DNS_TYPE_NSEC:
+ return !bitmap_isset(rr->nsec.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec.types, DNS_TYPE_SOA);
+
+ case DNS_TYPE_NSEC3:
+ return !bitmap_isset(rr->nsec3.types, DNS_TYPE_NS) ||
+ bitmap_isset(rr->nsec3.types, DNS_TYPE_SOA);
+
+ default:
+ return true;
+ }
+}
+
int dns_cache_put(
DnsCache *c,
DnsResourceKey *key,
@@ -635,6 +657,12 @@ int dns_cache_put(
if ((flags & DNS_ANSWER_CACHEABLE) == 0)
continue;
+ r = rr_eligible(rr);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
r = dns_cache_put_positive(
c,
rr,
@@ -835,7 +863,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r
have_non_authenticated = true;
}
- if (nsec && key->type != DNS_TYPE_NSEC) {
+ if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) {
+ /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from
+ * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */
+
if (log_get_max_level() >= LOG_DEBUG) {
r = dns_resource_key_to_string(key, &key_str);
if (r < 0)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 1f48f588ce..8e3c78e7bf 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -35,9 +35,6 @@
*
* TODO:
*
- * - bus calls to override DNSEC setting per interface
- * - log all DNSSEC downgrades
- * - log all RRs that failed validation
* - enable by default
* - Allow clients to request DNSSEC even if DNSSEC is off
* - make sure when getting an NXDOMAIN response through CNAME, we still process the first CNAMEs in the packet
@@ -1270,11 +1267,12 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) {
if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX)
return 0;
- /* Ignore NSEC3 RRs generated from wildcards */
- if (rr->n_skip_labels_source != 0)
+ /* Ignore NSEC3 RRs generated from wildcards. If these NSEC3 RRs weren't correctly signed we can't make this
+ * check (since rr->n_skip_labels_source is -1), but that's OK, as we won't trust them anyway in that case. */
+ if (rr->n_skip_labels_source != 0 && rr->n_skip_labels_source != (unsigned) -1)
return 0;
/* Ignore NSEC3 RRs that are located anywhere else than one label below the zone */
- if (rr->n_skip_labels_signer != 1)
+ if (rr->n_skip_labels_signer != 1 && rr->n_skip_labels_signer != (unsigned) -1)
return 0;
if (!nsec3)
@@ -1458,19 +1456,20 @@ found_zone:
found_closest_encloser:
/* We found a closest encloser in 'p'; next closer is 'pp' */
- /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
- return -EBADMSG;
-
- /* Ensure that this data is from the delegated domain
- * (i.e. originates from the "lower" DNS server), and isn't
- * just glue records (i.e. doesn't originate from the "upper"
- * DNS server). */
- if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
- !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
- return -EBADMSG;
-
if (!pp) {
+ /* We have an exact match! If we area looking for a DS RR, then we must insist that we got the NSEC3 RR
+ * from the parent. Otherwise the one from the child. Do so, by checking whether SOA and NS are
+ * appropriately set. */
+
+ if (key->type == DNS_TYPE_DS) {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ } else {
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+ }
+
/* No next closer NSEC3 RR. That means there's a direct NSEC3 RR for our key. */
if (bitmap_isset(enclosure_rr->nsec3.types, key->type))
*result = DNSSEC_NSEC_FOUND;
@@ -1487,6 +1486,18 @@ found_closest_encloser:
return 0;
}
+ /* Ensure this is not a DNAME domain, see RFC5155, section 8.3. */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_DNAME))
+ return -EBADMSG;
+
+ /* Ensure that this data is from the delegated domain
+ * (i.e. originates from the "lower" DNS server), and isn't
+ * just glue records (i.e. doesn't originate from the "upper"
+ * DNS server). */
+ if (bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_NS) &&
+ !bitmap_isset(enclosure_rr->nsec3.types, DNS_TYPE_SOA))
+ return -EBADMSG;
+
/* Prove that there is no next closer and whether or not there is a wildcard domain. */
wildcard = strjoina("*.", p);
@@ -2129,3 +2140,11 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
+
+static const char* const dnssec_verdict_table[_DNSSEC_VERDICT_MAX] = {
+ [DNSSEC_SECURE] = "secure",
+ [DNSSEC_INSECURE] = "insecure",
+ [DNSSEC_BOGUS] = "bogus",
+ [DNSSEC_INDETERMINATE] = "indeterminate",
+};
+DEFINE_STRING_TABLE_LOOKUP(dnssec_verdict, DnssecVerdict);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
index 955017e8cb..c99861b8e5 100644
--- a/src/resolve/resolved-dns-dnssec.h
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -21,8 +21,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-typedef enum DnssecMode DnssecMode;
typedef enum DnssecResult DnssecResult;
+typedef enum DnssecVerdict DnssecVerdict;
#include "dns-domain.h"
#include "resolved-dns-answer.h"
@@ -50,6 +50,16 @@ enum DnssecResult {
_DNSSEC_RESULT_INVALID = -1
};
+enum DnssecVerdict {
+ DNSSEC_SECURE,
+ DNSSEC_INSECURE,
+ DNSSEC_BOGUS,
+ DNSSEC_INDETERMINATE,
+
+ _DNSSEC_VERDICT_MAX,
+ _DNSSEC_VERDICT_INVALID = -1
+};
+
#define DNSSEC_CANONICAL_HOSTNAME_MAX (DNS_HOSTNAME_MAX + 2)
/* The longest digest we'll ever generate, of all digest algorithms we support */
@@ -90,3 +100,6 @@ int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *so
const char* dnssec_result_to_string(DnssecResult m) _const_;
DnssecResult dnssec_result_from_string(const char *s) _pure_;
+
+const char* dnssec_verdict_to_string(DnssecVerdict m) _const_;
+DnssecVerdict dnssec_verdict_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 9a5223ef01..032e719595 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -2094,7 +2094,7 @@ int dns_packet_extract(DnsPacket *p) {
n = DNS_PACKET_RRCOUNT(p);
if (n > 0) {
- DnsResourceRecord *previous = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL;
bool bad_opt = false;
answer = dns_answer_new(n);
@@ -2150,12 +2150,12 @@ int dns_packet_extract(DnsPacket *p) {
}
if (has_rfc6975) {
- /* OPT RR contains RFC6975 algorithm data, then this is indication that the
- * server just copied the OPT it got from us (which contained that data) back
- * into the reply. If so, then it doesn't properly support EDNS, as RFC6975
- * makes it very clear that the algorithm data should only be contained in
- * questions, never in replies. Crappy Belkin copy the OPT data for example,
- * hence let's detect this so that we downgrade early. */
+ /* If the OPT RR contains RFC6975 algorithm data, then this is indication that
+ * the server just copied the OPT it got from us (which contained that data)
+ * back into the reply. If so, then it doesn't properly support EDNS, as
+ * RFC6975 makes it very clear that the algorithm data should only be contained
+ * in questions, never in replies. Crappy Belkin routers copy the OPT data for
+ * example, hence let's detect this so that we downgrade early. */
log_debug("OPT RR contained RFC6975 data, ignoring.");
bad_opt = true;
continue;
@@ -2174,6 +2174,11 @@ int dns_packet_extract(DnsPacket *p) {
if (r < 0)
goto finish;
}
+
+ /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note
+ * that we only do this if we actually decided to keep the RR around. */
+ dns_resource_record_unref(previous);
+ previous = dns_resource_record_ref(rr);
}
if (bad_opt)
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index fc5bf4020f..ef977b0948 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -24,6 +24,8 @@
#include "hostname-util.h"
#include "local-addresses.h"
#include "resolved-dns-query.h"
+#include "resolved-dns-synthesize.h"
+#include "resolved-etc-hosts.h"
#include "string-util.h"
/* How long to wait for the query in total */
@@ -180,7 +182,7 @@ static DnsTransactionState dns_query_candidate_state(DnsQueryCandidate *c) {
assert(c);
if (c->error_code != 0)
- return DNS_TRANSACTION_RESOURCES;
+ return DNS_TRANSACTION_ERRNO;
SET_FOREACH(t, c->transactions, i) {
@@ -322,6 +324,7 @@ static void dns_query_reset_answer(DnsQuery *q) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = 0;
q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = 0;
q->answer_authenticated = false;
q->answer_protocol = _DNS_PROTOCOL_INVALID;
q->answer_family = AF_UNSPEC;
@@ -547,413 +550,73 @@ fail:
return r;
}
-static int 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;
-}
-
-static int SYNTHESIZE_FAMILY(uint64_t flags) {
-
- /* Picks an address family depending on set flags. This is
- * purely for synthesized answers, where the family we return
- * for the reply should match what was requested in the
- * question, even though we are synthesizing the answer
- * here. */
-
- if (!(flags & SD_RESOLVED_DNS)) {
- if (flags & SD_RESOLVED_LLMNR_IPV4)
- return AF_INET;
- if (flags & SD_RESOLVED_LLMNR_IPV6)
- return AF_INET6;
- }
-
- return AF_UNSPEC;
-}
-
-static DnsProtocol SYNTHESIZE_PROTOCOL(uint64_t flags) {
-
- /* Similar as SYNTHESIZE_FAMILY() but does this for the
- * protocol. If resolving via DNS was requested, we claim it
- * was DNS. Similar, if nothing specific was
- * requested. However, if only resolving via LLMNR was
- * requested we return that. */
-
- if (flags & SD_RESOLVED_DNS)
- return DNS_PROTOCOL_DNS;
- if (flags & SD_RESOLVED_LLMNR)
- return DNS_PROTOCOL_LLMNR;
-
- return DNS_PROTOCOL_DNS;
-}
-
-static int dns_type_to_af(uint16_t t) {
- switch (t) {
-
- case DNS_TYPE_A:
- return AF_INET;
-
- case DNS_TYPE_AAAA:
- return AF_INET6;
-
- case DNS_TYPE_ANY:
- return AF_UNSPEC;
-
- default:
- return -EINVAL;
- }
-}
-
-static int synthesize_localhost_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- int r;
-
- assert(q);
- assert(key);
- assert(answer);
-
- r = dns_answer_reserve(answer, 2);
- if (r < 0)
- return r;
-
- if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- 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(INADDR_LOOPBACK);
-
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
- if (!rr)
- return -ENOMEM;
-
- rr->aaaa.in6_addr = in6addr_loopback;
-
- r = dns_answer_add(*answer, rr, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
- if (!rr)
- return -ENOMEM;
-
- rr->ptr.name = strdup(to);
- if (!rr->ptr.name)
- return -ENOMEM;
-
- return dns_answer_add(*answer, rr, ifindex, flags);
-}
-
-static int synthesize_localhost_ptr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- int r;
-
- assert(q);
- assert(key);
- assert(answer);
-
- if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_rr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- r = dns_answer_reserve(answer, n_addresses);
- if (r < 0)
- return r;
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int answer_add_addresses_ptr(
- DnsAnswer **answer,
- const char *name,
- struct local_address *addresses,
- unsigned n_addresses,
- int af, const union in_addr_union *match) {
-
- unsigned j;
- int r;
-
- assert(answer);
- assert(name);
-
- for (j = 0; j < n_addresses; j++) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
-
- if (af != AF_UNSPEC) {
-
- if (addresses[j].family != af)
- continue;
-
- if (match && !in_addr_equal(af, match, &addresses[j].address))
- continue;
- }
-
- r = dns_answer_reserve(answer, 1);
- if (r < 0)
- return r;
-
- r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
- if (r < 0)
- return r;
-
- r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-static int synthesize_system_hostname_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(q);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- 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 (af == AF_INET || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET,
- .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
- .address.in.s_addr = htobe32(0x7F000002),
- };
-
- if (af == AF_INET6 || af == AF_UNSPEC)
- buffer[n++] = (struct local_address) {
- .family = AF_INET6,
- .ifindex = SYNTHESIZE_IFINDEX(q->ifindex),
- .address.in6 = in6addr_loopback,
- };
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
- }
- }
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
-}
-
-static int synthesize_system_hostname_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n, r;
-
- assert(q);
- assert(address);
- assert(answer);
-
- if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
-
- /* Always map the IPv4 address 127.0.0.2 to the local
- * hostname, in addition to "localhost": */
-
- r = dns_answer_reserve(answer, 3);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->llmnr_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", q->manager->mdns_hostname, SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- r = answer_add_ptr(answer, "2.0.0.127.in-addr.arpa", "localhost", SYNTHESIZE_IFINDEX(q->ifindex), DNS_ANSWER_AUTHENTICATED);
- if (r < 0)
- return r;
-
- return 0;
- }
-
- n = local_addresses(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- r = answer_add_addresses_ptr(answer, q->manager->llmnr_hostname, addresses, n, af, address);
- if (r < 0)
- return r;
-
- return answer_add_addresses_ptr(answer, q->manager->mdns_hostname, addresses, n, af, address);
-}
-
-static int synthesize_gateway_rr(DnsQuery *q, const DnsResourceKey *key, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n = 0, af;
-
- assert(q);
- assert(key);
- assert(answer);
-
- af = dns_type_to_af(key->type);
- if (af >= 0) {
- n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
- }
-
- return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
-}
-
-static int synthesize_gateway_ptr(DnsQuery *q, int af, const union in_addr_union *address, DnsAnswer **answer) {
- _cleanup_free_ struct local_address *addresses = NULL;
- int n;
-
- assert(q);
- assert(address);
- assert(answer);
-
- n = local_gateways(q->manager->rtnl, q->ifindex, af, &addresses);
- if (n < 0)
- return n;
-
- return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
-}
-
static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- DnsResourceKey *key;
int r;
assert(q);
assert(state);
- /* Tries to synthesize localhost RR replies where appropriate */
+ /* Tries to synthesize localhost RR replies (and others) where appropriate. Note that this is done *after* the
+ * the normal lookup finished. The data from the network hence takes precedence over the data we
+ * synthesize. (But note that many scopes refuse to resolve certain domain names) */
if (!IN_SET(*state,
DNS_TRANSACTION_RCODE_FAILURE,
DNS_TRANSACTION_NO_SERVERS,
DNS_TRANSACTION_TIMEOUT,
- DNS_TRANSACTION_ATTEMPTS_MAX_REACHED))
+ DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND))
return 0;
- DNS_QUESTION_FOREACH(key, q->question_utf8) {
- union in_addr_union address;
- const char *name;
- int af;
-
- if (key->class != DNS_CLASS_IN &&
- key->class != DNS_CLASS_ANY)
- continue;
-
- name = DNS_RESOURCE_KEY_NAME(key);
+ r = dns_synthesize_answer(
+ q->manager,
+ q->question_utf8,
+ q->ifindex,
+ &answer);
- if (is_localhost(name)) {
-
- r = synthesize_localhost_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
-
- } else if (manager_is_own_hostname(q->manager, name)) {
+ if (r <= 0)
+ return r;
- r = synthesize_system_hostname_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
+ dns_query_reset_answer(q);
- } else if (is_gateway_hostname(name)) {
+ q->answer = answer;
+ answer = NULL;
+ q->answer_rcode = DNS_RCODE_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
- r = synthesize_gateway_rr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway RRs: %m");
+ *state = DNS_TRANSACTION_SUCCESS;
- } 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) {
+ return 1;
+}
- r = synthesize_localhost_ptr(q, key, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
+static int dns_query_try_etc_hosts(DnsQuery *q) {
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ int r;
- } else if (dns_name_address(name, &af, &address) > 0) {
+ assert(q);
- r = synthesize_system_hostname_ptr(q, af, &address, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
+ /* Looks in /etc/hosts for matching entries. Note that this is done *before* the normal lookup is done. The
+ * data from /etc/hosts hence takes precedence over the network. */
- r = synthesize_gateway_ptr(q, af, &address, &answer);
- if (r < 0)
- return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
- }
- }
+ r = manager_etc_hosts_lookup(
+ q->manager,
+ q->question_utf8,
+ &answer);
+ if (r <= 0)
+ return r;
- if (!answer)
- return 0;
+ dns_query_reset_answer(q);
- dns_answer_unref(q->answer);
q->answer = answer;
answer = NULL;
-
q->answer_rcode = DNS_RCODE_SUCCESS;
- q->answer_protocol = SYNTHESIZE_PROTOCOL(q->flags);
- q->answer_family = SYNTHESIZE_FAMILY(q->flags);
-
- *state = DNS_TRANSACTION_SUCCESS;
+ q->answer_protocol = dns_synthesize_protocol(q->flags);
+ q->answer_family = dns_synthesize_family(q->flags);
+ q->answer_authenticated = true;
return 1;
}
@@ -969,6 +632,14 @@ int dns_query_go(DnsQuery *q) {
if (q->state != DNS_TRANSACTION_NULL)
return 0;
+ r = dns_query_try_etc_hosts(q);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
+ return 1;
+ }
+
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
const char *name;
@@ -1000,7 +671,10 @@ int dns_query_go(DnsQuery *q) {
if (found == DNS_SCOPE_NO) {
DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS;
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ return r;
+
dns_query_complete(q, state);
return 1;
}
@@ -1029,10 +703,7 @@ int dns_query_go(DnsQuery *q) {
goto fail;
}
- q->answer = dns_answer_unref(q->answer);
- q->answer_rcode = 0;
- q->answer_family = AF_UNSPEC;
- q->answer_protocol = _DNS_PROTOCOL_INVALID;
+ dns_query_reset_answer(q);
r = sd_event_add_time(
q->manager->event,
@@ -1078,11 +749,23 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
assert(q);
if (!c) {
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
return;
}
+ if (c->error_code != 0) {
+ /* If the candidate had an error condition of its own, start with that. */
+ state = DNS_TRANSACTION_ERRNO;
+ q->answer = dns_answer_unref(q->answer);
+ q->answer_rcode = 0;
+ q->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
+ q->answer_errno = c->error_code;
+ }
+
SET_FOREACH(t, c->transactions, i) {
switch (t->state) {
@@ -1090,12 +773,11 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
case DNS_TRANSACTION_SUCCESS: {
/* We found a successfuly reply, merge it into the answer */
r = dns_answer_extend(&q->answer, t->answer);
- if (r < 0) {
- dns_query_complete(q, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
q->answer_rcode = t->answer_rcode;
+ q->answer_errno = 0;
if (t->answer_authenticated) {
has_authenticated = true;
@@ -1126,6 +808,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
q->answer = dns_answer_unref(q->answer);
q->answer_rcode = t->answer_rcode;
q->answer_dnssec_result = t->answer_dnssec_result;
+ q->answer_errno = t->answer_errno;
state = t->state;
break;
@@ -1143,8 +826,16 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) {
dns_search_domain_unref(q->answer_search_domain);
q->answer_search_domain = dns_search_domain_ref(c->search_domain);
- dns_query_synthesize_reply(q, &state);
+ r = dns_query_synthesize_reply(q, &state);
+ if (r < 0)
+ goto fail;
+
dns_query_complete(q, state);
+ return;
+
+fail:
+ q->answer_errno = -r;
+ dns_query_complete(q, DNS_TRANSACTION_ERRNO);
}
void dns_query_ready(DnsQuery *q) {
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 9f618d6f6b..c7e4ce9a00 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -83,6 +83,7 @@ struct DnsQuery {
DnsProtocol answer_protocol;
int answer_family;
DnsSearchDomain *answer_search_domain;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Bus client information */
sd_bus_message *request;
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index 8a52d66fad..ac4887abea 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -1015,3 +1015,22 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
return dns_name_is_single_label(name);
}
+
+bool dns_scope_network_good(DnsScope *s) {
+ Iterator i;
+ Link *l;
+
+ /* Checks whether the network is in good state for lookups on this scope. For mDNS/LLMNR/Classic DNS scopes
+ * bound to links this is easy, as they don't even exist if the link isn't in a suitable state. For the global
+ * DNS scope we check whether there are any links that are up and have an address. */
+
+ if (s->link)
+ return true;
+
+ HASHMAP_FOREACH(l, s->manager->links, i) {
+ if (link_relevant(l, AF_UNSPEC, false))
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index a0676bd30e..f9b63d56d9 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -107,3 +107,5 @@ void dns_scope_dump(DnsScope *s, FILE *f);
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
+
+bool dns_scope_network_good(DnsScope *s);
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 5a86661807..e1d2025863 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sd-messages.h>
+
#include "alloc-util.h"
#include "resolved-dns-server.h"
#include "resolved-resolv-conf.h"
@@ -547,6 +549,22 @@ bool dns_server_dnssec_supported(DnsServer *server) {
return true;
}
+void dns_server_warn_downgrade(DnsServer *server) {
+ assert(server);
+
+ if (server->warned_downgrade)
+ return;
+
+ log_struct(LOG_NOTICE,
+ LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE),
+ LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
+ "DNS_SERVER=%s", dns_server_string(server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level),
+ NULL);
+
+ server->warned_downgrade = true;
+}
+
static void dns_server_hash_func(const void *p, struct siphash *state) {
const DnsServer *s = p;
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index 02bd3463a7..2a3c921678 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -82,6 +82,9 @@ struct DnsServer {
usec_t verified_usec;
usec_t features_grace_period_usec;
+ /* Whether we already warned about downgrading to non-DNSSEC mode for this server */
+ bool warned_downgrade:1;
+
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked:1;
@@ -119,6 +122,8 @@ const char *dns_server_string(DnsServer *server);
bool dns_server_dnssec_supported(DnsServer *server);
+void dns_server_warn_downgrade(DnsServer *server);
+
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr);
void dns_server_unlink_all(DnsServer *first);
diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c
new file mode 100644
index 0000000000..f4a43dee8c
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.c
@@ -0,0 +1,413 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "hostname-util.h"
+#include "local-addresses.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
+ * purely for synthesized answers, where the family we return
+ * for the reply should match what was requested in the
+ * question, even though we are synthesizing the answer
+ * here. */
+
+ if (!(flags & SD_RESOLVED_DNS)) {
+ if (flags & (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_MDNS_IPV4))
+ return AF_INET;
+ if (flags & (SD_RESOLVED_LLMNR_IPV6|SD_RESOLVED_MDNS_IPV6))
+ return AF_INET6;
+ }
+
+ return AF_UNSPEC;
+}
+
+DnsProtocol dns_synthesize_protocol(uint64_t flags) {
+
+ /* Similar as dns_synthesize_family() but does this for the
+ * protocol. If resolving via DNS was requested, we claim it
+ * was DNS. Similar, if nothing specific was
+ * requested. However, if only resolving via LLMNR was
+ * requested we return that. */
+
+ if (flags & SD_RESOLVED_DNS)
+ return DNS_PROTOCOL_DNS;
+ if (flags & SD_RESOLVED_LLMNR)
+ return DNS_PROTOCOL_LLMNR;
+ if (flags & SD_RESOLVED_MDNS)
+ return DNS_PROTOCOL_MDNS;
+
+ return DNS_PROTOCOL_DNS;
+}
+
+static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ r = dns_answer_reserve(answer, 2);
+ if (r < 0)
+ return r;
+
+ if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ 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(INADDR_LOOPBACK);
+
+ r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key));
+ if (!rr)
+ return -ENOMEM;
+
+ rr->aaaa.in6_addr = in6addr_loopback;
+
+ r = dns_answer_add(*answer, rr, dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_ptr(DnsAnswer **answer, const char *from, const char *to, int ifindex, DnsAnswerFlags flags) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR, from);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(to);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ return dns_answer_add(*answer, rr, ifindex, flags);
+}
+
+static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ int r;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ if (IN_SET(key->type, DNS_TYPE_PTR, DNS_TYPE_ANY)) {
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_rr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ r = dns_answer_reserve(answer, n_addresses);
+ if (r < 0)
+ return r;
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ r = dns_resource_record_new_address(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int answer_add_addresses_ptr(
+ DnsAnswer **answer,
+ const char *name,
+ struct local_address *addresses,
+ unsigned n_addresses,
+ int af, const union in_addr_union *match) {
+
+ unsigned j;
+ int r;
+
+ assert(answer);
+ assert(name);
+
+ for (j = 0; j < n_addresses; j++) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ if (af != AF_UNSPEC) {
+
+ if (addresses[j].family != af)
+ continue;
+
+ if (match && !in_addr_equal(af, match, &addresses[j].address))
+ continue;
+ }
+
+ r = dns_answer_reserve(answer, 1);
+ if (r < 0)
+ return r;
+
+ r = dns_resource_record_new_reverse(&rr, addresses[j].family, &addresses[j].address, name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, addresses[j].ifindex, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_addresses(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ 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 (af == AF_INET || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET,
+ .ifindex = dns_synthesize_ifindex(ifindex),
+ .address.in.s_addr = htobe32(0x7F000002),
+ };
+
+ if (af == AF_INET6 || af == AF_UNSPEC)
+ buffer[n++] = (struct local_address) {
+ .family = AF_INET6,
+ .ifindex = dns_synthesize_ifindex(ifindex),
+ .address.in6 = in6addr_loopback,
+ };
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n);
+ }
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n, r;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ if (af == AF_INET && address->in.s_addr == htobe32(0x7F000002)) {
+
+ /* Always map the IPv4 address 127.0.0.2 to the local
+ * hostname, in addition to "localhost": */
+
+ r = dns_answer_reserve(answer, 3);
+ 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);
+ 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);
+ 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);
+ if (r < 0)
+ return r;
+
+ return 0;
+ }
+
+ n = local_addresses(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ r = answer_add_addresses_ptr(answer, m->llmnr_hostname, addresses, n, af, address);
+ if (r < 0)
+ return r;
+
+ return answer_add_addresses_ptr(answer, m->mdns_hostname, addresses, n, af, address);
+}
+
+static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifindex, DnsAnswer **answer) {
+ _cleanup_free_ struct local_address *addresses = NULL;
+ int n = 0, af;
+
+ assert(m);
+ assert(key);
+ assert(answer);
+
+ af = dns_type_to_af(key->type);
+ if (af >= 0) {
+ n = local_gateways(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+ }
+
+ return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n);
+}
+
+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;
+
+ assert(m);
+ assert(address);
+ assert(answer);
+
+ n = local_gateways(m->rtnl, ifindex, af, &addresses);
+ if (n < 0)
+ return n;
+
+ return answer_add_addresses_ptr(answer, "gateway", addresses, n, af, address);
+}
+
+int dns_synthesize_answer(
+ Manager *m,
+ DnsQuestion *q,
+ int ifindex,
+ DnsAnswer **ret) {
+
+ _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+ DnsResourceKey *key;
+ bool found = false;
+ int r;
+
+ assert(m);
+ assert(q);
+
+ DNS_QUESTION_FOREACH(key, q) {
+ union in_addr_union address;
+ const char *name;
+ int af;
+
+ if (key->class != DNS_CLASS_IN &&
+ key->class != DNS_CLASS_ANY)
+ continue;
+
+ name = DNS_RESOURCE_KEY_NAME(key);
+
+ if (is_localhost(name)) {
+
+ r = synthesize_localhost_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost RRs: %m");
+
+ } else if (manager_is_own_hostname(m, name)) {
+
+ r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
+
+ } else if (is_gateway_hostname(name)) {
+
+ r = synthesize_gateway_rr(m, key, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway 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, "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);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
+
+ } else if (dns_name_address(name, &af, &address) > 0) {
+
+ r = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize system hostname PTR RR: %m");
+
+ r = synthesize_gateway_ptr(m, af, &address, ifindex, &answer);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
+ } else
+ continue;
+
+ found = true;
+ }
+
+ r = found;
+
+ if (ret) {
+ *ret = answer;
+ answer = NULL;
+ }
+
+ return r;
+}
diff --git a/src/resolve/resolved-dns-synthesize.h b/src/resolve/resolved-dns-synthesize.h
new file mode 100644
index 0000000000..5d829bb2e7
--- /dev/null
+++ b/src/resolve/resolved-dns-synthesize.h
@@ -0,0 +1,30 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-dns-answer.h"
+#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);
+
+int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 5640cd1d33..77f9ef0a83 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -24,6 +24,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "dns-domain.h"
+#include "errno-list.h"
#include "fd-util.h"
#include "random-util.h"
#include "resolved-dns-cache.h"
@@ -43,6 +44,7 @@ static void dns_transaction_reset_answer(DnsTransaction *t) {
t->answer_source = _DNS_TRANSACTION_SOURCE_INVALID;
t->answer_authenticated = false;
t->answer_nsec_ttl = (uint32_t) -1;
+ t->answer_errno = 0;
}
static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) {
@@ -285,6 +287,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
DnsZoneItem *z;
DnsTransaction *d;
Iterator i;
+ const char *st;
assert(t);
assert(!DNS_TRANSACTION_IS_LIVE(state));
@@ -296,19 +299,26 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
"DNS_TRANSACTION=%" PRIu16, t->id,
"DNS_QUESTION=%s", dns_transaction_key_string(t),
"DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
+ "DNS_SERVER=%s", dns_server_string(t->server),
+ "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level),
NULL);
/* Note that this call might invalidate the query. Callers
* should hence not attempt to access the query or transaction
* after calling this function. */
+ if (state == DNS_TRANSACTION_ERRNO)
+ st = errno_to_name(t->answer_errno);
+ else
+ st = dns_transaction_state_to_string(state);
+
log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).",
t->id,
dns_transaction_key_string(t),
dns_protocol_to_string(t->scope->protocol),
t->scope->link ? t->scope->link->name : "*",
t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family),
- dns_transaction_state_to_string(state),
+ st,
t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source),
t->answer_authenticated ? "authenticated" : "unsigned");
@@ -391,8 +401,10 @@ static void dns_transaction_retry(DnsTransaction *t) {
dns_scope_next_dns_server(t->scope);
r = dns_transaction_go(t);
- if (r < 0)
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r < 0) {
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ }
}
static int dns_transaction_maybe_restart(DnsTransaction *t) {
@@ -432,6 +444,13 @@ static int on_stream_complete(DnsStream *s, int error) {
if (ERRNO_IS_DISCONNECT(error)) {
usec_t usec;
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR) {
+ /* If the LLMNR/TCP connection failed, the host doesn't support LLMNR, and we cannot answer the
+ * question on this scope. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
+
log_debug_errno(error, "Connection failure for DNS TCP stream: %m");
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0);
dns_server_packet_lost(t->server, IPPROTO_TCP, t->current_feature_level, usec - t->start_usec);
@@ -440,7 +459,8 @@ static int on_stream_complete(DnsStream *s, int error) {
return 0;
}
if (error != 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ t->answer_errno = error;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
return 0;
}
@@ -655,30 +675,28 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
/* Are there ongoing DNSSEC transactions? If so, let's wait for them. */
r = dns_transaction_dnssec_ready(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r == 0) /* We aren't ready yet (or one of our auxiliary transactions failed, and we shouldn't validate now */
return;
/* See if we learnt things from the additional DNSSEC transactions, that we didn't know before, and better
* restart the lookup immediately. */
r = dns_transaction_maybe_restart(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) /* Transaction got restarted... */
return;
/* All our auxiliary DNSSEC transactions are complete now. Try
* to validate our RRset now. */
r = dns_transaction_validate_dnssec(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ if (r == -EBADMSG) {
+ dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
}
+ if (r < 0)
+ goto fail;
if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER &&
t->scope->dnssec_mode == DNSSEC_YES) {
@@ -697,12 +715,21 @@ static void dns_transaction_process_dnssec(DnsTransaction *t) {
return;
}
+ if (t->answer_dnssec_result == DNSSEC_INCOMPATIBLE_SERVER)
+ dns_server_warn_downgrade(t->server);
+
dns_transaction_cache_answer(t);
if (t->answer_rcode == DNS_RCODE_SUCCESS)
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
else
dns_transaction_complete(t, DNS_TRANSACTION_RCODE_FAILURE);
+
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
@@ -846,10 +873,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
if (r < 0) {
/* On LLMNR, if we cannot connect to the host,
* we immediately give up */
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ goto fail;
/* On DNS, couldn't send? Try immediately again, with a new server */
dns_transaction_retry(t);
@@ -875,10 +900,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* See if we know things we didn't know before that indicate we better restart the lookup immediately. */
r = dns_transaction_maybe_restart(t);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) /* Transaction got restarted... */
return;
@@ -886,10 +909,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
/* Only consider responses with equivalent query section to the request */
r = dns_packet_is_reply_for(p, t->key);
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r == 0) {
dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY);
return;
@@ -918,10 +939,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
* quickly. */
if (t->state != DNS_TRANSACTION_PENDING)
return;
- if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return;
- }
+ if (r < 0)
+ goto fail;
if (r > 0) {
/* There are DNSSEC transactions pending now. Update the state accordingly. */
t->state = DNS_TRANSACTION_VALIDATING;
@@ -932,6 +951,11 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
}
dns_transaction_process_dnssec(t);
+ return;
+
+fail:
+ t->answer_errno = -r;
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
@@ -957,7 +981,8 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use
return 0;
}
if (r < 0) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
+ dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
+ t->answer_errno = -r;
return 0;
}
@@ -1094,6 +1119,14 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
dns_transaction_stop_timeout(t);
+ r = dns_scope_network_good(t->scope);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dns_transaction_complete(t, DNS_TRANSACTION_NETWORK_DOWN);
+ return 0;
+ }
+
if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) {
dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED);
return 0;
@@ -1451,11 +1484,15 @@ int dns_transaction_go(DnsTransaction *t) {
dns_transaction_complete(t, DNS_TRANSACTION_RR_TYPE_UNSUPPORTED);
return 0;
}
+ if (t->scope->protocol == DNS_PROTOCOL_LLMNR && ERRNO_IS_DISCONNECT(-r)) {
+ /* On LLMNR, if we cannot connect to a host via TCP when doing revers lookups. This means we cannot
+ * answer this request with this protocol. */
+ dns_transaction_complete(t, DNS_TRANSACTION_NOT_FOUND);
+ return 0;
+ }
if (r < 0) {
- if (t->scope->protocol != DNS_PROTOCOL_DNS) {
- dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES);
- return 0;
- }
+ if (t->scope->protocol != DNS_PROTOCOL_DNS)
+ return r;
/* Couldn't send? Try immediately again, with a new server */
dns_scope_next_dns_server(t->scope);
@@ -1679,33 +1716,13 @@ static int dns_transaction_is_primary_response(DnsTransaction *t, DnsResourceRec
/* Check if the specified RR is the "primary" response,
* i.e. either matches the question precisely or is a
- * CNAME/DNAME for it, or is any kind of NSEC/NSEC3 RR */
+ * CNAME/DNAME for it. */
r = dns_resource_key_match_rr(t->key, rr, NULL);
if (r != 0)
return r;
- r = dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
- if (r != 0)
- return r;
-
- if (rr->key->type == DNS_TYPE_NSEC3) {
- const char *p;
-
- p = DNS_RESOURCE_KEY_NAME(rr->key);
- r = dns_name_parent(&p);
- if (r < 0)
- return r;
- if (r > 0) {
- r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), p);
- if (r < 0)
- return r;
- if (r > 0)
- return true;
- }
- }
-
- return rr->key->type == DNS_TYPE_NSEC;
+ return dns_resource_key_match_cname_or_dname(t->key, rr->key, NULL);
}
static bool dns_transaction_dnssec_supported(DnsTransaction *t) {
@@ -2543,7 +2560,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (!dns_transaction_dnssec_supported_full(t)) {
/* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */
t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER;
- log_debug("Not validating response, server lacks DNSSEC support.");
+ log_debug("Not validating response for %" PRIu16 ", server lacks DNSSEC support.", t->id);
return 0;
}
@@ -2648,7 +2665,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_secure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2688,10 +2705,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- if (authenticated)
- t->scope->manager->n_dnssec_secure++;
- else
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key);
/* Exit the loop, we dropped something from the answer, start from the beginning */
changed = true;
@@ -2710,7 +2724,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2732,7 +2746,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2758,7 +2772,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
@@ -2780,20 +2794,12 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
if (r < 0)
return r;
- t->scope->manager->n_dnssec_insecure++;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key);
changed = true;
break;
}
}
- if (IN_SET(result,
- DNSSEC_INVALID,
- DNSSEC_SIGNATURE_EXPIRED,
- DNSSEC_NO_SIGNATURE))
- t->scope->manager->n_dnssec_bogus++;
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
- t->scope->manager->n_dnssec_indeterminate++;
-
r = dns_transaction_is_primary_response(t, rr);
if (r < 0)
return r;
@@ -2811,6 +2817,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
}
if (r == 0) {
+ if (IN_SET(result,
+ DNSSEC_INVALID,
+ DNSSEC_SIGNATURE_EXPIRED,
+ DNSSEC_NO_SIGNATURE))
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
+ else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
+
/* This is a primary response to our question, and it failed validation. That's
* fatal. */
t->answer_dnssec_result = result;
@@ -2892,6 +2906,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_NXDOMAIN;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NODATA:
@@ -2900,6 +2916,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
t->answer_dnssec_result = DNSSEC_VALIDATED;
t->answer_rcode = DNS_RCODE_SUCCESS;
t->answer_authenticated = authenticated;
+
+ manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_OPTOUT:
@@ -2907,6 +2925,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t));
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
break;
case DNSSEC_NSEC_NO_RR:
@@ -2915,11 +2935,13 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
r = dns_transaction_requires_nsec(t);
if (r < 0)
return r;
- if (r > 0)
+ if (r > 0) {
t->answer_dnssec_result = DNSSEC_NO_SIGNATURE;
- else {
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
+ } else {
t->answer_dnssec_result = DNSSEC_UNSIGNED;
t->answer_authenticated = false;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, t->key);
}
break;
@@ -2927,12 +2949,14 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
case DNSSEC_NSEC_UNSUPPORTED_ALGORITHM:
/* We don't know the NSEC3 algorithm used? */
t->answer_dnssec_result = DNSSEC_UNSUPPORTED_ALGORITHM;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, t->key);
break;
case DNSSEC_NSEC_FOUND:
case DNSSEC_NSEC_CNAME:
/* NSEC says it needs to be there, but we couldn't find it? Bummer! */
t->answer_dnssec_result = DNSSEC_NSEC_MISMATCH;
+ manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, t->key);
break;
default:
@@ -2964,11 +2988,13 @@ static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX]
[DNS_TRANSACTION_TIMEOUT] = "timeout",
[DNS_TRANSACTION_ATTEMPTS_MAX_REACHED] = "attempts-max-reached",
[DNS_TRANSACTION_INVALID_REPLY] = "invalid-reply",
- [DNS_TRANSACTION_RESOURCES] = "resources",
+ [DNS_TRANSACTION_ERRNO] = "errno",
[DNS_TRANSACTION_ABORTED] = "aborted",
[DNS_TRANSACTION_DNSSEC_FAILED] = "dnssec-failed",
[DNS_TRANSACTION_NO_TRUST_ANCHOR] = "no-trust-anchor",
[DNS_TRANSACTION_RR_TYPE_UNSUPPORTED] = "rr-type-unsupported",
+ [DNS_TRANSACTION_NETWORK_DOWN] = "network-down",
+ [DNS_TRANSACTION_NOT_FOUND] = "not-found",
};
DEFINE_STRING_TABLE_LOOKUP(dns_transaction_state, DnsTransactionState);
diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h
index 76cf6e71db..b6c5b2861c 100644
--- a/src/resolve/resolved-dns-transaction.h
+++ b/src/resolve/resolved-dns-transaction.h
@@ -35,11 +35,13 @@ enum DnsTransactionState {
DNS_TRANSACTION_TIMEOUT,
DNS_TRANSACTION_ATTEMPTS_MAX_REACHED,
DNS_TRANSACTION_INVALID_REPLY,
- DNS_TRANSACTION_RESOURCES,
+ DNS_TRANSACTION_ERRNO,
DNS_TRANSACTION_ABORTED,
DNS_TRANSACTION_DNSSEC_FAILED,
DNS_TRANSACTION_NO_TRUST_ANCHOR,
DNS_TRANSACTION_RR_TYPE_UNSUPPORTED,
+ DNS_TRANSACTION_NETWORK_DOWN,
+ DNS_TRANSACTION_NOT_FOUND, /* like NXDOMAIN, but when LLMNR/TCP connections fail */
_DNS_TRANSACTION_STATE_MAX,
_DNS_TRANSACTION_STATE_INVALID = -1
};
@@ -82,6 +84,7 @@ struct DnsTransaction {
DnssecResult answer_dnssec_result;
DnsTransactionSource answer_source;
uint32_t answer_nsec_ttl;
+ int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */
/* Indicates whether the primary answer is authenticated,
* i.e. whether the RRs from answer which directly match the
diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c
new file mode 100644
index 0000000000..467754774f
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.c
@@ -0,0 +1,448 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "fd-util.h"
+#include "fileio.h"
+#include "hostname-util.h"
+#include "resolved-etc-hosts.h"
+#include "resolved-dns-synthesize.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+/* Recheck /etc/hosts at most once every 2s */
+#define ETC_HOSTS_RECHECK_USEC (2*USEC_PER_SEC)
+
+typedef struct EtcHostsItem {
+ int family;
+ union in_addr_union address;
+
+ char **names;
+} EtcHostsItem;
+
+typedef struct EtcHostsItemByName {
+ char *name;
+
+ EtcHostsItem **items;
+ size_t n_items, n_allocated;
+} EtcHostsItemByName;
+
+void manager_etc_hosts_flush(Manager *m) {
+ EtcHostsItem *item;
+ EtcHostsItemByName *bn;
+
+ while ((item = set_steal_first(m->etc_hosts_by_address))) {
+ strv_free(item->names);
+ free(item);
+ }
+
+ while ((bn = hashmap_steal_first(m->etc_hosts_by_name))) {
+ free(bn->name);
+ free(bn->items);
+ free(bn);
+ }
+
+ m->etc_hosts_by_address = set_free(m->etc_hosts_by_address);
+ m->etc_hosts_by_name = hashmap_free(m->etc_hosts_by_name);
+
+ m->etc_hosts_mtime = USEC_INFINITY;
+}
+
+static void etc_hosts_item_hash_func(const void *p, struct siphash *state) {
+ const EtcHostsItem *item = p;
+
+ siphash24_compress(&item->family, sizeof(item->family), state);
+
+ if (item->family == AF_INET)
+ siphash24_compress(&item->address.in, sizeof(item->address.in), state);
+ else if (item->family == AF_INET6)
+ siphash24_compress(&item->address.in6, sizeof(item->address.in6), state);
+}
+
+static int etc_hosts_item_compare_func(const void *a, const void *b) {
+ const EtcHostsItem *x = a, *y = b;
+
+ if (x->family != x->family)
+ return x->family - y->family;
+
+ if (x->family == AF_INET)
+ return memcmp(&x->address.in.s_addr, &y->address.in.s_addr, sizeof(struct in_addr));
+
+ if (x->family == AF_INET6)
+ return memcmp(&x->address.in6.s6_addr, &y->address.in6.s6_addr, sizeof(struct in6_addr));
+
+ return trivial_compare_func(a, b);
+}
+
+static const struct hash_ops etc_hosts_item_ops = {
+ .hash = etc_hosts_item_hash_func,
+ .compare = etc_hosts_item_compare_func,
+};
+
+static int add_item(Manager *m, int family, const union in_addr_union *address, char **names) {
+
+ EtcHostsItem key = {
+ .family = family,
+ .address = *address,
+ };
+ EtcHostsItem *item;
+ char **n;
+ int r;
+
+ assert(m);
+ assert(address);
+
+ r = in_addr_is_null(family, address);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ /* This is an 0.0.0.0 or :: item, which we assume means that we shall map the specified hostname to
+ * nothing. */
+ item = NULL;
+ else {
+ /* If this is a normal address, then, simply add entry mapping it to the specified names */
+
+ item = set_get(m->etc_hosts_by_address, &key);
+ if (item) {
+ r = strv_extend_strv(&item->names, names, true);
+ if (r < 0)
+ return log_oom();
+ } else {
+
+ r = set_ensure_allocated(&m->etc_hosts_by_address, &etc_hosts_item_ops);
+ if (r < 0)
+ return log_oom();
+
+ item = new0(EtcHostsItem, 1);
+ if (!item)
+ return log_oom();
+
+ item->family = family;
+ item->address = *address;
+ item->names = names;
+
+ r = set_put(m->etc_hosts_by_address, item);
+ if (r < 0) {
+ free(item);
+ return log_oom();
+ }
+ }
+ }
+
+ STRV_FOREACH(n, names) {
+ EtcHostsItemByName *bn;
+
+ bn = hashmap_get(m->etc_hosts_by_name, *n);
+ if (!bn) {
+ r = hashmap_ensure_allocated(&m->etc_hosts_by_name, &dns_name_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ bn = new0(EtcHostsItemByName, 1);
+ if (!bn)
+ return log_oom();
+
+ bn->name = strdup(*n);
+ if (!bn->name) {
+ free(bn);
+ return log_oom();
+ }
+
+ r = hashmap_put(m->etc_hosts_by_name, bn->name, bn);
+ if (r < 0) {
+ free(bn->name);
+ free(bn);
+ return log_oom();
+ }
+ }
+
+ if (item) {
+ if (!GREEDY_REALLOC(bn->items, bn->n_allocated, bn->n_items+1))
+ return log_oom();
+
+ bn->items[bn->n_items++] = item;
+ }
+ }
+
+ return 0;
+}
+
+static int parse_line(Manager *m, unsigned nr, const char *line) {
+ _cleanup_free_ char *address = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ union in_addr_union in;
+ bool suppressed = false;
+ int family, r;
+
+ assert(m);
+ assert(line);
+
+ r = extract_first_word(&line, &address, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract address, in line /etc/hosts:%u.", nr);
+ if (r == 0) {
+ log_error("Premature end of line, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ r = in_addr_from_string_auto(address, &family, &in);
+ if (r < 0)
+ return log_error_errno(r, "Address '%s' is invalid, in line /etc/hosts:%u.", address, nr);
+
+ for (;;) {
+ _cleanup_free_ char *name = NULL;
+
+ r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
+ if (r < 0)
+ return log_error_errno(r, "Couldn't extract host name, in line /etc/hosts:%u.", nr);
+ if (r == 0)
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r <= 0)
+ return log_error_errno(r, "Hostname %s is not valid, ignoring, in line /etc/hosts:%u.", name, nr);
+
+ if (is_localhost(name)) {
+ /* Suppress the "localhost" line that is often seen */
+ suppressed = true;
+ continue;
+ }
+
+ r = strv_push(&names, name);
+ if (r < 0)
+ return log_oom();
+
+ name = NULL;
+ }
+
+ if (strv_isempty(names)) {
+
+ if (suppressed)
+ return 0;
+
+ log_error("Line is missing any host names, in line /etc/hosts:%u.", nr);
+ return -EINVAL;
+ }
+
+ /* Takes possession of the names strv */
+ r = add_item(m, family, &in, names);
+ if (r < 0)
+ return r;
+
+ names = NULL;
+ return r;
+}
+
+int manager_etc_hosts_read(Manager *m) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ struct stat st;
+ usec_t ts;
+ unsigned nr = 0;
+ int r;
+
+ assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0);
+
+ /* See if we checked /etc/hosts recently already */
+ if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts)
+ return 0;
+
+ m->etc_hosts_last = ts;
+
+ if (m->etc_hosts_mtime != USEC_INFINITY) {
+ if (stat("/etc/hosts", &st) < 0) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to stat /etc/hosts: %m");
+ }
+
+ /* Did the mtime change? If not, there's no point in re-reading the file. */
+ if (timespec_load(&st.st_mtim) == m->etc_hosts_mtime)
+ return 0;
+ }
+
+ f = fopen("/etc/hosts", "re");
+ if (!f) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto clear;
+ }
+
+ return log_error_errno(errno, "Failed to open /etc/hosts: %m");
+ }
+
+ /* Take the timestamp at the beginning of processing, so that any changes made later are read on the next
+ * invocation */
+ r = fstat(fileno(f), &st);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to fstat() /etc/hosts: %m");
+
+ manager_etc_hosts_flush(m);
+
+ FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) {
+ char *l;
+
+ nr ++;
+
+ l = strstrip(line);
+ if (isempty(l))
+ continue;
+ if (l[0] == '#')
+ continue;
+
+ r = parse_line(m, nr, l);
+ if (r == -ENOMEM) /* On OOM we abandon the half-built-up structure. All other errors we ignore and proceed */
+ goto clear;
+ }
+
+ m->etc_hosts_mtime = timespec_load(&st.st_mtim);
+ m->etc_hosts_last = ts;
+
+ return 1;
+
+clear:
+ manager_etc_hosts_flush(m);
+ return r;
+}
+
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) {
+ bool found_a = false, found_aaaa = false;
+ EtcHostsItemByName *bn;
+ EtcHostsItem k = {};
+ DnsResourceKey *t;
+ const char *name;
+ unsigned i;
+ int r;
+
+ assert(m);
+ assert(q);
+ assert(answer);
+
+ r = manager_etc_hosts_read(m);
+ if (r < 0)
+ return r;
+
+ name = dns_question_first_name(q);
+ if (!name)
+ return 0;
+
+ r = dns_name_address(name, &k.family, &k.address);
+ if (r > 0) {
+ EtcHostsItem *item;
+ DnsResourceKey *found_ptr = NULL;
+
+ item = set_get(m->etc_hosts_by_address, &k);
+ if (!item)
+ return 0;
+
+ /* We have an address in /etc/hosts that matches the queried name. Let's return successful. Actual data
+ * we'll only return if the request was for PTR. */
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_PTR, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ found_ptr = t;
+ break;
+ }
+ }
+
+ if (found_ptr) {
+ char **n;
+
+ r = dns_answer_reserve(answer, strv_length(item->names));
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH(n, item->names) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
+
+ rr = dns_resource_record_new(found_ptr);
+ if (!rr)
+ return -ENOMEM;
+
+ rr->ptr.name = strdup(*n);
+ if (!rr->ptr.name)
+ return -ENOMEM;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 1;
+ }
+
+ bn = hashmap_get(m->etc_hosts_by_name, name);
+ if (!bn)
+ return 0;
+
+ r = dns_answer_reserve(answer, bn->n_items);
+ if (r < 0)
+ return r;
+
+ DNS_QUESTION_FOREACH(t, q) {
+ if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ continue;
+ if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY))
+ continue;
+
+ r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY))
+ found_a = true;
+ if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY))
+ found_aaaa = true;
+
+ if (found_a && found_aaaa)
+ break;
+ }
+
+ 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))
+ continue;
+
+ r = dns_resource_record_new_address(&rr, bn->items[i]->family, &bn->items[i]->address, bn->name);
+ if (r < 0)
+ return r;
+
+ r = dns_answer_add(*answer, rr, 0, DNS_ANSWER_AUTHENTICATED);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
diff --git a/src/resolve/resolved-etc-hosts.h b/src/resolve/resolved-etc-hosts.h
new file mode 100644
index 0000000000..9d5a175f18
--- /dev/null
+++ b/src/resolve/resolved-etc-hosts.h
@@ -0,0 +1,28 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "resolved-manager.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-answer.h"
+
+void manager_etc_hosts_flush(Manager *m);
+int manager_etc_hosts_read(Manager *m);
+int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 20352a3e51..2ca7ece851 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -392,7 +392,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search negative trust anchor domain: %s", *i);
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid negative trust anchor domain: %s", *i);
}
ns = set_new(&dns_name_hash_ops);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index b203f19dbb..e2f9c8b400 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -49,6 +49,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
l->llmnr_support = RESOLVE_SUPPORT_YES;
l->mdns_support = RESOLVE_SUPPORT_NO;
l->dnssec_mode = _DNSSEC_MODE_INVALID;
+ l->operstate = IF_OPER_UNKNOWN;
r = hashmap_put(m->links, INT_TO_PTR(ifindex), l);
if (r < 0)
@@ -177,7 +178,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) {
if (r < 0)
return r;
- sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu);
+ (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate);
if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) {
strncpy(l->name, n, sizeof(l->name)-1);
@@ -514,7 +516,12 @@ bool link_relevant(Link *l, int family, bool multicast) {
return false;
}
- sd_network_link_get_operational_state(l->ifindex, &state);
+ /* Check kernel operstate
+ * https://www.kernel.org/doc/Documentation/networking/operstates.txt */
+ if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP))
+ return false;
+
+ (void) sd_network_link_get_operational_state(l->ifindex, &state);
if (state && !STR_IN_SET(state, "unknown", "degraded", "routable"))
return false;
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 6544214b77..3b6aafb8f0 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -82,6 +82,7 @@ struct Link {
char name[IF_NAMESIZE];
uint32_t mtu;
+ uint8_t operstate;
};
int link_new(Manager *m, Link **ret, int ifindex);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index d6d75a3f78..704f012c37 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -37,10 +37,11 @@
#include "random-util.h"
#include "resolved-bus.h"
#include "resolved-conf.h"
+#include "resolved-etc-hosts.h"
#include "resolved-llmnr.h"
#include "resolved-manager.h"
-#include "resolved-resolv-conf.h"
#include "resolved-mdns.h"
+#include "resolved-resolv-conf.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -485,6 +486,7 @@ int manager_new(Manager **ret) {
m->dnssec_mode = DNSSEC_NO;
m->read_resolv_conf = true;
m->need_builtin_fallbacks = true;
+ m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY;
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
@@ -594,6 +596,7 @@ Manager *manager_free(Manager *m) {
free(m->mdns_hostname);
dns_trust_anchor_flush(&m->trust_anchor);
+ manager_etc_hosts_flush(m);
free(m);
@@ -1203,3 +1206,19 @@ bool manager_dnssec_supported(Manager *m) {
return true;
}
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key) {
+
+ assert(verdict >= 0);
+ assert(verdict < _DNSSEC_VERDICT_MAX);
+
+ if (log_get_max_level() >= LOG_DEBUG) {
+ _cleanup_free_ char *s = NULL;
+
+ (void) dns_resource_key_to_string(key, &s);
+
+ log_debug("Found verdict for lookup %s: %s", s ? strstrip(s) : "n/a", dnssec_verdict_to_string(verdict));
+ }
+
+ m->n_dnssec_verdict[verdict]++;
+}
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index 8b13074298..a4291d57ff 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -123,7 +123,12 @@ struct Manager {
sd_event_source *sigusr1_event_source;
unsigned n_transactions_total;
- unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate;
+ unsigned n_dnssec_verdict[_DNSSEC_VERDICT_MAX];
+
+ /* Data from /etc/hosts */
+ Set* etc_hosts_by_address;
+ Hashmap* etc_hosts_by_name;
+ usec_t etc_hosts_last, etc_hosts_mtime;
};
/* Manager */
@@ -161,3 +166,5 @@ int manager_compile_search_domains(Manager *m, OrderedSet **domains);
DnssecMode manager_get_dnssec_mode(Manager *m);
bool manager_dnssec_supported(Manager *m);
+
+void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResourceKey *key);
diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h
index 1183df6105..814263546b 100644
--- a/src/systemd/sd-messages.h
+++ b/src/systemd/sd-messages.h
@@ -88,6 +88,7 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d)
#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65)
+#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57)
_SD_END_DECLARATIONS;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index e83b8b1c12..104d5111c5 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -106,6 +106,8 @@
#include "string-util.h"
#include "udev.h"
+#define ONBOARD_INDEX_MAX (16*1024-1)
+
enum netname_type{
NET_UNDEF,
NET_PCI,
@@ -152,6 +154,13 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
if (idx <= 0)
return -EINVAL;
+ /* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to report for
+ * example). Let's define a cut-off where we don't consider the index reliable anymore. We pick some arbitrary
+ * cut-off, which is somewhere beyond the realistic number of physical network interface a system might
+ * have. Ideally the kernel would already filter his crap for us, but it doesn't currently. */
+ if (idx > ONBOARD_INDEX_MAX)
+ return -ENOENT;
+
/* kernel provided port index for multiple ports on a single PCI function */
attr = udev_device_get_sysattr_value(dev, "dev_port");
if (attr)