summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2016-01-26 18:07:19 +0100
committerTom Gundersen <teg@jklm.no>2016-01-26 18:07:19 +0100
commitcfd77192c1de3bd264d15d6f4d8b3117f5619f4f (patch)
tree2534573f6826eac1ed2bdebfbff27ac07609cf44
parent3820ed90335211dc208b048f1ff48ae66940ce3b (diff)
parent4850d39ab72e7cb00a6e9c9aa4745c997674efa6 (diff)
Merge pull request #2437 from poettering/dnssec19
nineteenth dnssec patch
-rw-r--r--CONTRIBUTING.md1
-rw-r--r--Makefile.am1
-rw-r--r--TODO13
-rw-r--r--man/logind.conf.xml14
-rw-r--r--man/resolved.conf.xml58
-rw-r--r--man/systemd.network.xml47
-rw-r--r--src/basic/fileio.c29
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/ordered-set.c64
-rw-r--r--src/basic/ordered-set.h6
-rw-r--r--src/basic/strv.c20
-rw-r--r--src/basic/strv.h2
-rw-r--r--src/journal/coredump.c2
-rw-r--r--src/journal/journal-send.c1
-rw-r--r--src/journal/journald-audit.c4
-rw-r--r--src/journal/journald-server.c2
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c76
-rw-r--r--src/libsystemd/sd-network/sd-network.c32
-rw-r--r--src/network/networkctl.c81
-rw-r--r--src/network/networkd-dhcp4.c32
-rw-r--r--src/network/networkd-link.c119
-rw-r--r--src/network/networkd-manager.c67
-rw-r--r--src/network/networkd-network-gperf.gperf30
-rw-r--r--src/network/networkd-network.c134
-rw-r--r--src/network/networkd-network.h33
-rw-r--r--src/resolve/resolved-bus.c10
-rw-r--r--src/resolve/resolved-conf.c20
-rw-r--r--src/resolve/resolved-dns-dnssec.c12
-rw-r--r--src/resolve/resolved-dns-query.c13
-rw-r--r--src/resolve/resolved-dns-search-domain.c3
-rw-r--r--src/resolve/resolved-dns-search-domain.h1
-rw-r--r--src/resolve/resolved-dns-transaction.c72
-rw-r--r--src/resolve/resolved-link-bus.c52
-rw-r--r--src/resolve/resolved-link.c56
-rw-r--r--src/resolve/resolved-manager.c6
-rw-r--r--src/resolve/resolved-manager.h1
-rw-r--r--src/shared/dns-domain.c23
-rw-r--r--src/systemd/sd-network.h18
-rw-r--r--src/test/test-dns-domain.c26
39 files changed, 721 insertions, 462 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ba4080cb6e..c96958020f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,7 +24,6 @@ right-away for being misfiled.
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
diff --git a/Makefile.am b/Makefile.am
index 1f204b826c..f933b5228e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -840,6 +840,7 @@ libbasic_la_SOURCES = \
src/basic/siphash24.h \
src/basic/set.h \
src/basic/ordered-set.h \
+ src/basic/ordered-set.c \
src/basic/bitmap.c \
src/basic/bitmap.h \
src/basic/fdset.c \
diff --git a/TODO b/TODO
index 8e4acb51db..b5e6decdb5 100644
--- a/TODO
+++ b/TODO
@@ -173,9 +173,9 @@ Features:
- use equvalent of cat() to insert existing config as a comment, prepended with #.
Upon editor exit, lines with one # are removed, lines with two # are left with one #, etc.
-* exponential backoff in timesyncd and resolved when we cannot reach a server
+* exponential backoff in timesyncd when we cannot reach a server
-* timesyncd + resolved: add ugly bus calls to set NTP and DNS servers per-interface, for usage by NM
+* timesyncd: add ugly bus calls to set NTP servers per-interface, for usage by NM
* extract_many_words() should probably be used by a lot of code that
currently uses FOREACH_WORD and friends. For example, most conf
@@ -190,13 +190,7 @@ Features:
(throughout the codebase, not only PID1)
* resolved:
- - put networkd events and rtnl events at a higher priority, so that
- we always process them before we process client requests
- - DNSSEC
- - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)?
- - synthesize negative cache entries from NSEC/NSEC3 and drop explicit negative caching of authenticated answers
- mDNS/DNS-SD
- - mDNS RR resolving
- service registration
- service/domain/types browsing
- avahi compat
@@ -204,7 +198,8 @@ Features:
- resolved should optionally register additional per-interface LLMNR
names, so that for the container case we can establish the same name
(maybe "host") for referencing the server, everywhere.
- - add API so NM can push DNS server info into resolved
+ - enable DNSSEC by default
+ - allow clients to request DNSSEC for a single lookup even if DNSSEC is off (?)
* refcounting in sd-resolve is borked
diff --git a/man/logind.conf.xml b/man/logind.conf.xml
index 48130aa003..597759e33a 100644
--- a/man/logind.conf.xml
+++ b/man/logind.conf.xml
@@ -294,15 +294,11 @@
<varlistentry>
<term><varname>RemoveIPC=</varname></term>
- <listitem><para>Controls whether System V and POSIX IPC
- objects belonging to the user shall be removed when the user
- fully logs out. Takes a boolean argument. If enabled, the user
- may not consume IPC resources after the last of the user's
- sessions terminated. This covers System V semaphores, shared
- memory and message queues, as well as POSIX shared memory and
- message queues. Note that IPC objects of the root user are
- excluded from the effect of this setting. Defaults to
- <literal>yes</literal>.</para></listitem>
+ <listitem><para>Controls whether System V and POSIX IPC objects belonging to the user shall be removed when the
+ user fully logs out. Takes a boolean argument. If enabled, the user may not consume IPC resources after the
+ last of the user's sessions terminated. This covers System V semaphores, shared memory and message queues, as
+ well as POSIX shared memory and message queues. Note that IPC objects of the root user and other system users
+ are excluded from the effect of this setting. Defaults to <literal>yes</literal>.</para></listitem>
</varlistentry>
</variablelist>
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
index 09a192c933..3aa9c3acb1 100644
--- a/man/resolved.conf.xml
+++ b/man/resolved.conf.xml
@@ -72,40 +72,40 @@
<varlistentry>
<term><varname>DNS=</varname></term>
- <listitem><para>A space-separated list of IPv4 and IPv6
- addresses to be used as system DNS servers. DNS requests are
- sent to one of the listed DNS servers in parallel to any
- per-interface DNS servers acquired from
- <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
- For compatibility reasons, if this setting is not specified,
- the DNS servers listed in
- <filename>/etc/resolv.conf</filename> are used instead, if
- that file exists and any servers are configured in it. This
- setting defaults to the empty list.</para></listitem>
+ <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests
+ are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from
+ <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
+ set at runtime by external applications. For compatibility reasons, if this setting is not specified, the DNS
+ servers listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any servers
+ are configured in it. This setting defaults to the empty list.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>FallbackDNS=</varname></term>
- <listitem><para>A space-separated list of IPv4 and IPv6
- addresses to be used as the fallback DNS servers. Any
- per-interface DNS servers obtained from
+ <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any
+ per-link DNS servers obtained from
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- take precedence over this setting, as do any servers set via
- <varname>DNS=</varname> above or
- <filename>/etc/resolv.conf</filename>. This setting is hence
- only used if no other DNS server information is known. If this
- option is not given, a compiled-in list of DNS servers is used
- instead.</para></listitem>
+ take precedence over this setting, as do any servers set via <varname>DNS=</varname> above or
+ <filename>/etc/resolv.conf</filename>. This setting is hence only used if no other DNS server information is
+ known. If this option is not given, a compiled-in list of DNS servers is used instead.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
- <listitem><para>A space-separated list of search domains. For
- compatibility reasons, if this setting is not specified, the
- search domains listed in <filename>/etc/resolv.conf</filename>
- are used instead, if that file exists and any domains are
- configured in it. This setting defaults to the empty
- list.</para></listitem>
+ <listitem><para>A space-separated list of domains. These domains are used as search suffixes when resolving
+ single-label host names (domain names which contain no dot), in order to qualify them into fully-qualified
+ domain names (FQDNs). Search domains are strictly processed in the order they are specified, until the name
+ with the suffix appended is found. For compatibility reasons, if this setting is not specified, the search
+ domains listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any domains
+ are configured in it. This setting defaults to the empty list.</para>
+
+ <para>Specified domain names may optionally be prefixed with <literal>~</literal>. In this case they do not
+ define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured
+ with the system <varname>DNS=</varname> setting (see above), in case additional, suitable per-link DNS servers
+ are known. If no per-link DNS servers are known using the <literal>~</literal> syntax has no effect. Use the
+ construct <literal>~.</literal> (which is composed of <literal>~</literal> to indicate a routing domain and
+ <literal>.</literal> to indicate the DNS root domain that is the implied suffix of all DNS domains) to use the
+ system DNS server defined with <varname>DNS=</varname> preferably for all domains.</para></listitem>
</varlistentry>
<varlistentry>
@@ -119,8 +119,8 @@
<literal>resolve</literal>, only resolution support is enabled,
but responding is disabled. Note that
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- also maintains per-interface LLMNR settings. LLMNR will be
- enabled on an interface only if the per-interface and the
+ also maintains per-link LLMNR settings. LLMNR will be
+ enabled on a link only if the per-link and the
global setting is on.</para></listitem>
</varlistentry>
@@ -181,9 +181,9 @@
<para>In addition to this global DNSSEC setting
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
- also maintains per-interface DNSSEC settings. For system DNS
+ also maintains per-link DNSSEC settings. For system DNS
servers (see above), only the global DNSSEC setting is in
- effect. For per-interface DNS servers the per-interface
+ effect. For per-link DNS servers the per-link
setting is in effect, unless it is unset in which case the
global setting is used instead.</para>
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 5a6383cfc2..f88751b672 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -396,21 +396,37 @@
described in
<citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
This option may be specified more than once. This setting is read by
- <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem>
- <para>The domains used for DNS resolution over this link. This setting is read by
- <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+ <para>The domains used for DNS host name resolution on this link. Takes a list of DNS domain names which
+ are used as search suffixes for extending single-label host names (host names containing no dots) to become
+ fully qualified domain names (FQDNs). If a single-label host name is resolved on this interface, each of
+ the specified search domains are appended to it in turn, converting it into a fully qualified domain name,
+ until one of them may be successfully resolved.</para>
+
+ <para>The specified domains are also used for routing of DNS queries: look-ups for host names ending in the
+ domains specified here are preferably routed to the DNS servers configured for this interface. If a domain
+ name is prefixed with <literal>~</literal>, the domain name becomes a pure "routing" domain, is used for
+ DNS query routing purposes only and is not used in the described domain search logic. By specifying a
+ routing domain of <literal>~.</literal> (the tilda indicating definition of a routing domain, the dot
+ referring to the DNS root domain which is the implied suffix of all valid DNS names) it is possible to
+ route all DNS traffic preferably to the DNS server specified for this interface. The route domain logic is
+ particularly useful on multi-homed hosts with DNS servers serving particular private DNS zones on each
+ interface.</para>
+
+ <para>This setting is read by
+ <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NTP=</varname></term>
<listitem>
<para>An NTP server address. This option may be specified more than once. This setting is read by
- <citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></para>
+ <citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -701,15 +717,20 @@
<varlistentry>
<term><varname>UseDomains=</varname></term>
<listitem>
- <para>When true (not the default), the domain name
- received from the DHCP server will be used for DNS
- resolution over this link. When a name cannot be resolved
- as specified, the domain name will be used a suffix and
- name resolution of that will be attempted.</para>
-
- <para>This corresponds to the <option>domain</option>
- option in <citerefentry project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- and should not be enabled on untrusted networks.</para>
+ <para>Takes a boolean argument, or a the special value <literal>route</literal>. When true, the domain name
+ received from the DHCP server will be used as DNS search domain over this link, similar to the effect of
+ the <option>Domains=</option> setting. If set to <literal>route</literal>, the domain name received from
+ the DHCP server will be used for routing DNS queries only, but not for searching, similar to the effect of
+ the <option>Domains=</option> setting when the argument is prefixed with <literal>~</literal>. Defaults to
+ false.</para>
+
+ <para>It is recommended to enable this option only on trusted networks, as setting this affects resolution
+ of all host names, in particular to single-label names. It is generally safer to use the supplied domain
+ only as routing domain, rather than as search domain, in order to not have it affect local resolution of
+ single-label names.</para>
+
+ <para>When set to true, this setting corresponds to the <option>domain</option> option in <citerefentry
+ project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</listitem>
</varlistentry>
<varlistentry>
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 5ed5460904..3ff70310e1 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -1251,3 +1251,32 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
*ret = (usec_t) t;
return 0;
}
+
+int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
+ int r;
+
+ assert(s);
+
+ /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
+ * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
+ * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
+ * element, but not before the first one. */
+
+ if (!f)
+ f = stdout;
+
+ if (space) {
+ if (!separator)
+ separator = " ";
+
+ if (*space) {
+ r = fputs(separator, f);
+ if (r < 0)
+ return r;
+ }
+
+ *space = true;
+ }
+
+ return fputs(s, f);
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 95e8698941..9e09574133 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -82,3 +82,5 @@ int tempfn_random_child(const char *p, const char *extra, char **ret);
int write_timestamp_file_atomic(const char *fn, usec_t n);
int read_timestamp_file(const char *fn, usec_t *ret);
+
+int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space);
diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c
new file mode 100644
index 0000000000..2e0bdf6488
--- /dev/null
+++ b/src/basic/ordered-set.c
@@ -0,0 +1,64 @@
+/***
+ 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 "ordered-set.h"
+#include "strv.h"
+
+int ordered_set_consume(OrderedSet *s, void *p) {
+ int r;
+
+ r = ordered_set_put(s, p);
+ if (r <= 0)
+ free(p);
+
+ return r;
+}
+
+int ordered_set_put_strdup(OrderedSet *s, const char *p) {
+ char *c;
+ int r;
+
+ assert(s);
+ assert(p);
+
+ c = strdup(p);
+ if (!c)
+ return -ENOMEM;
+
+ r = ordered_set_consume(s, c);
+ if (r == -EEXIST)
+ return 0;
+
+ return r;
+}
+
+int ordered_set_put_strdupv(OrderedSet *s, char **l) {
+ int n = 0, r;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = ordered_set_put_strdup(s, *i);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ return n;
+}
diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h
index da10e90ff2..ab185c11aa 100644
--- a/src/basic/ordered-set.h
+++ b/src/basic/ordered-set.h
@@ -62,9 +62,15 @@ static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value)
return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL);
}
+int ordered_set_consume(OrderedSet *s, void *p);
+int ordered_set_put_strdup(OrderedSet *s, const char *p);
+int ordered_set_put_strdupv(OrderedSet *s, char **l);
+
#define ORDERED_SET_FOREACH(e, s, i) \
for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); )
DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free);
#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep)
+#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep)
diff --git a/src/basic/strv.c b/src/basic/strv.c
index 0a3d15706f..5532c53ad1 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
+#include "fileio.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -871,3 +872,22 @@ rollback:
nl[k] = NULL;
return -ENOMEM;
}
+
+int fputstrv(FILE *f, char **l, const char *separator, bool *space) {
+ bool b = false;
+ char **s;
+ int r;
+
+ /* Like fputs(), but for strv, and with a less stupid argument order */
+
+ if (!space)
+ space = &b;
+
+ STRV_FOREACH(s, l) {
+ r = fputs_with_space(f, *s, separator, space);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
diff --git a/src/basic/strv.h b/src/basic/strv.h
index bb61db2638..560f90115c 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -169,3 +169,5 @@ char ***strv_free_free(char ***l);
char **strv_skip(char **l, size_t n);
int strv_extend_n(char ***l, const char *value, size_t n);
+
+int fputstrv(FILE *f, char **l, const char *separator, bool *space);
diff --git a/src/journal/coredump.c b/src/journal/coredump.c
index 7df59fe29b..8298b02439 100644
--- a/src/journal/coredump.c
+++ b/src/journal/coredump.c
@@ -794,6 +794,8 @@ int main(int argc, char* argv[]) {
IOVEC_SET_STRING(iovec[j++], core_timestamp);
IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1");
+
+ assert_cc(2 == LOG_CRIT);
IOVEC_SET_STRING(iovec[j++], "PRIORITY=2");
/* Vacuum before we write anything again */
diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c
index 44fa11a00e..def4caab92 100644
--- a/src/journal/journal-send.c
+++ b/src/journal/journal-send.c
@@ -372,6 +372,7 @@ static int fill_iovec_perror_and_send(const char *message, int skip, struct iove
xsprintf(error, "ERRNO=%i", _saved_errno_);
+ assert_cc(3 == LOG_ERR);
IOVEC_SET_STRING(iov[skip+0], "PRIORITY=3");
IOVEC_SET_STRING(iov[skip+1], buffer);
IOVEC_SET_STRING(iov[skip+2], error);
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index 3c13fe0d67..28970131e7 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -397,8 +397,8 @@ static void process_audit_string(Server *s, int type, const char *data, size_t s
sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
IOVEC_SET_STRING(iov[n_iov++], id_field);
- assert_cc(32 == LOG_AUTH);
- IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=32");
+ assert_cc(4 == LOG_FAC(LOG_AUTH));
+ IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=4");
IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit");
type_name = audit_type_name_alloca(type);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 8c1b064e86..fd802b4461 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -866,10 +866,12 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format,
assert(s);
assert(format);
+ assert_cc(3 == LOG_FAC(LOG_DAEMON));
IOVEC_SET_STRING(iovec[n++], "SYSLOG_FACILITY=3");
IOVEC_SET_STRING(iovec[n++], "SYSLOG_IDENTIFIER=systemd-journald");
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver");
+ assert_cc(6 == LOG_INFO);
IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
if (!sd_id128_equal(message_id, SD_ID128_NULL)) {
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index f466b07503..7a119fd488 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -353,6 +353,38 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) {
return 0;
}
+static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
+ _cleanup_free_ char *name = NULL, *normalized = NULL;
+ int r;
+
+ assert(option);
+ assert(ret);
+
+ r = lease_parse_string(option, len, &name);
+ if (r < 0)
+ return r;
+ if (!name) {
+ *ret = mfree(*ret);
+ return 0;
+ }
+
+ r = dns_name_normalize(name, &normalized);
+ if (r < 0)
+ return r;
+
+ if (is_localhost(normalized))
+ return -EINVAL;
+
+ if (dns_name_is_root(normalized))
+ return -EINVAL;
+
+ free(*ret);
+ *ret = normalized;
+ normalized = NULL;
+
+ return 0;
+}
+
static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
assert(option);
assert(ret);
@@ -547,59 +579,23 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
break;
- case SD_DHCP_OPTION_DOMAIN_NAME: {
- _cleanup_free_ char *domainname = NULL, *normalized = NULL;
-
- r = lease_parse_string(option, len, &domainname);
+ case SD_DHCP_OPTION_DOMAIN_NAME:
+ r = lease_parse_domain(option, len, &lease->domainname);
if (r < 0) {
log_debug_errno(r, "Failed to parse domain name, ignoring: %m");
return 0;
}
- r = dns_name_normalize(domainname, &normalized);
- if (r < 0) {
- log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname);
- return 0;
- }
-
- if (is_localhost(normalized)) {
- log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring.");
- break;
- }
-
- free(lease->domainname);
- lease->domainname = normalized;
- normalized = NULL;
-
break;
- }
- case SD_DHCP_OPTION_HOST_NAME: {
- _cleanup_free_ char *hostname = NULL, *normalized = NULL;
-
- r = lease_parse_string(option, len, &hostname);
+ case SD_DHCP_OPTION_HOST_NAME:
+ r = lease_parse_domain(option, len, &lease->hostname);
if (r < 0) {
log_debug_errno(r, "Failed to parse host name, ignoring: %m");
return 0;
}
- r = dns_name_normalize(hostname, &normalized);
- if (r < 0) {
- log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname);
- return 0;
- }
-
- if (is_localhost(normalized)) {
- log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring.");
- return 0;
- }
-
- free(lease->hostname);
- lease->hostname = normalized;
- normalized = NULL;
-
break;
- }
case SD_DHCP_OPTION_ROOT_PATH:
r = lease_parse_string(option, len, &lease->root_path);
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index c1f5867ee4..4b7fad9c81 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -95,10 +95,14 @@ _public_ int sd_network_get_ntp(char ***ret) {
return network_get_strv("NTP", ret);
}
-_public_ int sd_network_get_domains(char ***ret) {
+_public_ int sd_network_get_search_domains(char ***ret) {
return network_get_strv("DOMAINS", ret);
}
+_public_ int sd_network_get_route_domains(char ***ret) {
+ return network_get_strv("ROUTE_DOMAINS", ret);
+}
+
static int network_link_get_string(int ifindex, const char *field, char **ret) {
_cleanup_free_ char *s = NULL, *p = NULL;
int r;
@@ -222,10 +226,14 @@ _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "NTP", ret);
}
-_public_ int sd_network_link_get_domains(int ifindex, char ***ret) {
+_public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "DOMAINS", ret);
}
+_public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
+ return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
+}
+
_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret);
}
@@ -234,26 +242,6 @@ _public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) {
return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret);
}
-_public_ int sd_network_link_get_wildcard_domain(int ifindex) {
- _cleanup_free_ char *p = NULL, *s = NULL;
- int r;
-
- assert_return(ifindex > 0, -EINVAL);
-
- if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0)
- return -ENOMEM;
-
- r = parse_env_file(p, NEWLINE, "WILDCARD_DOMAIN", &s, NULL);
- if (r == -ENOENT)
- return -ENODATA;
- if (r < 0)
- return r;
- if (isempty(s))
- return -ENODATA;
-
- return parse_boolean(s);
-}
-
static inline int MONITOR_TO_FD(sd_network_monitor *m) {
return (int) (unsigned long) m - 1;
}
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 4a8fa4d8f3..253692aa64 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -490,6 +490,9 @@ static int dump_addresses(
static void dump_list(const char *prefix, char **l) {
char **i;
+ if (strv_isempty(l))
+ return;
+
STRV_FOREACH(i, l) {
printf("%*s%s\n",
(int) strlen(prefix),
@@ -502,7 +505,7 @@ static int link_status_one(
sd_netlink *rtnl,
sd_hwdb *hwdb,
const char *name) {
- _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
+ _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
_cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
@@ -552,7 +555,6 @@ static int link_status_one(
return rtnl_log_parse_error(r);
have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0;
-
if (have_mac) {
const uint8_t *p;
bool all_zeroes = true;
@@ -567,44 +569,35 @@ static int link_status_one(
have_mac = false;
}
- sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
+ (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu);
- sd_network_link_get_operational_state(ifindex, &operational_state);
+ (void) sd_network_link_get_operational_state(ifindex, &operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
- sd_network_link_get_setup_state(ifindex, &setup_state);
+ (void) sd_network_link_get_setup_state(ifindex, &setup_state);
setup_state_to_color(setup_state, &on_color_setup, &off_color_setup);
- sd_network_link_get_dns(ifindex, &dns);
- sd_network_link_get_domains(ifindex, &domains);
- r = sd_network_link_get_wildcard_domain(ifindex);
- if (r > 0) {
- char *wildcard;
-
- wildcard = strdup("*");
- if (!wildcard)
- return log_oom();
-
- if (strv_consume(&domains, wildcard) < 0)
- return log_oom();
- }
+ (void) sd_network_link_get_dns(ifindex, &dns);
+ (void) sd_network_link_get_search_domains(ifindex, &search_domains);
+ (void) sd_network_link_get_route_domains(ifindex, &route_domains);
+ (void) sd_network_link_get_ntp(ifindex, &ntp);
sprintf(devid, "n%i", ifindex);
- (void)sd_device_new_from_device_id(&d, devid);
+ (void) sd_device_new_from_device_id(&d, devid);
if (d) {
- (void)sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
- (void)sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
- (void)sd_device_get_property_value(d, "ID_PATH", &path);
+ (void) sd_device_get_property_value(d, "ID_NET_LINK_FILE", &link);
+ (void) sd_device_get_property_value(d, "ID_NET_DRIVER", &driver);
+ (void) sd_device_get_property_value(d, "ID_PATH", &path);
r = sd_device_get_property_value(d, "ID_VENDOR_FROM_DATABASE", &vendor);
if (r < 0)
- (void)sd_device_get_property_value(d, "ID_VENDOR", &vendor);
+ (void) sd_device_get_property_value(d, "ID_VENDOR", &vendor);
r = sd_device_get_property_value(d, "ID_MODEL_FROM_DATABASE", &model);
if (r < 0)
- (void)sd_device_get_property_value(d, "ID_MODEL", &model);
+ (void) sd_device_get_property_value(d, "ID_MODEL", &model);
}
link_get_type_string(iftype, d, &t);
@@ -653,20 +646,14 @@ static int link_status_one(
dump_addresses(rtnl, " Address: ", ifindex);
dump_gateways(rtnl, hwdb, " Gateway: ", ifindex);
- if (!strv_isempty(dns))
- dump_list(" DNS: ", dns);
- if (!strv_isempty(domains))
- dump_list(" Domain: ", domains);
+ dump_list(" DNS: ", dns);
+ dump_list(" Search Domains: ", search_domains);
+ dump_list(" Route Domains: ", route_domains);
- (void) sd_network_link_get_ntp(ifindex, &ntp);
- if (!strv_isempty(ntp))
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
- if (!strv_isempty(carrier_bound_to))
- dump_list("Carrier Bound To: ", carrier_bound_to);
-
- if (!strv_isempty(carrier_bound_by))
- dump_list("Carrier Bound By: ", carrier_bound_by);
+ dump_list("Carrier Bound To: ", carrier_bound_to);
+ dump_list("Carrier Bound By: ", carrier_bound_by);
(void) sd_network_link_get_timezone(ifindex, &tz);
if (tz)
@@ -691,30 +678,30 @@ static int link_status(int argc, char *argv[], void *userdata) {
if (argc <= 1 && !arg_all) {
_cleanup_free_ char *operational_state = NULL;
- _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL;
+ _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains;
const char *on_color_operational, *off_color_operational;
sd_network_get_operational_state(&operational_state);
operational_state_to_color(operational_state, &on_color_operational, &off_color_operational);
- printf("%s%s%s State: %s%s%s\n",
+ printf("%s%s%s State: %s%s%s\n",
on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational,
on_color_operational, strna(operational_state), off_color_operational);
- dump_addresses(rtnl, " Address: ", 0);
- dump_gateways(rtnl, hwdb, " Gateway: ", 0);
+ dump_addresses(rtnl, " Address: ", 0);
+ dump_gateways(rtnl, hwdb, " Gateway: ", 0);
sd_network_get_dns(&dns);
- if (!strv_isempty(dns))
- dump_list(" DNS: ", dns);
+ dump_list(" DNS: ", dns);
+
+ sd_network_get_search_domains(&search_domains);
+ dump_list("Search Domains: ", search_domains);
- sd_network_get_domains(&domains);
- if (!strv_isempty(domains))
- dump_list(" Domain: ", domains);
+ sd_network_get_route_domains(&route_domains);
+ dump_list(" Route Domains: ", route_domains);
sd_network_get_ntp(&ntp);
- if (!strv_isempty(ntp))
- dump_list(" NTP: ", ntp);
+ dump_list(" NTP: ", ntp);
return 0;
}
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index c7d22876bc..59eccb392f 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -158,7 +158,7 @@ static int dhcp_lease_lost(Link *link) {
log_link_warning(link, "DHCP lease lost");
- if (link->network->dhcp_routes) {
+ if (link->network->dhcp_use_routes) {
_cleanup_free_ sd_dhcp_route **routes = NULL;
int n, i;
@@ -223,7 +223,7 @@ static int dhcp_lease_lost(Link *link) {
}
}
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
@@ -238,11 +238,11 @@ static int dhcp_lease_lost(Link *link) {
}
}
- if (link->network->dhcp_hostname) {
+ if (link->network->dhcp_use_hostname) {
const char *hostname = NULL;
- if (link->network->hostname)
- hostname = link->network->hostname;
+ if (link->network->dhcp_hostname)
+ hostname = link->network->dhcp_hostname;
else
(void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
@@ -412,7 +412,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
link->dhcp_lease = sd_dhcp_lease_ref(lease);
link_dirty(link);
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
uint16_t mtu;
r = sd_dhcp_lease_get_mtu(lease, &mtu);
@@ -423,11 +423,11 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
- if (link->network->dhcp_hostname) {
+ if (link->network->dhcp_use_hostname) {
const char *hostname = NULL;
- if (link->network->hostname)
- hostname = link->network->hostname;
+ if (link->network->dhcp_hostname)
+ hostname = link->network->dhcp_hostname;
else
(void) sd_dhcp_lease_get_hostname(lease, &hostname);
@@ -438,7 +438,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
}
}
- if (link->network->dhcp_timezone) {
+ if (link->network->dhcp_use_timezone) {
const char *tz = NULL;
(void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
@@ -571,14 +571,14 @@ int dhcp4_configure(Link *link) {
return r;
}
- if (link->network->dhcp_mtu) {
+ if (link->network->dhcp_use_mtu) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_INTERFACE_MTU);
if (r < 0)
return r;
}
- if (link->network->dhcp_routes) {
+ if (link->network->dhcp_use_routes) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)
@@ -589,7 +589,7 @@ int dhcp4_configure(Link *link) {
return r;
}
- /* Always acquire the timezone and NTP*/
+ /* Always acquire the timezone and NTP */
r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NTP_SERVER);
if (r < 0)
return r;
@@ -598,18 +598,18 @@ int dhcp4_configure(Link *link) {
if (r < 0)
return r;
- if (link->network->dhcp_sendhost) {
+ if (link->network->dhcp_send_hostname) {
_cleanup_free_ char *hostname = NULL;
const char *hn = NULL;
- if (!link->network->hostname) {
+ if (!link->network->dhcp_hostname) {
hostname = gethostname_malloc();
if (!hostname)
return -ENOMEM;
hn = hostname;
} else
- hn = link->network->hostname;
+ hn = link->network->dhcp_hostname;
if (!is_localhost(hn)) {
r = sd_dhcp_client_set_hostname(link->dhcp_client, hn);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index bbda691c08..a2f0eceb6d 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -767,7 +767,7 @@ static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
addresses[n_addresses++] = ia;
}
- if (link->network->dhcp_dns &&
+ if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
const struct in_addr *da = NULL;
int n;
@@ -812,7 +812,7 @@ static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
addresses[n_addresses++] = ia;
}
- if (link->network->dhcp_ntp &&
+ if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
const struct in_addr *da = NULL;
int n;
@@ -2729,9 +2729,10 @@ int link_save(Link *link) {
admin_state, oper_state);
if (link->network) {
- char **address, **domain;
bool space;
sd_dhcp6_lease *dhcp6_lease = NULL;
+ const char *dhcp_domainname = NULL;
+ char **dhcp6_domains = NULL;
if (link->dhcp6_client) {
r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
@@ -2743,14 +2744,9 @@ int link_save(Link *link) {
fputs("DNS=", f);
space = false;
- STRV_FOREACH(address, link->network->dns) {
- if (space)
- fputc(' ', f);
- fputs(*address, f);
- space = true;
- }
+ fputstrv(f, link->network->dns, NULL, &space);
- if (link->network->dhcp_dns &&
+ if (link->network->dhcp_use_dns &&
link->dhcp_lease) {
const struct in_addr *addresses;
@@ -2763,7 +2759,7 @@ int link_save(Link *link) {
}
}
- if (link->network->dhcp_dns && dhcp6_lease) {
+ if (link->network->dhcp_use_dns && dhcp6_lease) {
struct in6_addr *in6_addrs;
r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
@@ -2778,14 +2774,9 @@ int link_save(Link *link) {
fputs("NTP=", f);
space = false;
- STRV_FOREACH(address, link->network->ntp) {
- if (space)
- fputc(' ', f);
- fputs(*address, f);
- space = true;
- }
+ fputstrv(f, link->network->ntp, NULL, &space);
- if (link->network->dhcp_ntp &&
+ if (link->network->dhcp_use_ntp &&
link->dhcp_lease) {
const struct in_addr *addresses;
@@ -2798,10 +2789,9 @@ int link_save(Link *link) {
}
}
- if (link->network->dhcp_ntp && dhcp6_lease) {
+ if (link->network->dhcp_use_ntp && dhcp6_lease) {
struct in6_addr *in6_addrs;
char **hosts;
- char **hostname;
r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease,
&in6_addrs);
@@ -2813,58 +2803,41 @@ int link_save(Link *link) {
}
r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts);
- if (r > 0) {
- STRV_FOREACH(hostname, hosts) {
- if (space)
- fputc(' ', f);
- fputs(*hostname, f);
- space = true;
- }
- }
+ if (r > 0)
+ fputstrv(f, hosts, NULL, &space);
}
fputc('\n', f);
- fputs("DOMAINS=", f);
- space = false;
- STRV_FOREACH(domain, link->network->domains) {
- if (space)
- fputc(' ', f);
- fputs(*domain, f);
- space = true;
+ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
+ if (link->dhcp_lease)
+ (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
+
+ if (dhcp6_lease)
+ (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
}
- if (link->network->dhcp_domains &&
- link->dhcp_lease) {
- const char *domainname;
+ fputs("DOMAINS=", f);
+ fputstrv(f, link->network->search_domains, NULL, &space);
- r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
- if (r >= 0) {
- if (space)
- fputc(' ', f);
- fputs(domainname, f);
- space = true;
- }
- }
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
- if (link->network->dhcp_domains && dhcp6_lease) {
- char **domains;
-
- r = sd_dhcp6_lease_get_domains(dhcp6_lease, &domains);
- if (r >= 0) {
- STRV_FOREACH(domain, domains) {
- if (space)
- fputc(' ', f);
- fputs(*domain, f);
- space = true;
- }
- }
- }
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES && dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
fputc('\n', f);
- fprintf(f, "WILDCARD_DOMAIN=%s\n",
- yes_no(link->network->wildcard_domain));
+ fputs("ROUTE_DOMAINS=", f);
+ fputstrv(f, link->network->route_domains, NULL, NULL);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp_domainname)
+ fputs_with_space(f, dhcp_domainname, NULL, &space);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE && dhcp6_domains)
+ fputstrv(f, dhcp6_domains, NULL, &space);
+
+ fputc('\n', f);
fprintf(f, "LLMNR=%s\n",
resolve_support_to_string(link->network->llmnr));
@@ -2880,12 +2853,8 @@ int link_save(Link *link) {
fputs("DNSSEC_NTA=", f);
space = false;
- SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i) {
- if (space)
- fputc(' ', f);
- fputs(n, f);
- space = true;
- }
+ SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i)
+ fputs_with_space(f, n, NULL, &space);
fputc('\n', f);
}
@@ -2925,12 +2894,8 @@ int link_save(Link *link) {
bool space = false;
fputs("CARRIER_BOUND_TO=", f);
- HASHMAP_FOREACH(carrier, link->bound_to_links, i) {
- if (space)
- fputc(' ', f);
- fputs(carrier->ifname, f);
- space = true;
- }
+ HASHMAP_FOREACH(carrier, link->bound_to_links, i)
+ fputs_with_space(f, carrier->ifname, NULL, &space);
fputc('\n', f);
}
@@ -2940,12 +2905,8 @@ int link_save(Link *link) {
bool space = false;
fputs("CARRIER_BOUND_BY=", f);
- HASHMAP_FOREACH(carrier, link->bound_by_links, i) {
- if (space)
- fputc(' ', f);
- fputs(carrier->ifname, f);
- space = true;
- }
+ HASHMAP_FOREACH(carrier, link->bound_by_links, i)
+ fputs_with_space(f, carrier->ifname, NULL, &space);
fputc('\n', f);
}
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 24f5304cb0..c10635d202 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -29,12 +29,14 @@
#include "bus-util.h"
#include "conf-parser.h"
#include "def.h"
+#include "dns-domain.h"
#include "fd-util.h"
#include "fileio.h"
#include "libudev-private.h"
#include "local-addresses.h"
#include "netlink-util.h"
#include "networkd.h"
+#include "ordered-set.h"
#include "path-util.h"
#include "set.h"
#include "udev-util.h"
@@ -776,7 +778,7 @@ static int manager_connect_rtnl(Manager *m) {
return 0;
}
-static int set_put_in_addr(Set *s, const struct in_addr *address) {
+static int ordered_set_put_in_addr(OrderedSet *s, const struct in_addr *address) {
char *p;
int r;
@@ -786,21 +788,21 @@ static int set_put_in_addr(Set *s, const struct in_addr *address) {
if (r < 0)
return r;
- r = set_consume(s, p);
+ r = ordered_set_consume(s, p);
if (r == -EEXIST)
return 0;
return r;
}
-static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
+static int ordered_set_put_in_addrv(OrderedSet *s, const struct in_addr *addresses, int n) {
int r, i, c = 0;
assert(s);
assert(n <= 0 || addresses);
for (i = 0; i < n; i++) {
- r = set_put_in_addr(s, addresses+i);
+ r = ordered_set_put_in_addr(s, addresses+i);
if (r < 0)
return r;
@@ -810,27 +812,24 @@ static int set_put_in_addrv(Set *s, const struct in_addr *addresses, int n) {
return c;
}
-static void print_string_set(FILE *f, const char *field, Set *s) {
+static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
bool space = false;
Iterator i;
char *p;
- if (set_isempty(s))
+ if (ordered_set_isempty(s))
return;
fputs(field, f);
- SET_FOREACH(p, s, i) {
- if (space)
- fputc(' ', f);
- fputs(p, f);
- space = true;
- }
+ ORDERED_SET_FOREACH(p, s, i)
+ fputs_with_space(f, p, NULL, &space);
+
fputc('\n', f);
}
static int manager_save(Manager *m) {
- _cleanup_set_free_free_ Set *dns = NULL, *ntp = NULL, *domains = NULL;
+ _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
Link *link;
Iterator i;
_cleanup_free_ char *temp_path = NULL;
@@ -843,16 +842,20 @@ static int manager_save(Manager *m) {
assert(m->state_file);
/* We add all NTP and DNS server to a set, to filter out duplicates */
- dns = set_new(&string_hash_ops);
+ dns = ordered_set_new(&string_hash_ops);
if (!dns)
return -ENOMEM;
- ntp = set_new(&string_hash_ops);
+ ntp = ordered_set_new(&string_hash_ops);
if (!ntp)
return -ENOMEM;
- domains = set_new(&string_hash_ops);
- if (!domains)
+ search_domains = ordered_set_new(&dns_name_hash_ops);
+ if (!search_domains)
+ return -ENOMEM;
+
+ route_domains = ordered_set_new(&dns_name_hash_ops);
+ if (!route_domains)
return -ENOMEM;
HASHMAP_FOREACH(link, m->links, i) {
@@ -866,15 +869,19 @@ static int manager_save(Manager *m) {
continue;
/* First add the static configured entries */
- r = set_put_strdupv(dns, link->network->dns);
+ r = ordered_set_put_strdupv(dns, link->network->dns);
if (r < 0)
return r;
- r = set_put_strdupv(ntp, link->network->ntp);
+ r = ordered_set_put_strdupv(ntp, link->network->ntp);
if (r < 0)
return r;
- r = set_put_strdupv(domains, link->network->domains);
+ r = ordered_set_put_strdupv(search_domains, link->network->search_domains);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_put_strdupv(route_domains, link->network->route_domains);
if (r < 0)
return r;
@@ -882,36 +889,41 @@ static int manager_save(Manager *m) {
continue;
/* Secondly, add the entries acquired via DHCP */
- if (link->network->dhcp_dns) {
+ if (link->network->dhcp_use_dns) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
if (r > 0) {
- r = set_put_in_addrv(dns, addresses, r);
+ r = ordered_set_put_in_addrv(dns, addresses, r);
if (r < 0)
return r;
} else if (r < 0 && r != -ENODATA)
return r;
}
- if (link->network->dhcp_ntp) {
+ if (link->network->dhcp_use_ntp) {
const struct in_addr *addresses;
r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
if (r > 0) {
- r = set_put_in_addrv(ntp, addresses, r);
+ r = ordered_set_put_in_addrv(ntp, addresses, r);
if (r < 0)
return r;
} else if (r < 0 && r != -ENODATA)
return r;
}
- if (link->network->dhcp_domains) {
+ if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
const char *domainname;
r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname);
if (r >= 0) {
- r = set_put_strdup(domains, domainname);
+
+ if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES)
+ r = ordered_set_put_strdup(search_domains, domainname);
+ else
+ r = ordered_set_put_strdup(route_domains, domainname);
+
if (r < 0)
return r;
} else if (r != -ENODATA)
@@ -934,7 +946,8 @@ static int manager_save(Manager *m) {
print_string_set(f, "DNS=", dns);
print_string_set(f, "NTP=", ntp);
- print_string_set(f, "DOMAINS=", domains);
+ print_string_set(f, "DOMAINS=", search_domains);
+ print_string_set(f, "ROUTE_DOMAINS=", route_domains);
r = fflush_and_check(f);
if (r < 0)
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 2f2a36ccca..409df1709f 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -43,7 +43,7 @@ Network.IPv6Token, config_parse_ipv6token,
Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp)
Network.Address, config_parse_address, 0, 0
Network.Gateway, config_parse_gateway, 0, 0
-Network.Domains, config_parse_domains, 0, offsetof(Network, domains)
+Network.Domains, config_parse_domains, 0, 0
Network.DNS, config_parse_strv, 0, offsetof(Network, dns)
Network.LLMNR, config_parse_resolve_support, 0, offsetof(Network, llmnr)
Network.MulticastDNS, config_parse_resolve_support, 0, offsetof(Network, mdns)
@@ -68,19 +68,19 @@ Route.Metric, config_parse_route_priority,
Route.Scope, config_parse_route_scope, 0, 0
Route.PreferredSource, config_parse_preferred_src, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_ntp)
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomains, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_sendhost)
-DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, hostname)
+DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone)
+DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec)
DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns)
@@ -101,9 +101,9 @@ BridgeFDB.MACAddress, config_parse_fdb_hwaddr,
BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
-DHCP.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
-DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domains)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index c11cb3dcb3..e1a811129d 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -105,11 +105,11 @@ static int network_load_one(Manager *manager, const char *filename) {
*d = '\0';
network->dhcp = ADDRESS_FAMILY_NO;
- network->dhcp_ntp = true;
- network->dhcp_dns = true;
- network->dhcp_hostname = true;
- network->dhcp_routes = true;
- network->dhcp_sendhost = true;
+ network->dhcp_use_ntp = true;
+ network->dhcp_use_dns = true;
+ network->dhcp_use_hostname = true;
+ network->dhcp_use_routes = true;
+ network->dhcp_send_hostname = true;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
@@ -227,13 +227,14 @@ void network_free(Network *network) {
free(network->description);
free(network->dhcp_vendor_class_identifier);
- free(network->hostname);
+ free(network->dhcp_hostname);
free(network->mac);
strv_free(network->ntp);
strv_free(network->dns);
- strv_free(network->domains);
+ strv_free(network->search_domains);
+ strv_free(network->route_domains);
strv_free(network->bind_carrier);
netdev_unref(network->bridge);
@@ -384,7 +385,10 @@ int network_apply(Manager *manager, Network *network, Link *link) {
route->protocol = RTPROT_STATIC;
}
- if (network->dns || network->ntp || network->domains) {
+ if (!strv_isempty(network->dns) ||
+ !strv_isempty(network->ntp) ||
+ !strv_isempty(network->search_domains) ||
+ !strv_isempty(network->route_domains)) {
manager_dirty(manager);
link_dirty(link);
}
@@ -469,49 +473,85 @@ int config_parse_netdev(const char *unit,
return 0;
}
-int config_parse_domains(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = userdata;
- char ***domains = data;
- char **domain;
+int config_parse_domains(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *p;
+ Network *n = data;
int r;
- r = config_parse_strv(unit, filename, line, section, section_line,
- lvalue, ltype, rvalue, domains, userdata);
- if (r < 0)
- return r;
+ assert(n);
+ assert(lvalue);
+ assert(rvalue);
- strv_uniq(*domains);
- network->wildcard_domain = !!strv_find(*domains, "*");
+ if (isempty(rvalue)) {
+ n->search_domains = strv_free(n->search_domains);
+ n->route_domains = strv_free(n->route_domains);
+ return 0;
+ }
- STRV_FOREACH(domain, *domains) {
- if (is_localhost(*domain))
- log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
- else {
- r = dns_name_is_valid(*domain);
- if (r <= 0 && !streq(*domain, "*")) {
- if (r < 0)
- log_error_errno(r, "Failed to validate domain name: %s: %m", *domain);
- if (r == 0)
- log_warning("Domain name is not valid, ignoring assignment: %s", *domain);
- } else
+ p = rvalue;
+ for (;;) {
+ _cleanup_free_ char *w = NULL, *normalized = NULL;
+ const char *domain;
+ bool is_route;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ is_route = w[0] == '~';
+ domain = is_route ? w + 1 : w;
+
+ if (dns_name_is_root(domain) || streq(domain, "*")) {
+ /* If the root domain appears as is, or the special token "*" is found, we'll consider this as
+ * routing domain, unconditionally. */
+ is_route = true;
+ domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */
+
+ } else {
+ r = dns_name_normalize(domain, &normalized);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "'%s' is not a valid domain name, ignoring.", domain);
+ continue;
+ }
+
+ domain = normalized;
+
+ if (is_localhost(domain)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain);
continue;
+ }
}
- strv_remove(*domains, *domain);
+ if (is_route) {
+ r = strv_extend(&n->route_domains, domain);
+ if (r < 0)
+ return log_oom();
- /* We removed one entry, make sure we don't skip the next one */
- domain--;
+ } else {
+ r = strv_extend(&n->search_domains, domain);
+ if (r < 0)
+ return log_oom();
+ }
}
+ strv_uniq(n->route_domains);
+ strv_uniq(n->search_domains);
+
return 0;
}
@@ -930,7 +970,7 @@ int config_parse_dnssec_negative_trust_anchors(
Network *n = data;
int r;
- assert(filename);
+ assert(n);
assert(lvalue);
assert(rvalue);
@@ -965,3 +1005,13 @@ int config_parse_dnssec_negative_trust_anchors(
return 0;
}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting");
+
+static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
+ [DHCP_USE_DOMAINS_NO] = "no",
+ [DHCP_USE_DOMAINS_ROUTE] = "route",
+ [DHCP_USE_DOMAINS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index b07fa41abc..626dfbd40a 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -52,6 +52,14 @@ typedef enum IPv6PrivacyExtensions {
_IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
} IPv6PrivacyExtensions;
+typedef enum DHCPUseDomains {
+ DHCP_USE_DOMAINS_NO,
+ DHCP_USE_DOMAINS_YES,
+ DHCP_USE_DOMAINS_ROUTE,
+ _DHCP_USE_DOMAINS_MAX,
+ _DHCP_USE_DOMAINS_INVALID = -1,
+} DHCPUseDomains;
+
struct Network {
Manager *manager;
@@ -79,17 +87,17 @@ struct Network {
AddressFamilyBoolean dhcp;
DCHPClientIdentifier dhcp_client_identifier;
char *dhcp_vendor_class_identifier;
- char *hostname;
- bool dhcp_dns;
- bool dhcp_ntp;
- bool dhcp_mtu;
- bool dhcp_hostname;
- bool dhcp_domains;
- bool dhcp_sendhost;
+ char *dhcp_hostname;
+ bool dhcp_use_dns;
+ bool dhcp_use_ntp;
+ bool dhcp_use_mtu;
+ bool dhcp_use_hostname;
+ DHCPUseDomains dhcp_use_domains;
+ bool dhcp_send_hostname;
bool dhcp_broadcast;
bool dhcp_critical;
- bool dhcp_routes;
- bool dhcp_timezone;
+ bool dhcp_use_routes;
+ bool dhcp_use_timezone;
unsigned dhcp_route_metric;
/* DHCP Server Support */
@@ -141,8 +149,7 @@ struct Network {
Hashmap *routes_by_section;
Hashmap *fdb_entries_by_section;
- bool wildcard_domain;
- char **domains, **dns, **ntp, **bind_carrier;
+ char **search_domains, **route_domains, **dns, **ntp, **bind_carrier;
ResolveSupport llmnr;
ResolveSupport mdns;
@@ -175,6 +182,7 @@ int config_parse_timezone(const char *unit, const char *filename, unsigned line,
int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_dnssec_negative_trust_anchors(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_dhcp_use_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* Legacy IPv4LL support */
int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -188,3 +196,6 @@ int network_object_find(sd_bus *bus, const char *path, const char *interface, vo
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
+
+const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
+DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index d8e8638327..834ae837de 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -1217,19 +1217,19 @@ static int bus_property_get_search_domains(
assert(reply);
assert(m);
- r = sd_bus_message_open_container(reply, 'a', "(is)");
+ r = sd_bus_message_open_container(reply, 'a', "(isb)");
if (r < 0)
return r;
LIST_FOREACH(domains, d, m->search_domains) {
- r = sd_bus_message_append(reply, "(is)", 0, d->name);
+ r = sd_bus_message_append(reply, "(isb)", 0, d->name, d->route_only);
if (r < 0)
return r;
}
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(domains, d, l->search_domains) {
- r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
+ r = sd_bus_message_append(reply, "(isb)", l->ifindex, d->name, d->route_only);
if (r < 0)
return r;
}
@@ -1450,7 +1450,7 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0),
- SD_BUS_PROPERTY("Domains", "a(is)", bus_property_get_search_domains, 0, 0),
+ SD_BUS_PROPERTY("SearchDomains", "a(isb)", bus_property_get_search_domains, 0, 0),
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
@@ -1463,7 +1463,7 @@ static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
- SD_BUS_METHOD("SetLinkDomains", "ias", NULL, bus_method_set_link_search_domains, 0),
+ SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_search_domains, 0),
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0),
diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c
index 88df7534c4..6d8c35164e 100644
--- a/src/resolve/resolved-conf.c
+++ b/src/resolve/resolved-conf.c
@@ -80,20 +80,34 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con
int manager_add_search_domain_by_string(Manager *m, const char *domain) {
DnsSearchDomain *d;
+ bool route_only;
int r;
assert(m);
assert(domain);
+ route_only = *domain == '~';
+ if (route_only)
+ domain++;
+
+ if (dns_name_is_root(domain) || streq(domain, "*")) {
+ route_only = true;
+ domain = ".";
+ }
+
r = dns_search_domain_find(m->search_domains, domain, &d);
if (r < 0)
return r;
- if (r > 0) {
+ if (r > 0)
dns_search_domain_move_back_and_unmark(d);
- return 0;
+ else {
+ r = dns_search_domain_new(m, &d, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+ if (r < 0)
+ return r;
}
- return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+ d->route_only = route_only;
+ return 0;
}
int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 8e3c78e7bf..76c801cce8 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -28,18 +28,6 @@
#include "resolved-dns-packet.h"
#include "string-table.h"
-/* Open question:
- *
- * How does the DNSSEC canonical form of a hostname with a label
- * containing a dot look like, the way DNS-SD does it?
- *
- * TODO:
- *
- * - 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
- * */
-
#define VERIFY_RRS_MAX 256
#define MAX_KEY_SIZE (32*1024)
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index ef977b0948..a00851658e 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -93,17 +93,20 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
assert(c);
- if (c->search_domain && c->search_domain->linked) {
+ if (c->search_domain && c->search_domain->linked)
next = c->search_domain->domains_next;
+ else
+ next = dns_scope_get_search_domains(c->scope);
+ for (;;) {
if (!next) /* We hit the end of the list */
return 0;
- } else {
- next = dns_scope_get_search_domains(c->scope);
+ if (!next->route_only)
+ break;
- if (!next) /* OK, there's nothing. */
- return 0;
+ /* Skip over route-only domains */
+ next = next->domains_next;
}
dns_search_domain_unref(c->search_domain);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
index f9d966abb1..356c05b9a4 100644
--- a/src/resolve/resolved-dns-search-domain.c
+++ b/src/resolve/resolved-dns-search-domain.c
@@ -42,9 +42,6 @@ int dns_search_domain_new(
if (r < 0)
return r;
- if (dns_name_is_root(normalized))
- return -EINVAL;
-
if (l) {
if (l->n_search_domains >= LINK_SEARCH_DOMAINS_MAX)
return -E2BIG;
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
index 2e0af31dda..c1903b334f 100644
--- a/src/resolve/resolved-dns-search-domain.h
+++ b/src/resolve/resolved-dns-search-domain.h
@@ -44,6 +44,7 @@ struct DnsSearchDomain {
char *name;
bool marked:1;
+ bool route_only:1;
bool linked:1;
LIST_FIELDS(DnsSearchDomain, domains);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 77f9ef0a83..802ad860a4 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -732,6 +732,55 @@ fail:
dns_transaction_complete(t, DNS_TRANSACTION_ERRNO);
}
+static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
+ int r;
+
+ assert(t);
+
+ /* Checks whether the answer is positive, i.e. either a direct
+ * answer to the question, or a CNAME/DNAME for it */
+
+ r = dns_answer_match_key(t->answer, t->key, flags);
+ if (r != 0)
+ return r;
+
+ r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
+ if (r != 0)
+ return r;
+
+ return false;
+}
+
+static int dns_transaction_fix_rcode(DnsTransaction *t) {
+ int r;
+
+ assert(t);
+
+ /* Fix up the RCODE to SUCCESS if we get at least one matching RR in a response. Note that this contradicts the
+ * DNS RFCs a bit. Specifically, RFC 6604 Section 3 clarifies that the RCODE shall say something about a
+ * CNAME/DNAME chain element coming after the last chain element contained in the message, and not the first
+ * one included. However, it also indicates that not all DNS servers implement this correctly. Moreover, when
+ * using DNSSEC we usually only can prove the first element of a CNAME/DNAME chain anyway, hence let's settle
+ * on always processing the RCODE as referring to the immediate look-up we do, i.e. the first element of a
+ * CNAME/DNAME chain. This way, we uniformly handle CNAME/DNAME chains, regardless if the DNS server
+ * incorrectly implements RCODE, whether DNSSEC is in use, or whether the DNS server only supplied us with an
+ * incomplete CNAME/DNAME chain.
+ *
+ * Or in other words: if we get at least one positive reply in a message we patch NXDOMAIN to become SUCCESS,
+ * and then rely on the CNAME chasing logic to figure out that there's actually a CNAME error with a new
+ * lookup. */
+
+ if (t->answer_rcode != DNS_RCODE_NXDOMAIN)
+ return 0;
+
+ r = dns_transaction_has_positive_answer(t, NULL);
+ if (r <= 0)
+ return r;
+
+ t->answer_rcode = DNS_RCODE_SUCCESS;
+ return 0;
+}
+
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
usec_t ts;
int r;
@@ -923,6 +972,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
t->answer_dnssec_result = _DNSSEC_RESULT_INVALID;
t->answer_authenticated = false;
+ r = dns_transaction_fix_rcode(t);
+ if (r < 0)
+ goto fail;
+
/* Block GC while starting requests for additional DNSSEC RRs */
t->block_gc++;
r = dns_transaction_request_dnssec_keys(t);
@@ -1635,25 +1688,6 @@ static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *
return 1;
}
-static int dns_transaction_has_positive_answer(DnsTransaction *t, DnsAnswerFlags *flags) {
- int r;
-
- assert(t);
-
- /* Checks whether the answer is positive, i.e. either a direct
- * answer to the question, or a CNAME/DNAME for it */
-
- r = dns_answer_match_key(t->answer, t->key, flags);
- if (r != 0)
- return r;
-
- r = dns_answer_find_cname_or_dname(t->answer, t->key, NULL, flags);
- if (r != 0)
- return r;
-
- return false;
-}
-
static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const char *name) {
int r;
diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c
index 2ca7ece851..e6b087f412 100644
--- a/src/resolve/resolved-link-bus.c
+++ b/src/resolve/resolved-link-bus.c
@@ -75,12 +75,12 @@ static int property_get_domains(
assert(reply);
assert(l);
- r = sd_bus_message_open_container(reply, 'a', "s");
+ r = sd_bus_message_open_container(reply, 'a', "(sb)");
if (r < 0)
return r;
LIST_FOREACH(domains, d, l->search_domains) {
- r = sd_bus_message_append(reply, "s", d->name);
+ r = sd_bus_message_append(reply, "(sb)", d->name, d->route_only);
if (r < 0)
return r;
}
@@ -242,47 +242,71 @@ clear:
}
int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char **domains = NULL;
Link *l = userdata;
- char **i;
int r;
assert(message);
assert(l);
- r = sd_bus_message_read_strv(message, &domains);
+ r = sd_bus_message_enter_container(message, 'a', "(sb)");
if (r < 0)
return r;
- STRV_FOREACH(i, domains) {
+ for (;;) {
+ const char *name;
+ int route_only;
- r = dns_name_is_valid(*i);
+ r = sd_bus_message_read(message, "(sb)", &name, &route_only);
if (r < 0)
return r;
if (r == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", *i);
- if (dns_name_is_root(*i))
+ break;
+
+ r = dns_name_is_valid(name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", name);
+ if (!route_only && dns_name_is_root(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
}
dns_search_domain_mark_all(l->search_domains);
- STRV_FOREACH(i, domains) {
+ r = sd_bus_message_rewind(message, false);
+ if (r < 0)
+ return r;
+
+ for (;;) {
DnsSearchDomain *d;
+ const char *name;
+ int route_only;
- r = dns_search_domain_find(l->search_domains, *i, &d);
+ r = sd_bus_message_read(message, "(sb)", &name, &route_only);
+ if (r < 0)
+ goto clear;
+ if (r == 0)
+ break;
+
+ r = dns_search_domain_find(l->search_domains, name, &d);
if (r < 0)
goto clear;
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
- r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
if (r < 0)
goto clear;
}
+
+ d->route_only = route_only;
}
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ goto clear;
+
dns_search_domain_unlink_marked(l->search_domains);
return sd_bus_reply_method_return(message, NULL);
@@ -430,7 +454,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
- SD_BUS_PROPERTY("Domains", "as", property_get_domains, 0, 0),
+ SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
SD_BUS_PROPERTY("LLMNR", "s", property_get_resolve_support, offsetof(Link, llmnr_support), 0),
SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0),
SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0),
@@ -438,7 +462,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0),
SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0),
- SD_BUS_METHOD("SetDomains", "as", NULL, bus_link_method_set_search_domains, 0),
+ SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_search_domains, 0),
SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0),
SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0),
SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0),
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index e2f9c8b400..37dd4a6e78 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -377,38 +377,60 @@ clear:
return r;
}
+static int link_update_search_domain_one(Link *l, const char *name, bool route_only) {
+ DnsSearchDomain *d;
+ int r;
+
+ r = dns_search_domain_find(l->search_domains, name, &d);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ dns_search_domain_move_back_and_unmark(d);
+ else {
+ r = dns_search_domain_new(l->manager, &d, DNS_SEARCH_DOMAIN_LINK, l, name);
+ if (r < 0)
+ return r;
+ }
+
+ d->route_only = route_only;
+ return 0;
+}
+
static int link_update_search_domains(Link *l) {
- _cleanup_strv_free_ char **domains = NULL;
+ _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL;
char **i;
- int r;
+ int r, q;
assert(l);
- r = sd_network_link_get_domains(l->ifindex, &domains);
- if (r == -ENODATA) {
+ r = sd_network_link_get_search_domains(l->ifindex, &sdomains);
+ if (r < 0 && r != -ENODATA)
+ goto clear;
+
+ q = sd_network_link_get_route_domains(l->ifindex, &rdomains);
+ if (q < 0 && q != -ENODATA) {
+ r = q;
+ goto clear;
+ }
+
+ if (r == -ENODATA && q == -ENODATA) {
/* networkd knows nothing about this interface, and that's fine. */
r = 0;
goto clear;
}
- if (r < 0)
- goto clear;
dns_search_domain_mark_all(l->search_domains);
- STRV_FOREACH(i, domains) {
- DnsSearchDomain *d;
-
- r = dns_search_domain_find(l->search_domains, *i, &d);
+ STRV_FOREACH(i, sdomains) {
+ r = link_update_search_domain_one(l, *i, false);
if (r < 0)
goto clear;
+ }
- if (r > 0)
- dns_search_domain_move_back_and_unmark(d);
- else {
- r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
- if (r < 0)
- goto clear;
- }
+ STRV_FOREACH(i, rdomains) {
+ r = link_update_search_domain_one(l, *i, true);
+ if (r < 0)
+ goto clear;
}
dns_search_domain_unlink_marked(l->search_domains);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 704f012c37..4306403834 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -206,7 +206,7 @@ static int manager_rtnl_listen(Manager *m) {
if (r < 0)
return r;
- r = sd_netlink_attach_event(m->rtnl, m->event, 0);
+ r = sd_netlink_attach_event(m->rtnl, m->event, SD_EVENT_PRIORITY_IMPORTANT);
if (r < 0)
return r;
@@ -314,6 +314,10 @@ static int manager_network_monitor_listen(Manager *m) {
if (r < 0)
return r;
+ r = sd_event_source_set_priority(m->network_event_source, SD_EVENT_PRIORITY_IMPORTANT+5);
+ if (r < 0)
+ return r;
+
(void) sd_event_source_set_description(m->network_event_source, "network-monitor");
return 0;
diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h
index a4291d57ff..1af49c8fb9 100644
--- a/src/resolve/resolved-manager.h
+++ b/src/resolve/resolved-manager.h
@@ -74,6 +74,7 @@ struct Manager {
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
+ bool permit_domain_search;
bool need_builtin_fallbacks:1;
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 3ad409fc29..7ef4ad3cf8 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -405,11 +405,17 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
int dns_name_concat(const char *a, const char *b, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
- const char *p = a;
+ const char *p;
bool first = true;
int r;
- assert(a);
+ if (a)
+ p = a;
+ else if (b) {
+ p = b;
+ b = NULL;
+ } else
+ goto finish;
for (;;) {
char label[DNS_LABEL_MAX];
@@ -457,12 +463,21 @@ int dns_name_concat(const char *a, const char *b, char **_ret) {
n += r;
}
+finish:
if (n > DNS_HOSTNAME_MAX)
return -EINVAL;
if (_ret) {
- if (!GREEDY_REALLOC(ret, allocated, n + 1))
- return -ENOMEM;
+ if (n == 0) {
+ /* Nothing appended? If so, generate at least a single dot, to indicate the DNS root domain */
+ if (!GREEDY_REALLOC(ret, allocated, 2))
+ return -ENOMEM;
+
+ ret[n++] = '.';
+ } else {
+ if (!GREEDY_REALLOC(ret, allocated, n + 1))
+ return -ENOMEM;
+ }
ret[n] = 0;
*_ret = ret;
diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h
index 653c61a162..ff0d2b191e 100644
--- a/src/systemd/sd-network.h
+++ b/src/systemd/sd-network.h
@@ -64,8 +64,11 @@ int sd_network_get_dns(char ***dns);
* representations of IP addresses */
int sd_network_get_ntp(char ***ntp);
-/* Get the search/routing domains for all links. */
-int sd_network_get_domains(char ***domains);
+/* Get the search domains for all links. */
+int sd_network_get_search_domains(char ***domains);
+
+/* Get the search domains for all links. */
+int sd_network_get_route_domains(char ***domains);
/* Get setup state from ifindex.
* Possible states:
@@ -134,8 +137,11 @@ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta);
int sd_network_link_get_lldp(int ifindex, char **lldp);
-/* Get the DNS domain names for a given link. */
-int sd_network_link_get_domains(int ifindex, char ***domains);
+/* Get the search DNS domain names for a given link. */
+int sd_network_link_get_search_domains(int ifindex, char ***domains);
+
+/* Get the route DNS domain names for a given link. */
+int sd_network_link_get_route_domains(int ifindex, char ***domains);
/* Get the CARRIERS to which current link is bound to. */
int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers);
@@ -146,10 +152,6 @@ int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers);
/* Get the timezone that was learnt on a specific link. */
int sd_network_link_get_timezone(int ifindex, char **timezone);
-/* Returns whether or not domains that don't match any link should be resolved
- * on this link. 1 for yes, 0 for no and negative value for error */
-int sd_network_link_get_wildcard_domain(int ifindex);
-
/* Monitor object */
typedef struct sd_network_monitor sd_network_monitor;
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index 3b260ee75d..3efc61ad0a 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -186,7 +186,7 @@ static void test_dns_name_normalize_one(const char *what, const char *expect, in
}
static void test_dns_name_normalize(void) {
- test_dns_name_normalize_one("", "", 0);
+ test_dns_name_normalize_one("", ".", 0);
test_dns_name_normalize_one("f", "f", 0);
test_dns_name_normalize_one("f.waldi", "f.waldi", 0);
test_dns_name_normalize_one("f \\032.waldi", "f\\032\\032.waldi", 0);
@@ -194,7 +194,7 @@ static void test_dns_name_normalize(void) {
test_dns_name_normalize_one("..", NULL, -EINVAL);
test_dns_name_normalize_one(".foobar", NULL, -EINVAL);
test_dns_name_normalize_one("foobar.", "foobar", 0);
- test_dns_name_normalize_one(".", "", 0);
+ test_dns_name_normalize_one(".", ".", 0);
}
static void test_dns_name_equal_one(const char *a, const char *b, int ret) {
@@ -340,10 +340,18 @@ static void test_dns_name_concat_one(const char *a, const char *b, int r, const
}
static void test_dns_name_concat(void) {
+ test_dns_name_concat_one("", "", 0, ".");
+ test_dns_name_concat_one(".", "", 0, ".");
+ test_dns_name_concat_one("", ".", 0, ".");
+ test_dns_name_concat_one(".", ".", 0, ".");
test_dns_name_concat_one("foo", "bar", 0, "foo.bar");
test_dns_name_concat_one("foo.foo", "bar.bar", 0, "foo.foo.bar.bar");
test_dns_name_concat_one("foo", NULL, 0, "foo");
+ test_dns_name_concat_one("foo", ".", 0, "foo");
test_dns_name_concat_one("foo.", "bar.", 0, "foo.bar");
+ test_dns_name_concat_one(NULL, NULL, 0, ".");
+ test_dns_name_concat_one(NULL, ".", 0, ".");
+ test_dns_name_concat_one(NULL, "foo", 0, "foo");
}
static void test_dns_name_is_valid_one(const char *s, int ret) {
@@ -429,7 +437,7 @@ static void test_dns_service_join_one(const char *a, const char *b, const char *
assert_se(dns_service_split(t, &x, &y, &z) >= 0);
assert_se(streq_ptr(a, x));
assert_se(streq_ptr(b, y));
- assert_se(streq_ptr(c, z));
+ assert_se(dns_name_equal(c, z) > 0);
}
static void test_dns_service_join(void) {
@@ -460,18 +468,18 @@ static void test_dns_service_split_one(const char *joined, const char *a, const
if (y) {
assert_se(dns_service_join(x, y, z, &t) == 0);
- assert_se(streq_ptr(joined, t));
+ assert_se(dns_name_equal(joined, t) > 0);
} else
- assert_se(!x && streq_ptr(z, joined));
+ assert_se(!x && dns_name_equal(z, joined) > 0);
}
static void test_dns_service_split(void) {
- test_dns_service_split_one("", NULL, NULL, "", 0);
+ test_dns_service_split_one("", NULL, NULL, ".", 0);
test_dns_service_split_one("foo", NULL, NULL, "foo", 0);
test_dns_service_split_one("foo.bar", NULL, NULL, "foo.bar", 0);
test_dns_service_split_one("_foo.bar", NULL, NULL, "_foo.bar", 0);
- test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", "", 0);
- test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", "", 0);
+ test_dns_service_split_one("_foo._bar", NULL, "_foo._bar", ".", 0);
+ test_dns_service_split_one("_meh._foo._bar", "_meh", "_foo._bar", ".", 0);
test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0);
}
@@ -490,7 +498,7 @@ static void test_dns_name_change_suffix(void) {
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff");
test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff");
- test_dns_name_change_suffix_one("", "", "", 1, "");
+ test_dns_name_change_suffix_one("", "", "", 1, ".");
test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
}