chiark / gitweb /
resolved: pull in domain names from sd-network
[elogind.git] / src / resolve / resolved-manager.c
index 1d33c2ae19cc473bcd112237c1a263433e161190..bfbdc7d55bc43aacd49cf64e4ec5075c9c4e2062 100644 (file)
@@ -426,23 +426,25 @@ static int manager_llmnr_start(Manager *m) {
         if (r < 0)
                 return r;
 
-        r = manager_llmnr_ipv6_udp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
-
         r = manager_llmnr_ipv4_tcp_fd(m);
         if (r == -EADDRINUSE)
                 goto eaddrinuse;
         if (r < 0)
                 return r;
 
-        r = manager_llmnr_ipv6_tcp_fd(m);
-        if (r == -EADDRINUSE)
-                goto eaddrinuse;
-        if (r < 0)
-                return r;
+        if (socket_ipv6_is_supported()) {
+                r = manager_llmnr_ipv6_udp_fd(m);
+                if (r == -EADDRINUSE)
+                        goto eaddrinuse;
+                if (r < 0)
+                        return r;
+
+                r = manager_llmnr_ipv6_tcp_fd(m);
+                if (r == -EADDRINUSE)
+                        goto eaddrinuse;
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
 
@@ -450,6 +452,7 @@ eaddrinuse:
         log_warning("There appears to be another LLMNR respondering running. Turning off LLMNR support.");
         m->llmnr_support = SUPPORT_NO;
         manager_llmnr_stop(m);
+
         return 0;
 }
 
@@ -536,10 +539,8 @@ Manager *manager_free(Manager *m) {
 
         dns_scope_free(m->unicast_scope);
 
-        while (m->dns_servers)
-                dns_server_free(m->dns_servers);
-        while (m->fallback_dns_servers)
-                dns_server_free(m->fallback_dns_servers);
+        manager_flush_dns_servers(m, DNS_SERVER_SYSTEM);
+        manager_flush_dns_servers(m, DNS_SERVER_FALLBACK);
 
         hashmap_free(m->links);
         hashmap_free(m->dns_transactions);
@@ -676,7 +677,7 @@ clear:
         return r;
 }
 
-static void write_resolve_conf_server(DnsServer *s, FILE *f, unsigned *count) {
+static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
         _cleanup_free_ char *t  = NULL;
         int r;
 
@@ -691,16 +692,39 @@ static void write_resolve_conf_server(DnsServer *s, FILE *f, unsigned *count) {
         }
 
         if (*count == MAXNS)
-                fputs("# Too many DNS servers configured, the following entries may be ignored\n", f);
+                fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
 
         fprintf(f, "nameserver %s\n", t);
         (*count) ++;
 }
 
+static void write_resolv_conf_search(const char *domain, FILE *f,
+                                     unsigned *length, unsigned *count) {
+        assert(domain);
+        assert(f);
+        assert(length);
+
+        if (*count >= MAXDNSRCH ||
+            *length + strlen(domain) > 256) {
+                if (*count == MAXDNSRCH)
+                        fputs(" # Too many search domains configured, remaining ones ignored.", f);
+                if (*length <= 256)
+                        fputs(" # Total length of all search domains is too long, remaining ones ignored.", f);
+
+                return;
+        }
+
+        fprintf(f, " %s", domain);
+
+        (*length) += strlen(domain);
+        (*count) ++;
+}
+
 int manager_write_resolv_conf(Manager *m) {
         static const char path[] = "/run/systemd/resolve/resolv.conf";
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_set_free_ Set *dns = NULL, *domains = NULL;
         unsigned count = 0;
         DnsServer *s;
         Iterator i;
@@ -712,6 +736,59 @@ int manager_write_resolv_conf(Manager *m) {
         /* Read the system /etc/resolv.conf first */
         manager_read_resolv_conf(m);
 
+        /* Add the full list to a set, to filter out duplicates */
+        dns = set_new(dns_server_hash_func, dns_server_compare_func);
+        if (!dns)
+                return -ENOMEM;
+
+        domains = set_new(dns_name_hash_func, dns_name_compare_func);
+        if (!domains)
+                return -ENOMEM;
+
+        /* First add the system-wide servers */
+        LIST_FOREACH(servers, s, m->dns_servers) {
+                r = set_put(dns, s);
+                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 = set_put(dns, s);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!l->unicast_scope)
+                        continue;
+
+                STRV_FOREACH(domain, l->unicast_scope->domains) {
+                        r = set_put(domains, *domain);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        /* If we found nothing, add the fallback servers */
+        if (set_isempty(dns)) {
+                LIST_FOREACH(servers, s, m->fallback_dns_servers) {
+                        r = set_put(dns, s);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+                }
+        }
+
         r = fopen_temporary(path, &f, &temp_path);
         if (r < 0)
                 return r;
@@ -724,16 +801,23 @@ int manager_write_resolv_conf(Manager *m) {
               "# resolv.conf(5) in a different way, replace the symlink by a\n"
               "# static file or a different symlink.\n\n", f);
 
-        LIST_FOREACH(servers, s, m->dns_servers)
-                write_resolve_conf_server(s, f, &count);
+        if (set_isempty(dns))
+                fputs("# No DNS servers known.\n", f);
+        else {
+                SET_FOREACH(s, dns, i)
+                        write_resolv_conf_server(s, f, &count);
+        }
+
+        if (!set_isempty(domains)) {
+                unsigned length = 0;
+                char *domain;
 
-        HASHMAP_FOREACH(l, m->links, i)
-                LIST_FOREACH(servers, s, l->dns_servers)
-                        write_resolve_conf_server(s, f, &count);
+                count = 0;
 
-        if (count == 0) {
-                LIST_FOREACH(servers, s, m->fallback_dns_servers)
-                        write_resolve_conf_server(s, f, &count);
+                fputs("search", f);
+                SET_FOREACH(domain, domains, i)
+                        write_resolv_conf_search(domain, f, &count, &length);
+                fputs("\n", f);
         }
 
         r = fflush_and_check(f);
@@ -1646,7 +1730,7 @@ void manager_refresh_rrs(Manager *m) {
 
 int manager_next_hostname(Manager *m) {
         const char *p;
-        uint64_t u;
+        uint64_t u, a;
         char *h;
 
         assert(m);
@@ -1664,7 +1748,15 @@ int manager_next_hostname(Manager *m) {
         if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0)
                 u = 1;
 
-        u++;
+        /* Add a random number to the old value. This way we can avoid
+         * that two hosts pick the same hostname, win on IPv4 and lose
+         * on IPv6 (or vice versa), and pick the same hostname
+         * replacement hostname, ad infinitum. We still want the
+         * numbers to go up monotonically, hence we just add a random
+         * value 1..10 */
+
+        random_bytes(&a, sizeof(a));
+        u += 1 + a % 10;
 
         if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
                 return -ENOMEM;
@@ -1732,6 +1824,18 @@ void manager_verify_all(Manager *m) {
                 dns_zone_verify_all(&s->zone);
 }
 
+void manager_flush_dns_servers(Manager *m, DnsServerType t) {
+        assert(m);
+
+        if (t == DNS_SERVER_SYSTEM)
+                while (m->dns_servers)
+                        dns_server_free(m->dns_servers);
+
+        if (t == DNS_SERVER_FALLBACK)
+                while (m->fallback_dns_servers)
+                        dns_server_free(m->fallback_dns_servers);
+}
+
 static const char* const support_table[_SUPPORT_MAX] = {
         [SUPPORT_NO] = "no",
         [SUPPORT_YES] = "yes",