Working with colleagues on a detection ultimately caused by a Zeek bug, we found there’s a lot of discussion online about how “DNS uses TCP for Zone Transfers”. While this is true, it’s not the complete truth! To be fair, it used to be true in practice, because DNS responses used to be a lot smaller.
What actually happens is, the client sends the standard “Hey, gimme the A record for dnstcp.deloril.com
”. If the response to the request is larger than 512 bytes, the DNS Server will send back only your query (in this example, dnstcp.deloril.com
), and set the “truncated” flag to true in the response packet. (this is because UDP responses are limited to 512 bytes for practical reasons)
When the client receives the response, it sees the truncated flag set, and goes “oh damn, I need to request this record over a mechanism that can receive large responses reliably.” TCP to the rescue! The client will then request the same record, but it will do it over TCP instead of UDP.
For a long time, this was just zone transfers, but nowadays (with DKIM and resource records and DNSSEC) large DNS responses are becoming a lot more common. Don’t believe me? Try it yourself! Spin up wireshark, and make a TXT record request for dnstcp.thisinsecureworld.com
. You’ll see a UDP request, and the response will have the truncate flag set, then a TCP request will be made for the same resource. Your DNS client might even tell you it received a large response and it’s automagically retrying over TCP.
For anyone looking for the gory details, check out the RFC.