summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-transaction.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2020-11-16 23:26:37 +0100
committerLennart Poettering <lennart@poettering.net>2021-02-18 15:55:58 +0100
commitacbf761b5d22129d6eebffc6747d18414168bda0 (patch)
treeb6367482eeb7c52e1d6a1b81c3658027157b4c86 /src/resolve/resolved-dns-transaction.c
parentd79677ab441152639a52aef56164930fa4490633 (diff)
downloadsystemd-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.c29
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. */