diff options
author | Lennart Poettering <lennart@poettering.net> | 2020-11-16 23:26:37 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2021-02-18 15:55:58 +0100 |
commit | acbf761b5d22129d6eebffc6747d18414168bda0 (patch) | |
tree | b6367482eeb7c52e1d6a1b81c3658027157b4c86 /src/resolve/resolved-dns-transaction.c | |
parent | d79677ab441152639a52aef56164930fa4490633 (diff) | |
download | systemd-acbf761b5d22129d6eebffc6747d18414168bda0.tar.gz |
resolved: let's track fragment sizes of servers/retry on fragmenting
Fragmenting sucks, let's avoid it. Thus let's start tracking the maximum
fragment size we receive.
Also, let's redo a transaction via TCP if we see fragmenting on UDP, as
effective mitigation against DNS fragment attacks.
Diffstat (limited to 'src/resolve/resolved-dns-transaction.c')
-rw-r--r-- | src/resolve/resolved-dns-transaction.c | 29 |
1 files changed, 26 insertions, 3 deletions
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 24f006be5a..260ce76b98 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1031,6 +1031,7 @@ static int dns_transaction_fix_rcode(DnsTransaction *t) { } void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypted) { + bool retry_with_tcp = false; int r; assert(t); @@ -1193,9 +1194,29 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt return; } + /* Response was truncated, let's try again with good old TCP */ log_debug("Reply truncated, retrying via TCP."); + retry_with_tcp = true; - /* Response was truncated, let's try again with good old TCP */ + } else if (t->scope->protocol == DNS_PROTOCOL_DNS && + DNS_PACKET_IS_FRAGMENTED(p)) { + + /* Report the fragment size, so that we downgrade from LARGE to regular EDNS0 if needed */ + if (t->server) + dns_server_packet_udp_fragmented(t->server, dns_packet_size_unfragmented(p)); + + if (t->current_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { + /* Packet was fragmented. Let's retry with TCP to avoid fragmentation attack + * issues. (We don't do that on the lowest feature level however, since crappy DNS + * servers often do not implement TCP, hence falling back to TCP on fragmentation is + * counter-productive there.) */ + + log_debug("Reply fragmented, retrying via TCP."); + retry_with_tcp = true; + } + } + + if (retry_with_tcp) { r = dns_transaction_emit_tcp(t); if (r == -ESRCH) { /* No servers found? Damn! */ @@ -1296,8 +1317,10 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p, bool encrypt 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); + /* Report that we successfully received a packet. We keep track of the largest packet + * size/fragment size we got. Which is useful for announcing the EDNS(0) packet size we can + * receive to our server. */ + dns_server_packet_received(t->server, p->ipproto, t->current_feature_level, dns_packet_size_unfragmented(p)); } /* See if we know things we didn't know before that indicate we better restart the lookup immediately. */ |