diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index da509a2c96b..6a5a466af66 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -378,6 +378,17 @@ void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level) { s->packet_invalid = true; } +void dns_server_packet_do_off(DnsServer *s, DnsServerFeatureLevel level) { + assert(s); + + /* Invoked whenever the DO flag was not copied from our request to the response. */ + + if (s->possible_feature_level != level) + return; + + s->packet_do_off = true; +} + static bool dns_server_grace_period_expired(DnsServer *s) { usec_t ts; @@ -494,6 +505,17 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { * when we can't use EDNS because the DNS server doesn't support it. */ log_level = LOG_NOTICE; + } else if (s->packet_do_off && + DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(s->possible_feature_level) && + dns_server_get_dnssec_mode(s) != DNSSEC_YES) { + + /* The server didn't copy the DO bit from request to response, thus DNSSEC is not + * correctly implemented, let's downgrade if that's allowed. */ + + log_debug("Detected server didn't copy DO flag from request to response, downgrading feature level..."); + s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level) ? DNS_SERVER_FEATURE_LEVEL_TLS_PLAIN : + DNS_SERVER_FEATURE_LEVEL_EDNS0; + } else if (s->packet_rrsig_missing && DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(s->possible_feature_level) && dns_server_get_dnssec_mode(s) != DNSSEC_YES) { @@ -653,6 +675,9 @@ bool dns_server_dnssec_supported(DnsServer *server) { if (server->packet_rrsig_missing) return false; + if (server->packet_do_off) + return false; + /* DNSSEC servers need to support TCP properly (see RFC5966), if they don't, we assume DNSSEC is borked too */ if (server->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS) return false; @@ -898,6 +923,7 @@ void dns_server_reset_features(DnsServer *s) { s->packet_bad_opt = false; s->packet_rrsig_missing = false; + s->packet_do_off = false; s->features_grace_period_usec = DNS_SERVER_FEATURE_GRACE_PERIOD_MIN_USEC; @@ -959,14 +985,16 @@ void dns_server_dump(DnsServer *s, FILE *f) { "\tSeen truncated packet: %s\n" "\tSeen OPT RR getting lost: %s\n" "\tSeen RRSIG RR missing: %s\n" - "\tSeen invalid packet: %s\n", + "\tSeen invalid packet: %s\n" + "\tServer dropped DO flag: %s\n", s->received_udp_packet_max, s->n_failed_udp, s->n_failed_tcp, yes_no(s->packet_truncated), yes_no(s->packet_bad_opt), yes_no(s->packet_rrsig_missing), - yes_no(s->packet_invalid)); + yes_no(s->packet_invalid), + yes_no(s->packet_do_off)); } void dns_server_unref_stream(DnsServer *s) { diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 7860e32183a..304d608f7a3 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -85,6 +85,7 @@ struct DnsServer { bool packet_bad_opt:1; /* Set when OPT was missing or otherwise bad on reply */ bool packet_rrsig_missing:1; /* Set when RRSIG was missing */ bool packet_invalid:1; /* Set when we failed to parse a reply */ + bool packet_do_off:1; /* Set when the server didn't copy DNSSEC DO flag from request to response */ usec_t verified_usec; usec_t features_grace_period_usec; @@ -124,6 +125,7 @@ void dns_server_packet_rrsig_missing(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_bad_opt(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level); void dns_server_packet_invalid(DnsServer *s, DnsServerFeatureLevel level); +void dns_server_packet_do_off(DnsServer *s, DnsServerFeatureLevel level); DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 31ef8181382..060a9d80a89 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1279,6 +1279,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt if (!p->opt) dns_server_packet_bad_opt(t->server, t->current_feature_level); + /* Report that the server didn't copy our query DO bit from request to response */ + if (DNS_PACKET_DO(t->sent) && !DNS_PACKET_DO(t->received)) + dns_server_packet_do_off(t->server, t->current_feature_level); + /* Report that we successfully received a packet */ dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, p->size); }