]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #2437 from poettering/dnssec19
authorTom Gundersen <teg@jklm.no>
Tue, 26 Jan 2016 17:07:19 +0000 (18:07 +0100)
committerTom Gundersen <teg@jklm.no>
Tue, 26 Jan 2016 17:07:19 +0000 (18:07 +0100)
nineteenth dnssec patch

39 files changed:
CONTRIBUTING.md
Makefile.am
TODO
man/logind.conf.xml
man/resolved.conf.xml
man/systemd.network.xml
src/basic/fileio.c
src/basic/fileio.h
src/basic/ordered-set.c [new file with mode: 0644]
src/basic/ordered-set.h
src/basic/strv.c
src/basic/strv.h
src/journal/coredump.c
src/journal/journal-send.c
src/journal/journald-audit.c
src/journal/journald-server.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd/sd-network/sd-network.c
src/network/networkctl.c
src/network/networkd-dhcp4.c
src/network/networkd-link.c
src/network/networkd-manager.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/resolve/resolved-bus.c
src/resolve/resolved-conf.c
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-search-domain.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-link-bus.c
src/resolve/resolved-link.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/shared/dns-domain.c
src/systemd/sd-network.h
src/test/test-dns-domain.c

index ba4080cb6e475a580b73fa033d5ced6fc64c880d..c96958020f429c08280acdc47d9272294ddca40f 100644 (file)
@@ -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
index 1f204b826cf8b6bb8c256a2b1bc9c0dcf178b677..f933b5228e6e1229d85d931b9413f9b32fca42a6 100644 (file)
@@ -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 8e4acb51dbae82662b51511340f64e8f0ca0b51d..b5e6decdb5e67e84bd71e57787e2b0b2ae71544f 100644 (file)
--- 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
 
index 48130aa003291a1fc915968de1a221fbafd7b67b..597759e33a9dd4822c7f0bdb9597bf264f27c0f5 100644 (file)
       <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>
index 09a192c93344c1ae3e9536321c187d82bb308ee9..3aa9c3acb11f0ea27befb8f5b01d0cd2fcb11e4e 100644 (file)
 
       <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>
         <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>
 
 
         <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>
 
index 5a6383cfc29de14616a358e2a55a5d475a050d55..f88751b672f0c21c24124782d8649416d889fe27 100644 (file)
             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>
         <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>
index 5ed5460904b4207d960ac4b11d7e94eca6997a99..3ff70310e184d4f4e3aceed7b7a0d7763d2dbbd6 100644 (file)
@@ -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);
+}
index 95e8698941c4d9914ae47828ecf83b67f925f58a..9e0957413380f8fb7680db28e3f53cdae176a817 100644 (file)
@@ -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 (file)
index 0000000..2e0bdf6
--- /dev/null
@@ -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;
+}
index da10e90ff2811cdb4979b81536c165c63a3bce9d..ab185c11aa601c44a314c06eaf393c8a380ba4d2 100644 (file)
@@ -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)
index 0a3d15706f7f89cbfeb7949b89ed925d965957c0..5532c53ad1936412b52f0d13bb57c6a6f65604f1 100644 (file)
@@ -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;
+}
index bb61db263816d9049b90f1f0711dcce2a8b07e08..560f90115cb47bfb21b22b68f0eafd0295f0452b 100644 (file)
@@ -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);
index 7df59fe29be2c2be916414861cb27411c0f03708..8298b024396a897bd9e9c9b0c5f51672082ffafb 100644 (file)
@@ -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 */
index 44fa11a00e9995d3f48924352d83a625ca044f7d..def4caab929b226563e219bdf4c86cdd3a1a0307 100644 (file)
@@ -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);
index 3c13fe0d675c56a654709ba9b64348d0d376977c..28970131e70e761c0b7b01d40fb829d0166f6f44 100644 (file)
@@ -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);
index 8c1b064e8664a8a447a7e074d8e074c07d2fc2cc..fd802b44614eb1ba8637f95a1f07ded035d0cda8 100644 (file)
@@ -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)) {
index f466b07503a81659f97aac9f5ca54007dc9101f1..7a119fd4887cc45342992b715f4e2e9853b6d925 100644 (file)
@@ -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);
index c1f5867ee4aec859040e2cd539721c122ea2f79d..4b7fad9c81375fb684ca5ce0be576d51409876c3 100644 (file)
@@ -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;
 }
index 4a8fa4d8f3c64809bd9080fb5f811fe6497cf44f..253692aa6487228853ce7e022ef749fd8f79f0a6 100644 (file)
@@ -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;
         }
index c7d22876bcc03ea38edf7de03d13d50cdfd4489a..59eccb392f66674a367532fd96a5d1d84e774dea 100644 (file)
@@ -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);
index bbda691c083a56e1cf4f54047d5e7c4b80fc5d7c..a2f0eceb6de8caba51a1f153388157fd22f2ddd4 100644 (file)
@@ -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);
         }
index 24f5304cb01dcd0d42e3ac69cb5c2c66142e43fd..c10635d202a7a2dd11fc60ada051cf5b4e94b01f 100644 (file)
 #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)
index 2f2a36ccca802e2d102f3492d5185523c674e21a..409df1709f76643eb507af81359b7177f7c8fd58 100644 (file)
@@ -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)
index c11cb3dcb3c48fa33ff5d64f50dc8a889b4b4e2c..e1a811129dd1fb6c0046601131bf337b3f997be8 100644 (file)
@@ -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);
index b07fa41abcfc4d74dc012f9f73cb95fd39bbe873..626dfbd40a2e45f56f03e2da4903ed147e63fa89 100644 (file)
@@ -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_;
index d8e863832746e48cd9593e397978a2a02221cc57..834ae837de3cf6ec17eb0d380619edf1d75e4f75 100644 (file)
@@ -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),
index 88df7534c4bc4b9d84d966736b403c3b498ddabd..6d8c35164e06b022d18640bcd48ec7e78de48b58 100644 (file)
@@ -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) {
index 8e3c78e7bfc7faa6c6f05953846e84b210a19a8c..76c801cce8f3c5145371d8a16c12539f154ea89c 100644 (file)
 #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)
 
index ef977b0948e74f050332a001694faec814019670..a00851658e4661544c263fffe4628c4dfa4ae5b7 100644 (file)
@@ -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);
index f9d966abb1ba6308f497567c6d96458a44c51d25..356c05b9a47a2f5ae472f6266f03c162578a0b3a 100644 (file)
@@ -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;
index 2e0af31dda7fa8750ad5fc203d788bab83a113f2..c1903b334f04d91cd6671fe961d3a20d4633affb 100644 (file)
@@ -44,6 +44,7 @@ struct DnsSearchDomain {
         char *name;
 
         bool marked:1;
+        bool route_only:1;
 
         bool linked:1;
         LIST_FIELDS(DnsSearchDomain, domains);
index 77f9ef0a8306d59b6f35f15df078a86407e8035d..802ad860a48b8c1b65d440143f282dae02a149f7 100644 (file)
@@ -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;
 
index 2ca7ece851bc613b15e3f3cffa5c035eb92c18e3..e6b087f41254bc686e21e6595f7f465080425254 100644 (file)
@@ -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),
index e2f9c8b400845d80a9fa479a94e914ed6ac267ee..37dd4a6e786f61d8b4ea2b9d6e6f856babdf06e8 100644 (file)
@@ -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);
index 704f012c379b1521bdbd5b35ead572db9578ba10..4306403834955fc7b647738e3948b9336fab1eb0 100644 (file)
@@ -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;
index a4291d57ff92df5ca4afadbde87d0ea7d4f18cd6..1af49c8fb9ce21655b27ca2f55f6148867ad266f 100644 (file)
@@ -74,6 +74,7 @@ struct Manager {
 
         LIST_HEAD(DnsSearchDomain, search_domains);
         unsigned n_search_domains;
+        bool permit_domain_search;
 
         bool need_builtin_fallbacks:1;
 
index 3ad409fc29af8fe11b5354e6420956539ce4fde5..7ef4ad3cf8169bf1d709f046965ce2ab1230edfe 100644 (file)
@@ -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;
index 653c61a162dba935c81ff6c04d6c67be77bad16d..ff0d2b191e6f0073c61cdf1914571e2cd2d41cf6 100644 (file)
@@ -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;
 
index 3b260ee75de8b21a2146a9ab49deb773ae2ec4fd..3efc61ad0a985862497db6329c83e313f5ceb3ab 100644 (file)
@@ -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);
 }