From: Lennart Poettering Date: Tue, 10 Nov 2020 13:48:13 +0000 (+0100) Subject: resolved: gracefully handle with packets with too large RR count X-Git-Tag: v248-rc1~133^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=18674159ebbf622a9e6e5a45cc36b38f74dae315;p=thirdparty%2Fsystemd.git resolved: gracefully handle with packets with too large RR count Apparently, there are plenty routers in place that report an incorrect RR count in the packets: they declare more RRs than are actually included. Let's accept these responses, but let's downgrade them to baseline, i.e. let's suppress OPT in this case: if they don't even get the RR count right, let's operate on the absolute baseline, and not bother with anything fancier such as EDNS. Prompted-by: https://github.com/systemd/systemd/issues/12841#issuecomment-724063973 Fixes: #3980 Most likely fixes: #12841 --- diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 59be55a0eb0..953d5fa337d 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -2253,6 +2253,18 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) { bool cache_flush = false; size_t start; + if (p->rindex == p->size) { + /* If we reached the end of the packet already, but there are still more RRs + * declared, then that's a corrupt packet. Let's accept the packet anyway, since it's + * apparently a common bug in routers. Let's however suppress OPT support in this + * case, so that we force the rest of the logic into lowest DNS baseline support. Or + * to say this differently: if the DNS server doesn't even get the RR counts right, + * it's highly unlikely it gets EDNS right. */ + log_debug("More resource records declared in packet than included, suppressing OPT."); + bad_opt = true; + break; + } + r = dns_packet_read_rr(p, &rr, &cache_flush, &start); if (r < 0) return r; @@ -2352,8 +2364,10 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) { previous = dns_resource_record_ref(rr); } - if (bad_opt) + if (bad_opt) { p->opt = dns_resource_record_unref(p->opt); + p->opt_start = p->opt_size = SIZE_MAX; + } *ret_answer = TAKE_PTR(answer);