From 44db02d0ef553e1ef96cd8d814853940db4eb20d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 8 Dec 2017 19:48:15 +0100 Subject: [PATCH] resolved: when a server consistently returns SERVFAIL, try another one Currently, we accept SERVFAIL after downgrading fully, cache it and move on. Let's extend this a bit: after downgrading fully, if the SERVFAIL logic continues to be an issue, then use a different DNS server if there are any. Fixes: #7147 --- src/resolve/resolved-dns-scope.c | 20 ++++++++++++++++++++ src/resolve/resolved-dns-scope.h | 1 + src/resolve/resolved-dns-transaction.c | 13 ++++++++++++- src/resolve/resolved-dns-transaction.h | 2 ++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 0a644f42525..0f9c4fa47ab 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -143,6 +143,26 @@ DnsServer *dns_scope_get_dns_server(DnsScope *s) { return manager_get_dns_server(s->manager); } +unsigned dns_scope_get_n_dns_servers(DnsScope *s) { + unsigned n = 0; + DnsServer *i; + + assert(s); + + if (s->protocol != DNS_PROTOCOL_DNS) + return 0; + + if (s->link) + i = s->link->dns_servers; + else + i = s->manager->dns_servers; + + for (; i; i = i->servers_next) + n++; + + return n; +} + void dns_scope_next_dns_server(DnsScope *s) { assert(s); diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index d46ccd884ce..6f58c3c2570 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -95,6 +95,7 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key); DnsServer *dns_scope_get_dns_server(DnsScope *s); +unsigned dns_scope_get_n_dns_servers(DnsScope *s); void dns_scope_next_dns_server(DnsScope *s); int dns_scope_llmnr_membership(DnsScope *s, bool b); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 792a16d6930..e930dd9c93b 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -408,6 +408,8 @@ static int dns_transaction_pick_server(DnsTransaction *t) { dns_server_unref(t->server); t->server = dns_server_ref(server); + t->n_picked_servers ++; + log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id); return 1; @@ -913,12 +915,21 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { /* Request failed, immediately try again with reduced features */ if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP) { + /* This was already at UDP feature level? If so, it doesn't make sense to downgrade - * this transaction anymore, hence let's process the response, and accept the + * this transaction anymore, but let's see if it might make sense to send the request + * to a different DNS server instead. If not let's process the response, and accept the * rcode. Note that we don't retry on TCP, since that's a suitable way to mitigate * packet loss, but is not going to give us better rcodes should we actually have * managed to get them already at UDP level. */ + if (t->n_picked_servers < dns_scope_get_n_dns_servers(t->scope)) { + /* We tried fewer servers on this transaction than we know, let's try another one then */ + dns_transaction_retry(t, true); + return; + } + + /* Give up, accept the rcode */ log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p))); break; } diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 4ae93c78138..31dcd7627a7 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -109,6 +109,8 @@ struct DnsTransaction { sd_event_source *timeout_event_source; unsigned n_attempts; + unsigned n_picked_servers; + /* UDP connection logic, if we need it */ int dns_udp_fd; sd_event_source *dns_udp_event_source; -- 2.39.2