summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve/resolved-dns-server.c')
-rw-r--r--src/resolve/resolved-dns-server.c149
1 files changed, 106 insertions, 43 deletions
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 1600e7c292..3baf63ffa7 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -224,31 +224,48 @@ void dns_server_move_back_and_unmark(DnsServer *s) {
}
}
-void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
+static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
- if (level == DNS_SERVER_FEATURE_LEVEL_LARGE) {
- /* Even if we successfully receive a reply to a
- request announcing support for large packets, that
- does not mean we can necessarily receive large
- packets. */
+ if (s->verified_feature_level > level)
+ return;
- if (s->verified_feature_level < DNS_SERVER_FEATURE_LEVEL_LARGE - 1) {
- s->verified_feature_level = DNS_SERVER_FEATURE_LEVEL_LARGE - 1;
- assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
- }
- } else if (s->verified_feature_level < level) {
+ if (s->verified_feature_level != level) {
+ log_debug("Verified feature level %s.", dns_server_feature_level_to_string(level));
s->verified_feature_level = level;
- assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
}
- if (s->possible_feature_level == level)
- s->n_failed_attempts = 0;
+ assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0);
+}
+
+void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t rtt, size_t size) {
+ assert(s);
+
+ if (protocol == IPPROTO_UDP) {
+ if (s->possible_feature_level == level)
+ s->n_failed_udp = 0;
+
+ if (level == DNS_SERVER_FEATURE_LEVEL_LARGE)
+ /* Even if we successfully receive a reply to a request announcing support for large packets,
+ that does not mean we can necessarily receive large packets. */
+ dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_LARGE - 1);
+ else
+ /* A successful UDP reply, verifies UDP, ENDS0 and DO levels */
+ dns_server_verified(s, level);
+
+ } else if (protocol == IPPROTO_TCP) {
+
+ if (s->possible_feature_level == level)
+ s->n_failed_tcp = 0;
+
+ /* Successful TCP connections are only useful to verify the TCP feature level. */
+ dns_server_verified(s, DNS_SERVER_FEATURE_LEVEL_TCP);
+ }
/* Remember the size of the largest UDP packet we received from a server,
we know that we can always announce support for packets with at least
this size. */
- if (s->received_udp_packet_max < size)
+ if (protocol == IPPROTO_UDP && s->received_udp_packet_max < size)
s->received_udp_packet_max = size;
if (s->max_rtt < rtt) {
@@ -257,12 +274,16 @@ void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_
}
}
-void dns_server_packet_lost(DnsServer *s, DnsServerFeatureLevel level, usec_t usec) {
+void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) {
assert(s);
assert(s->manager);
- if (s->possible_feature_level == level)
- s->n_failed_attempts ++;
+ if (s->possible_feature_level == level) {
+ if (protocol == IPPROTO_UDP)
+ s->n_failed_udp ++;
+ else if (protocol == IPPROTO_TCP)
+ s->n_failed_tcp ++;
+ }
if (s->resend_timeout > usec)
return;
@@ -274,18 +295,24 @@ void dns_server_packet_failed(DnsServer *s, DnsServerFeatureLevel level) {
assert(s);
assert(s->manager);
+ /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. */
+
if (s->possible_feature_level != level)
return;
- /* Invoked whenever we get a FORMERR, SERVFAIL or NOTIMP rcode from a server. This is an immediate trigger for
- * us to go one feature level down. Except when we are already at TCP or UDP level, in which case there's no
- * point in changing, under the assumption that packet failures are caused by packet contents, not by used
- * transport. */
+ s->packet_failed = true;
+}
+
+void dns_server_packet_truncated(DnsServer *s, DnsServerFeatureLevel level) {
+ assert(s);
+ assert(s->manager);
- if (s->possible_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP)
+ /* Invoked whenever we get a packet with TC bit set. */
+
+ if (s->possible_feature_level != level)
return;
- s->n_failed_attempts = (unsigned) -1;
+ s->packet_truncated = true;
}
void dns_server_packet_rrsig_missing(DnsServer *s) {
@@ -326,35 +353,71 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
_cleanup_free_ char *ip = NULL;
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST;
- s->n_failed_attempts = 0;
+ s->n_failed_udp = 0;
+ s->n_failed_tcp = 0;
+ s->packet_failed = false;
+ s->packet_truncated = false;
s->verified_usec = 0;
s->rrsig_missing = false;
in_addr_to_string(s->family, &s->address, &ip);
- log_info("Grace period over, resuming full feature set for DNS server %s", strna(ip));
+ log_info("Grace period over, resuming full feature set (%s) for DNS server %s",
+ dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
+
} else if (s->possible_feature_level <= s->verified_feature_level)
s->possible_feature_level = s->verified_feature_level;
- else if (s->n_failed_attempts >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS) {
- _cleanup_free_ char *ip = NULL;
+ else {
+ DnsServerFeatureLevel p = s->possible_feature_level;
- /* Switch one feature level down. Except when we are at TCP already, in which case we try UDP
- * again. Thus, if a DNS server is not responding we'll keep toggling between UDP and TCP until it
- * responds on one of them. Note that we generally prefer UDP over TCP (which is why it is at a higher
- * feature level), but many DNS servers support lack TCP support. */
+ if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP)
- if (s->possible_feature_level == DNS_SERVER_FEATURE_LEVEL_TCP)
+ /* We are at the TCP (lowest) level, and we tried a couple of TCP connections, and it didn't
+ * work. Upgrade back to UDP again. */
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
- else {
- assert(s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_WORST);
- s->possible_feature_level --;
- }
-
- s->n_failed_attempts = 0;
- s->verified_usec = 0;
- in_addr_to_string(s->family, &s->address, &ip);
- log_warning("Using degraded feature set (%s) for DNS server %s",
- dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
+ else if ((s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) ||
+ (s->packet_failed &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) ||
+ (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS &&
+ s->packet_truncated &&
+ s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP))
+
+ /* Downgrade the feature one level, maybe things will work better then. We do this under any of
+ * three conditions:
+ *
+ * 1. We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If
+ * the packets are lost, maybe the server cannot parse them, hence downgrading sounds like a
+ * good idea. We might downgrade all the way down to TCP this way.
+ *
+ * 2. We got a failure packet, and are at a feature level above UDP. Note that in this case we
+ * downgrade no further than UDP, under the assumption that a failure packet indicates an
+ * incompatible packet contents, but not a problem with the transport.
+ *
+ * 3. We got too many TCP connection failures in a row, we had at least one truncated packet,
+ * and are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or
+ * EDNS0 data we hope to make the packet smaller, so that it still works via UDP given that
+ * TCP appears not to be a fallback. Note that if we are already at the lowest UDP level, we
+ * don't go further down, since that's TCP, and TCP failed too often after all.
+ */
+
+ s->possible_feature_level--;
+
+ if (p != s->possible_feature_level) {
+ _cleanup_free_ char *ip = NULL;
+
+ /* We changed the feature level, reset the counting */
+ s->n_failed_udp = 0;
+ s->n_failed_tcp = 0;
+ s->packet_failed = false;
+ s->packet_truncated = false;
+ s->verified_usec = 0;
+
+ in_addr_to_string(s->family, &s->address, &ip);
+ log_warning("Using degraded feature set (%s) for DNS server %s",
+ dns_server_feature_level_to_string(s->possible_feature_level), strna(ip));
+ }
}
return s->possible_feature_level;