1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-23 10:50:16 +03:00

Merge pull request #2392 from poettering/dnssec18

eightteenth dnssec patch
This commit is contained in:
Tom Gundersen 2016-01-25 20:28:38 +01:00
commit f49ce89edf
46 changed files with 1785 additions and 589 deletions

View File

@ -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))))

2
.gitignore vendored
View File

@ -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

View File

@ -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.

38
CONTRIBUTING.md Normal file
View File

@ -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!

View File

@ -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 \

View File

@ -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 \

1
README
View File

@ -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

View File

@ -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).

View File

@ -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.

272
man/systemd-resolve.xml Normal file
View File

@ -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>

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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"

View File

@ -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);

View File

@ -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);

View File

@ -1 +0,0 @@
../Makefile

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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);

View File

@ -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_;

View File

@ -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)

View File

@ -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,417 +550,77 @@ 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;
r = dns_synthesize_answer(
q->manager,
q->question_utf8,
q->ifindex,
&answer);
if (key->class != DNS_CLASS_IN &&
key->class != DNS_CLASS_ANY)
continue;
if (r <= 0)
return r;
name = DNS_RESOURCE_KEY_NAME(key);
dns_query_reset_answer(q);
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)) {
r = synthesize_system_hostname_rr(q, key, &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(q, key, &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(q, key, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize localhost PTR RRs: %m");
} else if (dns_name_address(name, &af, &address) > 0) {
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");
r = synthesize_gateway_ptr(q, af, &address, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize gateway hostname PTR RR: %m");
}
}
if (!answer)
return 0;
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);
q->answer_protocol = dns_synthesize_protocol(q->flags);
q->answer_family = dns_synthesize_family(q->flags);
q->answer_authenticated = true;
*state = DNS_TRANSACTION_SUCCESS;
return 1;
}
static int dns_query_try_etc_hosts(DnsQuery *q) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
int r;
assert(q);
/* 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 = manager_etc_hosts_lookup(
q->manager,
q->question_utf8,
&answer);
if (r <= 0)
return r;
dns_query_reset_answer(q);
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;
return 1;
}
int dns_query_go(DnsQuery *q) {
DnsScopeMatch found = DNS_SCOPE_NO;
DnsScope *s, *first = NULL;
@ -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) {

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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]++;
}

View File

@ -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);

View File

@ -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;

View File

@ -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)