How many TXT data before DNS queries don't fit in a UDP packet?

I have a DNS domain with 3 TXT records:

$ORIGIN example.com.
@   IN TXT "thing one veryveryveryveryveryverylong"
@   IN TXT "thing two veryveryveryveryveryverylong"
@   IN TXT "thing three veryveryveryveryveryverylong"

When I do a DNS query (dig example.com. txt) the reply fits in a UDP packet because the payload is less than 512 bytes (resulting in a packet less than 576 bytes).

However I know that if the reply is long enough, it will be truncated and the DNS client will have to repeat the request using TCP, which has longer length limits.

How can I calculate whether or not I have exceeded the length limit without generating the DNS records and doing a query?

I assume that the formula is something like:

N: the number of TXT records on that label.
P: the number of bytes in all the TXT records.
S: the total number of text segments (TXT records can have multiple text segments per record)

UDP is required if N*a + P*b + S*c is more than 512

What are the values of a, b, and c?

(or am I going in the wrong direction?)


Solution 1:

In practice, you should not be trying to pre-calculate the exact response size of your TXT records prior to implementing them. There are many variables in play, some of which you do not control. Most admins generalize based off the size of an existing TXT record response that they observe from their authoritative server and call it a day. Since the focus of your question is how to avoid generalizing, this answer is going to focus on why it is difficult to use precise calculations.

(This answer should not be taken as a statement for or against trying to stay inside of 512 bytes, it is a commentary on the approach used to do so.)

sDNSHeader + sQuestion + sAnswer + sAuthority + sAdditional
  • The query itself shows up in the question section of the reply packet, and must be taken into consideration.
  • As you surmised, the number of answers and their byte length must also be taken into consideration.
  • If the authoritative server is configured to list the nameservers in the authority section and the corresponding address records in the additional section, those have to be factored in as well.
  • If the request contained an EDNS0 pseudo-section inside the additional section, a compliant server will reply with its own pseudo-section. The query probably requested a message size of greater than 512 bytes if EDNS0 is in play, but it's still a consideration, particularly since network hardware in the communication path may incorrectly reject DNS packets >512 bytes as invalid and force the effective message limit back down to the original constraints.
  • RFC 1035 message compression is also a factor.

Can you write a program or script that will do all of this calculation for you? Sure. Is it a good use of time for the problem you're trying to solve? Probably not. Use an existing TXT record within the zone as your rough sizing guideline, and if growing over 512 bytes is a concern make sure you are familiar with any "include" functionality built into the relevant standard leveraging the TXT records. (SPF, etc.)

Solution 2:

historically you get 512 octets of DNS payload, of which 10 octets is a fixed header. you have to make room for the copy of the question section that will be included in the response. that's roughly the question name plus four octets. each TXT RR will use a compression pointer up to the question name, for its owner name. that's two octets. then 10 octets of fixed overhead (ttl, class, type, and rdlen). you'll fit more text into a single really long TXT RR than into multiple discrete TXT RR's. in general you should try entering a bunch of different TXT RRs into one of your zones and then use "dig" to fetch each one and see how long "dig" says the payload is.

the historical 512 number was chosen for IPv4 but was not relaxed for IPv6. in IPv4, the payload plus the worst-case UDP and IP headers would yield a 568 octet IP datagram, which was the lowest allowed minimum reassembly buffer size -- an IPv4 endstation isn't "compliant" if it can't reassemble at least that much, so, noone is allowed to assume that you can reassemble more.

in modern DNS, the buffer size will be negotiated using an OPT RR ("EDNS0") and will usually be 4096. this reflects the much-larger-than-568-octets size that endstations can actually reassemble. obviously EDNS0 buffer sizes larger than your ethernet MTU (or your path MTU if you discover it) will cause IP fragmentation, and that often leads to loss since firewalls don't allow it. when EDNS0 @4K fails, it will often retry at 1460 (to leave room for IP and UDP headers plus your payload and still fit in an ethernet packet), and then retry at 512, and then perhaps retry without EDNS0 (which is also 512). if you depend on EDNS, you will often end up sending TC=1 ("truncation occurred") in which case the requestor will probably retry using TCP, or just fail outright.

what this means is if you depend on more than 512, you'll be using TCP a lot. since your question was about UDP, that likely means you should fit into 512.