diff --git a/TODO b/TODO index 71951d543e..a11ecdb277 100644 --- a/TODO +++ b/TODO @@ -183,16 +183,12 @@ Features: we always process them before we process client requests - DNSSEC - add display of private key types (http://tools.ietf.org/html/rfc4034#appendix-A.1.1)? - - DNS - - search paths - mDNS/DNS-SD - mDNS RR resolving - service registration - service/domain/types browsing - avahi compat - DNS-SD service registration from socket units - - edns0 - - cname on PTR (?) - resolved should optionally register additional per-interface LLMNR names, so that for the container case we can establish the same name (maybe "host") for referencing the server, everywhere. diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index f4e24121e7..1f61b68efd 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -44,7 +44,7 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { assert(u); if (family == AF_INET) - return (be32toh(u->in.s_addr) & 0xFFFF0000) == (169U << 24 | 254U << 16); + return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); if (family == AF_INET6) return IN6_IS_ADDR_LINKLOCAL(&u->in6); @@ -52,6 +52,19 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* All of 127.x.x.x is localhost. */ + return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; + + if (family == AF_INET) + return IN6_IS_ADDR_LOOPBACK(&u->in6); + + return -EAFNOSUPPORT; +} + int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { assert(a); assert(b); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 51af08868c..58f55b3418 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -33,6 +33,7 @@ union in_addr_union { int in_addr_is_null(int family, const union in_addr_union *u); int in_addr_is_link_local(int family, const union in_addr_union *u); +int in_addr_is_localhost(int family, const union in_addr_union *u); int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 48df5dfc53..90b5a7c8bd 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -177,6 +177,14 @@ void dns_packet_rewind(DnsPacket *p, size_t idx); int dns_packet_skip_question(DnsPacket *p); int dns_packet_extract(DnsPacket *p); +static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) { + /* Never cache data originating from localhost, under the + * assumption, that it's coming from a locally DNS forwarder + * or server, that is caching on its own. */ + + return in_addr_is_localhost(p->family, &p->sender) == 0; +} + enum { DNS_RCODE_SUCCESS = 0, DNS_RCODE_FORMERR = 1, diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index c65c9c9f49..8c4f23a4da 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -478,7 +478,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); + if (DNS_PACKET_SHALL_CACHE(p)) + dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 744969b745..956f380f3c 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -50,9 +50,9 @@ int manager_read_resolv_conf(Manager *m) { r = stat("/etc/resolv.conf", &st); if (r < 0) { if (errno == ENOENT) - r = 0; - else - r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); + return 0; + + r = log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m"); goto clear; } @@ -61,22 +61,18 @@ int manager_read_resolv_conf(Manager *m) { if (t == m->resolv_conf_mtime) return 0; - m->resolv_conf_mtime = t; - /* Is it symlinked to our own file? */ if (stat("/run/systemd/resolve/resolv.conf", &own) >= 0 && st.st_dev == own.st_dev && - st.st_ino == own.st_ino) { - r = 0; - goto clear; - } + st.st_ino == own.st_ino) + return 0; f = fopen("/etc/resolv.conf", "re"); if (!f) { if (errno == ENOENT) - r = 0; - else - r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); + return 0; + + r = log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); goto clear; } @@ -115,6 +111,8 @@ int manager_read_resolv_conf(Manager *m) { } } + m->resolv_conf_mtime = t; + /* Flush out all servers and search domains that are still * marked. Those are then ones that didn't appear in the new * /etc/resolv.conf */ @@ -131,6 +129,15 @@ int manager_read_resolv_conf(Manager *m) { * resolve VPN domains. */ manager_set_dns_server(m, m->dns_servers); + /* Unconditionally flush the cache when /etc/resolv.conf is + * modified, even if the data it contained was completely + * identical to the previous version we used. We do this + * because altering /etc/resolv.conf is typically done when + * the network configuration changes, and that should be + * enough to flush the global unicast DNS cache. */ + if (m->unicast_scope) + dns_cache_flush(&m->unicast_scope->cache); + return 0; clear: