resolved: add a generic DnsSearchDomain concept
authorLennart Poettering <lennart@poettering.net>
Tue, 24 Nov 2015 20:12:51 +0000 (21:12 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 25 Nov 2015 20:58:38 +0000 (21:58 +0100)
With this change, we add a new object to resolved, "DnsSearchDomain="
which wraps a search domain. This is then used to introduce a global
search domain list, in addition to the existing per-link search domain
list which is reword to make use of this new object too.

This is preparation for implement proper unicast DNS search domain
support.

15 files changed:
Makefile.am
man/resolved.conf.xml
src/resolve/resolved-conf.c
src/resolve/resolved-conf.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-search-domain.c [new file with mode: 0644]
src/resolve/resolved-dns-search-domain.h [new file with mode: 0644]
src/resolve/resolved-gperf.gperf
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-resolv-conf.c
src/resolve/resolved.conf.in

index cac5c5e..296f2c7 100644 (file)
@@ -5165,6 +5165,8 @@ systemd_resolved_SOURCES = \
        src/resolve/resolved-dns-scope.c \
        src/resolve/resolved-dns-server.h \
        src/resolve/resolved-dns-server.c \
+       src/resolve/resolved-dns-search-domain.h \
+       src/resolve/resolved-dns-search-domain.c \
        src/resolve/resolved-dns-cache.h \
        src/resolve/resolved-dns-cache.c \
        src/resolve/resolved-dns-zone.h \
index c9034a9..4680b6a 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
@@ -77,7 +77,7 @@
         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, ,
+        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
       </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>
+      </varlistentry>
+
+      <varlistentry>
         <term><varname>LLMNR=</varname></term>
         <listitem><para>Takes a boolean argument or
         <literal>resolve</literal>. Controls Link-Local Multicast Name
index c11a8ba..dbac451 100644 (file)
@@ -71,10 +71,49 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con
                         break;
 
                 r = manager_add_dns_server_by_string(m, type, word);
-                if (r < 0) {
+                if (r < 0)
                         log_warning_errno(r, "Failed to add DNS server address '%s', ignoring.", word);
-                        continue;
-                }
+        }
+
+        return 0;
+}
+
+int manager_add_search_domain_by_string(Manager *m, const char *domain) {
+        DnsSearchDomain *d;
+        int r;
+
+        assert(m);
+        assert(domain);
+
+        r = dns_search_domain_find(m->search_domains, domain, &d);
+        if (r < 0)
+                return r;
+        if (r > 0) {
+                dns_search_domain_move_back_and_unmark(d);
+                return 0;
+        }
+
+        return dns_search_domain_new(m, NULL, DNS_SEARCH_DOMAIN_SYSTEM, NULL, domain);
+}
+
+int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
+        int r;
+
+        assert(m);
+        assert(string);
+
+        for(;;) {
+                _cleanup_free_ char *word = NULL;
+
+                r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                r = manager_add_search_domain_by_string(m, word);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to add search domain '%s', ignoring.", word);
         }
 
         return 0;
@@ -122,6 +161,45 @@ int config_parse_dns_servers(
         return 0;
 }
 
+int config_parse_search_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) {
+
+        Manager *m = userdata;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(m);
+
+        if (isempty(rvalue))
+                /* Empty assignment means clear the list */
+                dns_search_domain_unlink_all(m->search_domains);
+        else {
+                /* Otherwise, add to the list */
+                r = manager_parse_search_domains_and_warn(m, rvalue);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse search domains string '%s'. Ignoring.", rvalue);
+                        return 0;
+                }
+        }
+
+        /* If we have a manual setting, then we stop reading
+         * /etc/resolv.conf */
+        m->read_resolv_conf = false;
+
+        return 0;
+}
+
 int config_parse_support(
                 const char *unit,
                 const char *filename,
index 170f3e9..28d2549 100644 (file)
 
 #include "resolved-manager.h"
 
-int manager_parse_dns_server(Manager *m, DnsServerType type, const char *string);
 int manager_parse_config_file(Manager *m);
 
-int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
+int manager_add_search_domain_by_string(Manager *m, const char *domain);
+int manager_parse_search_domains_and_warn(Manager *m, const char *string);
+
 int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word);
