]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/resolve/resolved-resolv-conf.c
resolved: automatically forget all learnt DNS server information when the network...
[thirdparty/systemd.git] / src / resolve / resolved-resolv-conf.c
index 7567f4c369e3f478cfa59d1acc632f641f8531e4..e3d6a3340934a7ce63e4f5b48241f00477d2e2fc 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
@@ -28,6 +26,7 @@
 #include "fileio.h"
 #include "ordered-set.h"
 #include "resolved-conf.h"
+#include "resolved-dns-server.h"
 #include "resolved-resolv-conf.h"
 #include "string-util.h"
 #include "strv.h"
@@ -62,7 +61,7 @@ int manager_read_resolv_conf(Manager *m) {
                 return 0;
 
         /* Is it symlinked to our own file? */
-        if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 &&
+        if (stat(PRIVATE_RESOLV_CONF, &own) >= 0 &&
             st.st_dev == own.st_dev &&
             st.st_ino == own.st_ino)
                 return 0;
@@ -89,12 +88,12 @@ int manager_read_resolv_conf(Manager *m) {
                 char *l;
 
                 l = strstrip(line);
-                if (*l == '#' || *l == ';')
+                if (IN_SET(*l, '#', ';'))
                         continue;
 
                 a = first_word(l, "nameserver");
                 if (a) {
-                        r = manager_add_dns_server_by_string(m, DNS_SERVER_SYSTEM, a);
+                        r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a);
                         if (r < 0)
                                 log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
 
@@ -138,6 +137,11 @@ int manager_read_resolv_conf(Manager *m) {
         if (m->unicast_scope)
                 dns_cache_flush(&m->unicast_scope->cache);
 
+        /* If /etc/resolv.conf changed, make sure to forget everything we learned about the DNS servers. After all we
+         * might now talk to a very different DNS server that just happens to have the same IP address as an old one
+         * (think 192.168.1.1). */
+        dns_server_reset_features_all(m->dns_servers);
+
         return 0;
 
 clear:
@@ -151,58 +155,71 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
         assert(f);
         assert(count);
 
-        (void) dns_server_string(s);
-
-        if (!s->server_string) {
+        if (!dns_server_string(s)) {
                 log_warning("Our of memory, or invalid DNS address. Ignoring server.");
                 return;
         }
 
+        /* Check if the DNS server is limited to particular domains;
+         * resolv.conf does not have a syntax to express that, so it must not
+         * appear as a global name server to avoid routing unrelated domains to
+         * it (which is a privacy violation, will most probably fail anyway,
+         * and adds unnecessary load) */
+        if (dns_server_limited_domains(s)) {
+                log_debug("DNS server %s has route-only domains, not using as global name server", dns_server_string(s));
+                return;
+        }
+
         if (*count == MAXNS)
-                fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f);
-        (*count) ++;
+                fputs_unlocked("# Too many DNS servers configured, the following entries may be ignored.\n", f);
+        (*count)++;
 
-        fprintf(f, "nameserver %s\n", s->server_string);
+        fprintf(f, "nameserver %s\n", dns_server_string(s));
 }
 
 static void write_resolv_conf_search(
-                const char *domain,
-                FILE *f,
-                unsigned *count,
-                unsigned *length) {
+                OrderedSet *domains,
+                FILE *f) {
+        unsigned length = 0, count = 0;
+        Iterator i;
+        char *domain;
 
-        assert(domain);
+        assert(domains);
         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);
+        fputs_unlocked("search", f);
 
-                return;
+        ORDERED_SET_FOREACH(domain, domains, i) {
+                if (++count > MAXDNSRCH) {
+                        fputs_unlocked("\n# Too many search domains configured, remaining ones ignored.", f);
+                        break;
+                }
+                length += strlen(domain) + 1;
+                if (length > 256) {
+                        fputs_unlocked("\n# Total length of all search domains is too long, remaining ones ignored.", f);
+                        break;
+                }
+                fputc_unlocked(' ', f);
+                fputs_unlocked(domain, f);
         }
 
-        (*length) += strlen(domain);
-        (*count) ++;
-
-        fputc(' ', f);
-        fputs(domain, f);
+        fputs_unlocked("\n", f);
 }
 
 static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) {
         Iterator i;
 
-        fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
-              "# Third party programs must not access this file directly, but\n"
-              "# only through the symlink at /etc/resolv.conf. To manage\n"
-              "# resolv.conf(5) in a different way, replace the symlink by a\n"
-              "# static file or a different symlink.\n\n", f);
+        fputs_unlocked("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n"
+                       "# This is a dynamic resolv.conf file for connecting local clients directly to\n"
+                       "# all known DNS servers.\n#\n"
+                       "# Third party programs must not access this file directly, but only through the\n"
+                       "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n"
+                       "# replace this symlink by a static file or a different symlink.\n#\n"
+                       "# See man:systemd-resolved.service(8) for details about the supported modes of\n"
+                       "# operation for /etc/resolv.conf.\n\n", f);
 
         if (ordered_set_isempty(dns))
-                fputs("# No DNS servers known.\n", f);
+                fputs_unlocked("# No DNS servers known.\n", f);
         else {
                 unsigned count = 0;
                 DnsServer *s;
@@ -211,23 +228,14 @@ static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *doma
                         write_resolv_conf_server(s, f, &count);
         }
 
-        if (!ordered_set_isempty(domains)) {
-                unsigned length = 0, count = 0;
-                char *domain;
-
-                fputs("search", f);
-                ORDERED_SET_FOREACH(domain, domains, i)
-                        write_resolv_conf_search(domain, f, &count, &length);
-                fputs("\n", f);
-        }
+        if (!ordered_set_isempty(domains))
+                write_resolv_conf_search(domains, f);
 
         return fflush_and_check(f);
 }
 
 int manager_write_resolv_conf(Manager *m) {
 
-        #define PRIVATE_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
-
         _cleanup_ordered_set_free_ OrderedSet *dns = NULL, *domains = NULL;
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -236,29 +244,31 @@ int manager_write_resolv_conf(Manager *m) {
         assert(m);
 
         /* Read the system /etc/resolv.conf first */
-        manager_read_resolv_conf(m);
+        (void) manager_read_resolv_conf(m);
 
         /* Add the full list to a set, to filter out duplicates */
         r = manager_compile_dns_servers(m, &dns);
         if (r < 0)
-                return r;
+                return log_warning_errno(r, "Failed to compile list of DNS servers: %m");
 
-        r = manager_compile_search_domains(m, &domains);
+        r = manager_compile_search_domains(m, &domains, false);
         if (r < 0)
-                return r;
+                return log_warning_errno(r, "Failed to compile list of search domains: %m");
 
         r = fopen_temporary_label(PRIVATE_RESOLV_CONF, PRIVATE_RESOLV_CONF, &f, &temp_path);
         if (r < 0)
-                return r;
+                return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m");
 
-        fchmod(fileno(f), 0644);
+        (void) fchmod(fileno(f), 0644);
 
         r = write_resolv_conf_contents(f, dns, domains);
-        if (r < 0)
+        if (r < 0) {
+                log_error_errno(r, "Failed to write private resolv.conf contents: %m");
                 goto fail;
+        }
 
         if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) {
-                r = -errno;
+                r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m");
                 goto fail;
         }
 
@@ -267,5 +277,6 @@ int manager_write_resolv_conf(Manager *m) {
 fail:
         (void) unlink(PRIVATE_RESOLV_CONF);
         (void) unlink(temp_path);
+
         return r;
 }