chiark / gitweb /
resolved: skip IPv6 LLMNR if IPv6 is not available
[elogind.git] / src / resolve / resolved-manager.c
index a93f4a597f76734e5f82c60fc8b52b21f9009cb4..84dad92c53eb81cbf99853f130b761ae9cf45c03 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);
@@ -554,6 +555,7 @@ Manager *manager_free(Manager *m) {
 
         manager_llmnr_stop(m);
 
+        sd_bus_slot_unref(m->prepare_for_sleep_slot);
         sd_event_source_unref(m->bus_retry_event_source);
         sd_bus_unref(m->bus);
 
@@ -690,7 +692,7 @@ 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) ++;
@@ -700,6 +702,7 @@ 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;
         unsigned count = 0;
         DnsServer *s;
         Iterator i;
@@ -711,6 +714,41 @@ 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;
+
+        /* 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 */
+        HASHMAP_FOREACH(l, m->links, i)
+                LIST_FOREACH(servers, s, l->dns_servers) {
+                        r = set_put(dns, s);
+                        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;
@@ -723,15 +761,10 @@ 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);
-
-        HASHMAP_FOREACH(l, m->links, i)
-                LIST_FOREACH(servers, s, l->dns_servers)
-                        write_resolve_conf_server(s, f, &count);
-
-        if (count == 0) {
-                LIST_FOREACH(servers, s, m->fallback_dns_servers)
+        if (set_isempty(dns))
+                fputs("# No DNS servers known.\n", f);
+        else {
+                SET_FOREACH(s, dns, i)
                         write_resolve_conf_server(s, f, &count);
         }
 
@@ -1645,7 +1678,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);
@@ -1663,7 +1696,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;
@@ -1722,6 +1763,27 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
         return NULL;
 }
 
+void manager_verify_all(Manager *m) {
+        DnsScope *s;
+
+        assert(m);
+
+        LIST_FOREACH(scopes, s, m->dns_scopes)
+                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",