+int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string);
 
 const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, unsigned length);
 
 int config_parse_dns_servers(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_search_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);
 int config_parse_support(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);
index 5174502..2e47691 100644 (file)
@@ -103,7 +103,6 @@ DnsScope* dns_scope_free(DnsScope *s) {
         dns_zone_flush(&s->zone);
 
         LIST_REMOVE(scopes, s->manager->dns_scopes, s);
-        strv_free(s->domains);
         free(s);
 
         return NULL;
@@ -323,7 +322,7 @@ int dns_scope_tcp_socket(DnsScope *s, int family, const union in_addr_union *add
 }
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain) {
-        char **i;
+        DnsSearchDomain *d;
 
         assert(s);
         assert(domain);
@@ -345,8 +344,8 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co
             dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
                 return DNS_SCOPE_NO;
 
-        STRV_FOREACH(i, s->domains)
-                if (dns_name_endswith(domain, *i) > 0)
+        LIST_FOREACH(domains, d, dns_scope_get_search_domains(s))
+                if (dns_name_endswith(domain, d->name) > 0)
                         return DNS_SCOPE_YES;
 
         switch (s->protocol) {
@@ -850,3 +849,14 @@ void dns_scope_dump(DnsScope *s, FILE *f) {
                 dns_cache_dump(&s->cache, f);
         }
 }
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
+
+        if (s->protocol != DNS_PROTOCOL_DNS)
+                return NULL;
+
+        if (s->link)
+                return s->link->search_domains;
+
+        return NULL;
+}
index f9b2bfd..f69d51b 100644 (file)
@@ -47,8 +47,6 @@ struct DnsScope {
 
         Link *link;
 
-        char **domains;
-
         DnsCache cache;
         DnsZone zone;
 
@@ -91,3 +89,5 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr);
 void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p);
 
 void dns_scope_dump(DnsScope *s, FILE *f);
