X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-resolv-conf.c;h=b182f4319ed99d6e2369e2c99a5c0313b38e9282;hb=53e1b683907c2f12330f00feb9630150196f064d;hp=956f380f3c8499de4ecc6a17f29149185bb20915;hpb=827661914afae10c2a751ca680296a9a849e4f4a;p=thirdparty%2Fsystemd.git diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 956f380f3c8..b182f4319ed 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -1,5 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - +/* SPDX-License-Identifier: LGPL-2.1+ */ /*** This file is part of systemd. @@ -28,6 +27,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 +62,13 @@ 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_UPLINK_RESOLV_CONF, &own) >= 0 && + st.st_dev == own.st_dev && + st.st_ino == own.st_ino) + return 0; + + /* Is it symlinked to our own stub file? */ + if (stat(PRIVATE_STUB_RESOLV_CONF, &own) >= 0 && st.st_dev == own.st_dev && st.st_ino == own.st_ino) return 0; @@ -89,12 +95,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 +144,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: @@ -147,64 +158,75 @@ clear: } static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { - _cleanup_free_ char *t = NULL; - int r; - assert(s); assert(f); assert(count); - r = in_addr_to_string(s->family, &s->address, &t); - if (r < 0) { - log_warning_errno(r, "Invalid DNS address. Ignoring: %m"); + 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", t); + 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) { +static int write_uplink_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; @@ -213,61 +235,85 @@ 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; + if (!ordered_set_isempty(domains)) + write_resolv_conf_search(domains, f); - fputs("search", f); - ORDERED_SET_FOREACH(domain, domains, i) - write_resolv_conf_search(domain, f, &count, &length); - fputs("\n", f); - } + return fflush_and_check(f); +} + +static int write_stub_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { + fputs("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n" + "# 127.0.0.53 is the systemd-resolved stub resolver.\n" + "# run \"systemd-resolve --status\" to see details about the actual nameservers.\n\n" + "nameserver 127.0.0.53\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; + _cleanup_free_ char *temp_path_uplink = NULL; + _cleanup_free_ char *temp_path_stub = NULL; + _cleanup_fclose_ FILE *f_uplink = NULL; + _cleanup_fclose_ FILE *f_stub = NULL; int r; 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); + r = fopen_temporary_label(PRIVATE_UPLINK_RESOLV_CONF, PRIVATE_UPLINK_RESOLV_CONF, &f_uplink, &temp_path_uplink); + if (r < 0) + return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m"); + r = fopen_temporary_label(PRIVATE_STUB_RESOLV_CONF, PRIVATE_STUB_RESOLV_CONF, &f_stub, &temp_path_stub); if (r < 0) - return r; + return log_warning_errno(r, "Failed to open private stub-resolv.conf file for writing: %m"); + (void) fchmod(fileno(f_uplink), 0644); + (void) fchmod(fileno(f_stub), 0644); - fchmod(fileno(f), 0644); + r = write_uplink_resolv_conf_contents(f_uplink, dns, domains); + if (r < 0) { + log_error_errno(r, "Failed to write private resolv.conf contents: %m"); + goto fail; + } - r = write_resolv_conf_contents(f, dns, domains); - if (r < 0) + if (rename(temp_path_uplink, PRIVATE_UPLINK_RESOLV_CONF) < 0) { + r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m"); + goto fail; + } + + r = write_stub_resolv_conf_contents(f_stub, dns, domains); + if (r < 0) { + log_error_errno(r, "Failed to write private stub-resolv.conf contents: %m"); goto fail; + } - if (rename(temp_path, PRIVATE_RESOLV_CONF) < 0) { - r = -errno; + if (rename(temp_path_stub, PRIVATE_STUB_RESOLV_CONF) < 0) { + r = log_error_errno(errno, "Failed to move private stub-resolv.conf file into place: %m"); goto fail; } return 0; fail: - (void) unlink(PRIVATE_RESOLV_CONF); - (void) unlink(temp_path); + (void) unlink(PRIVATE_UPLINK_RESOLV_CONF); + (void) unlink(temp_path_uplink); + (void) unlink(PRIVATE_STUB_RESOLV_CONF); + (void) unlink(temp_path_stub); + return r; }