chiark / gitweb /
resolved: filter out duplicate DNS servers when writing resolv.conf
authorLennart Poettering <lennart@poettering.net>
Tue, 12 Aug 2014 10:21:10 +0000 (12:21 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 12 Aug 2014 11:02:27 +0000 (13:02 +0200)
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-manager.c

index 30d9c8b34ed90fcccefc7a3ef6549e4acbb5b23e..043f6b637d8aedca68d5965ea2f68c7d12066ad9 100644 (file)
@@ -19,6 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include "siphash24.h"
+
 #include "resolved-dns-server.h"
 
 int dns_server_new(
 #include "resolved-dns-server.h"
 
 int dns_server_new(
@@ -97,3 +99,24 @@ DnsServer* dns_server_free(DnsServer *s)  {
 
         return NULL;
 }
 
         return NULL;
 }
+
+unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) {
+        const DnsServer *s = p;
+        uint64_t u;
+
+        siphash24((uint8_t*) &u, &s->address, FAMILY_ADDRESS_SIZE(s->family), hash_key);
+        u = u * hash_key[0] + u + s->family;
+
+        return u;
+}
+
+int dns_server_compare_func(const void *a, const void *b) {
+        const DnsServer *x = a, *y = b;
+
+        if (x->family < y->family)
+                return -1;
+        if (x->family > y->family)
+                return 1;
+
+        return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family));
+}
index 8a9f5560d5857906343ed3021e066db059ed2470..5178a6be7e827295f75c56da684eb1f909113088 100644 (file)
@@ -59,3 +59,6 @@ int dns_server_new(
                 const union in_addr_union *address);
 
 DnsServer* dns_server_free(DnsServer *s);
                 const union in_addr_union *address);
 
 DnsServer* dns_server_free(DnsServer *s);
+
+unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]);
+int dns_server_compare_func(const void *a, const void *b);
index 988aa6e3b1a2fec802c848fddb84bef0345c9d9c..6bb089451f1b8d1a09d07f1df4e1abe3eb9fb3ff 100644 (file)
@@ -691,7 +691,7 @@ static void write_resolve_conf_server(DnsServer *s, FILE *f, unsigned *count) {
         }
 
         if (*count == MAXNS)
         }
 
         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) ++;
 
         fprintf(f, "nameserver %s\n", t);
         (*count) ++;
@@ -701,6 +701,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;
         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;
         unsigned count = 0;
         DnsServer *s;
         Iterator i;
@@ -712,6 +713,41 @@ int manager_write_resolv_conf(Manager *m) {
         /* Read the system /etc/resolv.conf first */
         manager_read_resolv_conf(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;
         r = fopen_temporary(path, &f, &temp_path);
         if (r < 0)
                 return r;
@@ -724,15 +760,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);
 
               "# 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);
         }
 
                         write_resolve_conf_server(s, f, &count);
         }