+
+DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c
new file mode 100644 (file)
index 0000000..5d927bb
--- /dev/null
@@ -0,0 +1,223 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "dns-domain.h"
+#include "resolved-dns-search-domain.h"
+
+int dns_search_domain_new(
+                Manager *m,
+                DnsSearchDomain **ret,
+                DnsSearchDomainType type,
+                Link *l,
+                const char *name) {
+
+        _cleanup_free_ char *normalized = NULL;
+        DnsSearchDomain *d, *tail;
+        int r;
+
+        assert(m);
+        assert((type == DNS_SEARCH_DOMAIN_LINK) == !!l);
+        assert(name);
+
+        r = dns_name_normalize(name, &normalized);
+        if (r < 0)
+                return r;
+
+        r = dns_name_root(normalized);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return -EINVAL;
+
+        d = new0(DnsSearchDomain, 1);
+        if (!d)
+                return -ENOMEM;
+
+        d->n_ref = 1;
+        d->manager = m;
+        d->type = type;
+        d->name = normalized;
+        normalized = NULL;
+
+        switch (type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                d->link = l;
+                LIST_FIND_TAIL(domains, l->search_domains, tail);
+                LIST_INSERT_AFTER(domains, l->search_domains, tail, d);
+                break;
+
+        case DNS_SERVER_SYSTEM:
+                LIST_FIND_TAIL(domains, m->search_domains, tail);
+                LIST_INSERT_AFTER(domains, m->search_domains, tail, d);
+                break;
+
+        default:
+                assert_not_reached("Unknown search domain type");
+        }
+
+        d->linked = true;
+
+        if (ret)
+                *ret = d;
+
+        return 0;
+}
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d) {
+        if (!d)
+                return NULL;
+
+        assert(d->n_ref > 0);
+        d->n_ref++;
+
+        return d;
+}
+
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d) {
+        if (!d)
+                return NULL;
+
+        assert(d->n_ref > 0);
+        d->n_ref--;
+
+        if (d->n_ref > 0)
+                return NULL;
+
+        free(d->name);
+        free(d);
+
+        return NULL;
+}
+
+void dns_search_domain_unlink(DnsSearchDomain *d) {
+        assert(d);
+        assert(d->manager);
+
+        if (!d->linked)
+                return;
+
+        switch (d->type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                assert(d->link);
+                LIST_REMOVE(domains, d->link->search_domains, d);
+                break;
+
+        case DNS_SEARCH_DOMAIN_SYSTEM:
+                LIST_REMOVE(domains, d->manager->search_domains, d);
+                break;
+        }
+
+        d->linked = false;
+
+        dns_search_domain_unref(d);
+}
+
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d) {
+        DnsSearchDomain *tail;
+
+        assert(d);
+
+        if (!d->marked)
+                return;
+
+        d->marked = false;
+
+        if (!d->linked || !d->domains_next)
+                return;
+
+        switch (d->type) {
+
+        case DNS_SEARCH_DOMAIN_LINK:
+                assert(d->link);
+                LIST_FIND_TAIL(domains, d, tail);
+                LIST_REMOVE(domains, d->link->search_domains, d);
+                LIST_INSERT_AFTER(domains, d->link->search_domains, tail, d);
+                break;
+
+        case DNS_SEARCH_DOMAIN_SYSTEM:
+                LIST_FIND_TAIL(domains, d, tail);
+                LIST_REMOVE(domains, d->manager->search_domains, d);
+                LIST_INSERT_AFTER(domains, d->manager->search_domains, tail, d);
+                break;
+
+        default:
+                assert_not_reached("Unknown search domain type");
+        }
+}
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first) {
+        DnsSearchDomain *next;
+
+        if (!first)
+                return;
+
+        next = first->domains_next;
+        dns_search_domain_unlink(first);
+
+        dns_search_domain_unlink_all(next);
+}
+
+void dns_search_domain_unlink_marked(DnsSearchDomain *first) {
+        DnsSearchDomain *next;
+
+        if (!first)
+                return;
+
+        next = first->domains_next;
+
+        if (first->marked)
+                dns_search_domain_unlink(first);
+
+        dns_search_domain_unlink_marked(next);
+}
+
+void dns_search_domain_mark_all(DnsSearchDomain *first) {
+        if (!first)
+                return;
+
+        first->marked = true;
+        dns_search_domain_mark_all(first->domains_next);
+}
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) {
+        DnsSearchDomain *d;
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        LIST_FOREACH(domains, d, first) {
+
+                r = dns_name_equal(name, d->name);
+                if (r < 0)
+                        return r;
+                if (r > 0) {
+                        *ret = d;
+                        return 1;
+                }
+        }
+
+        *ret = NULL;
+        return 0;
+}
diff --git a/src/resolve/resolved-dns-search-domain.h b/src/resolve/resolved-dns-search-domain.h
new file mode 100644 (file)
index 0000000..20b4412
--- /dev/null
@@ -0,0 +1,71 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2015 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 "macro.h"
+
+typedef struct DnsSearchDomain DnsSearchDomain;
+
+typedef enum DnsSearchDomainType {
+        DNS_SEARCH_DOMAIN_SYSTEM,
+        DNS_SEARCH_DOMAIN_LINK,
+} DnsSearchDomainType;
+
+#include "resolved-link.h"
+#include "resolved-manager.h"
+
+struct DnsSearchDomain {
+        Manager *manager;
+
+        unsigned n_ref;
+
+        DnsSearchDomainType type;
+        Link *link;
+
+        char *name;
+
+        bool marked:1;
+
+        bool linked:1;
+        LIST_FIELDS(DnsSearchDomain, domains);
+};
+
+int dns_search_domain_new(
+                Manager *m,
+                DnsSearchDomain **ret,
+                DnsSearchDomainType type,
+                Link *link,
+                const char *name);
+
+DnsSearchDomain* dns_search_domain_ref(DnsSearchDomain *d);
+DnsSearchDomain* dns_search_domain_unref(DnsSearchDomain *d);
+
+void dns_search_domain_unlink(DnsSearchDomain *d);
+void dns_search_domain_move_back_and_unmark(DnsSearchDomain *d);
+
+void dns_search_domain_unlink_all(DnsSearchDomain *first);
+void dns_search_domain_unlink_marked(DnsSearchDomain *first);
+void dns_search_domain_mark_all(DnsSearchDomain *first);
+
+int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsSearchDomain*, dns_search_domain_unref);
index dc4ef74..5066265 100644 (file)
@@ -16,4 +16,5 @@ struct ConfigPerfItem;
 %%
 Resolve.DNS,          config_parse_dns_servers,    DNS_SERVER_SYSTEM,   0
 Resolve.FallbackDNS,  config_parse_dns_servers,    DNS_SERVER_FALLBACK, 0
