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:
commit
f49ce89edf
@ -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
2
.gitignore
vendored
@ -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
|
||||
|
@ -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
38
CONTRIBUTING.md
Normal 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!
|
@ -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 \
|
||||
|
14
Makefile.am
14
Makefile.am
@ -5188,6 +5188,8 @@ systemd_resolved_SOURCES = \
|
||||
src/resolve/resolved-dns-packet.c \
|
||||
src/resolve/resolved-dns-query.h \
|
||||
src/resolve/resolved-dns-query.c \
|
||||
src/resolve/resolved-dns-synthesize.h \
|
||||
src/resolve/resolved-dns-synthesize.c \
|
||||
src/resolve/resolved-dns-transaction.h \
|
||||
src/resolve/resolved-dns-transaction.c \
|
||||
src/resolve/resolved-dns-scope.h \
|
||||
@ -5206,6 +5208,8 @@ systemd_resolved_SOURCES = \
|
||||
src/resolve/resolved-dns-dnssec.c \
|
||||
src/resolve/resolved-dns-trust-anchor.h \
|
||||
src/resolve/resolved-dns-trust-anchor.c \
|
||||
src/resolve/resolved-etc-hosts.h \
|
||||
src/resolve/resolved-etc-hosts.c \
|
||||
src/resolve/dns-type.c \
|
||||
src/resolve/dns-type.h
|
||||
|
||||
@ -5265,8 +5269,8 @@ libnss_resolve_la_LIBADD = \
|
||||
lib_LTLIBRARIES += \
|
||||
libnss_resolve.la
|
||||
|
||||
systemd_resolve_host_SOURCES = \
|
||||
src/resolve-host/resolve-host.c \
|
||||
systemd_resolve_SOURCES = \
|
||||
src/resolve/resolve-tool.c \
|
||||
src/resolve/resolved-dns-packet.c \
|
||||
src/resolve/resolved-dns-packet.h \
|
||||
src/resolve/resolved-dns-rr.c \
|
||||
@ -5278,15 +5282,15 @@ systemd_resolve_host_SOURCES = \
|
||||
src/resolve/dns-type.c \
|
||||
src/resolve/dns-type.h
|
||||
|
||||
nodist_systemd_resolve_host_SOURCES = \
|
||||
nodist_systemd_resolve_SOURCES = \
|
||||
src/resolve/dns_type-from-name.h \
|
||||
src/resolve/dns_type-to-name.h
|
||||
|
||||
systemd_resolve_host_LDADD = \
|
||||
systemd_resolve_LDADD = \
|
||||
libshared.la
|
||||
|
||||
rootlibexec_PROGRAMS += \
|
||||
systemd-resolve-host
|
||||
systemd-resolve
|
||||
|
||||
tests += \
|
||||
test-dns-domain \
|
||||
|
1
README
1
README
@ -15,7 +15,6 @@ GITWEB:
|
||||
|
||||
MAILING LIST:
|
||||
http://lists.freedesktop.org/mailman/listinfo/systemd-devel
|
||||
http://lists.freedesktop.org/mailman/listinfo/systemd-commits
|
||||
|
||||
IRC:
|
||||
#systemd on irc.freenode.org
|
||||
|
13
README.md
13
README.md
@ -5,5 +5,14 @@
|
||||
|
||||
## Details
|
||||
|
||||
* General information about systemd can be found in the [systemd Wiki](http://www.freedesktop.org/wiki/Software/systemd)
|
||||
* Information about build requirements are provided in the [README file](../master/README)
|
||||
General information about systemd can be found in the [systemd Wiki](http://www.freedesktop.org/wiki/Software/systemd).
|
||||
|
||||
Information about build requirements are provided in the [README file](../master/README).
|
||||
|
||||
Consult our [NEWS file](../master/NEWS) for information about what's new in the most recent systemd versions.
|
||||
|
||||
Please see our [Contribution Guidelines](../master/CONTRIBUTING.md) for more information about filing GitHub Issues and posting GitHub Pull Requests.
|
||||
|
||||
When preparing patches for systemd, please follow our [Coding Style Guidelines](../master/CODING_STYLE).
|
||||
|
||||
If you are looking for support, please contact our [mailing list](http://lists.freedesktop.org/mailman/listinfo/systemd-devel) or join our [IRC channel](irc://irc.freenode.org/%23systemd).
|
||||
|
@ -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
272
man/systemd-resolve.xml
Normal 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>
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
../Makefile
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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_;
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
413
src/resolve/resolved-dns-synthesize.c
Normal file
413
src/resolve/resolved-dns-synthesize.c
Normal 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;
|
||||
}
|
30
src/resolve/resolved-dns-synthesize.h
Normal file
30
src/resolve/resolved-dns-synthesize.h
Normal 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);
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
448
src/resolve/resolved-etc-hosts.c
Normal file
448
src/resolve/resolved-etc-hosts.c
Normal 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;
|
||||
}
|
28
src/resolve/resolved-etc-hosts.h
Normal file
28
src/resolve/resolved-etc-hosts.h
Normal 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);
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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]++;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user