+Resolve.Domains,      config_parse_search_domains, 0,                   0
 Resolve.LLMNR,        config_parse_support,        0,                   offsetof(Manager, llmnr_support)
index 4931fc9..c367160 100644 (file)
@@ -66,6 +66,7 @@ Link *link_free(Link *l) {
                 return NULL;
 
         link_flush_dns_servers(l);
+        dns_search_domain_unlink_all(l->search_domains);
 
         while (l->addresses)
                 link_address_free(l->addresses);
@@ -219,29 +220,56 @@ clear:
         return r;
 }
 
-static int link_update_domains(Link *l) {
+static int link_update_search_domains(Link *l) {
+        _cleanup_strv_free_ char **domains = NULL;
+        char **i;
         int r;
 
-        if (!l->unicast_scope)
-                return 0;
-
-        l->unicast_scope->domains = strv_free(l->unicast_scope->domains);
+        assert(l);
 
-        r = sd_network_link_get_domains(l->ifindex,
-                                        &l->unicast_scope->domains);
+        r = sd_network_link_get_domains(l->ifindex, &domains);
         if (r < 0)
-                return r;
+                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);
+                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;
+                }
+        }
+
+        dns_search_domain_unlink_marked(l->search_domains);
         return 0;
+
+clear:
+        dns_search_domain_unlink_all(l->search_domains);
+        return r;
 }
 
 int link_update_monitor(Link *l) {
+        int r;
+
         assert(l);
 
         link_update_dns_servers(l);
         link_update_llmnr_support(l);
         link_allocate_scopes(l);
-        link_update_domains(l);
+
+        r = link_update_search_domains(l);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read search domains for interface %s, ignoring: %m", l->name);
+
         link_add_rrs(l, false);
 
         return 0;
index 76e96e7..d72461d 100644 (file)
@@ -30,6 +30,8 @@ typedef struct Link Link;
 typedef struct LinkAddress LinkAddress;
 
 #include "resolved-dns-rr.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
 #include "resolved-manager.h"
 
 struct LinkAddress {
@@ -57,6 +59,8 @@ struct Link {
         LIST_HEAD(DnsServer, dns_servers);
         DnsServer *current_dns_server;
 
+        LIST_HEAD(DnsSearchDomain, search_domains);
+
         Support llmnr_support;
 
         DnsScope *unicast_scope;
@@ -84,6 +88,8 @@ DnsServer* link_find_dns_server(Link *l, int family, const union in_addr_union *
 DnsServer* link_get_dns_server(Link *l);
 void link_next_dns_server(Link *l);
 
+void link_flush_search_domains(Link *l);
+
 int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr_union *in_addr);
 LinkAddress *link_address_free(LinkAddress *a);
 int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m);
index c3c61a0..0771ac8 100644 (file)
@@ -536,6 +536,8 @@ Manager *manager_free(Manager *m) {
         manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
         manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
 
+        dns_search_domain_unlink_all(m->search_domains);
+
         while ((l = hashmap_first(m->links)))
                link_free(l);
 
index 8a716b3..0683e23 100644 (file)
@@ -40,6 +40,8 @@ enum Support {
 };
 
 #include "resolved-dns-query.h"
+#include "resolved-dns-search-domain.h"
+#include "resolved-dns-server.h"
 #include "resolved-dns-stream.h"
 #include "resolved-link.h"
 
@@ -70,7 +72,10 @@ struct Manager {
         LIST_HEAD(DnsServer, fallback_dns_servers);
         DnsServer *current_dns_server;
 
+        LIST_HEAD(DnsSearchDomain, search_domains);
+
         bool need_builtin_fallbacks:1;
+
         bool read_resolv_conf:1;
         usec_t resolv_conf_mtime;
 
index 98c1720..2ab0008 100644 (file)
@@ -86,13 +86,12 @@ int manager_read_resolv_conf(Manager *m) {
         }
 
         manager_mark_dns_servers(m, DNS_SERVER_SYSTEM);
+        dns_search_domain_mark_all(m->search_domains);
 
         FOREACH_LINE(line, f, r = -errno; goto clear) {
                 const char *a;
                 char *l;
 
-                truncate_nl(line);
-
                 l = strstrip(line);
                 if (*l == '#' || *l == ';')
                         continue;
@@ -105,9 +104,22 @@ int manager_read_resolv_conf(Manager *m) {
 
                         continue;
                 }
+
+                a = first_word(l, "domain");
+                if (!a) /* We treat "domain" lines, and "search" lines as equivalent, and add both to our list. */
+                        a = first_word(l, "search");
+                if (a) {
+                        r = manager_parse_search_domains_and_warn(m, a);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to parse search domain string '%s', ignoring.", a);
+                }
         }
 
+        /* Flush out all servers and search domains that are still
+         * marked. Those are then ones that didn't appear in the new
+         * /etc/resolv.conf */
         manager_flush_marked_dns_servers(m, DNS_SERVER_SYSTEM);
+        dns_search_domain_unlink_marked(m->search_domains);
 
         /* Whenever /etc/resolv.conf changes, start using the first
          * DNS server of it. This is useful to deal with broken
@@ -123,6 +135,7 @@ int manager_read_resolv_conf(Manager *m) {
 
 clear:
         manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
+        dns_search_domain_unlink_all(m->search_domains);
         return r;
 }
 
@@ -211,6 +224,7 @@ int manager_write_resolv_conf(Manager *m) {
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
+        DnsSearchDomain *d;
         DnsServer *s;
         Iterator i;
         Link *l;
@@ -239,10 +253,16 @@ int manager_write_resolv_conf(Manager *m) {
                         return r;
         }
 
+        LIST_FOREACH(domains, d, m->search_domains) {
+                r = ordered_set_put(domains, d->name);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return r;
+        }
+
         /* Then, add the per-link servers and domains */
         HASHMAP_FOREACH(l, m->links, i) {
-                char **domain;
-
                 LIST_FOREACH(servers, s, l->dns_servers) {
                         r = ordered_set_put(dns, s);
                         if (r == -EEXIST)
@@ -251,11 +271,8 @@ int manager_write_resolv_conf(Manager *m) {
                                 return r;
                 }
 
-                if (!l->unicast_scope)
-                        continue;
-
-                STRV_FOREACH(domain, l->unicast_scope->domains) {
-                        r = ordered_set_put(domains, *domain);
+                LIST_FOREACH(domains, d, l->search_domains) {
+                        r = ordered_set_put(domains, d->name);
                         if (r == -EEXIST)
                                 continue;
                         if (r < 0)
index 3eb19e4..39ecf83 100644 (file)
@@ -14,4 +14,5 @@
 [Resolve]
 #DNS=
 #FallbackDNS=@DNS_SERVERS@
+#Domains=
 #LLMNR=yes