Some thoughts on DNS over TCP
The majority of DNS queries out there use UDP for the query. TCP is only
needed in the following cases:
- In the rare cases when a reply, after being compressed, is over 512
bytes in size.
- When a zone transfer is being performed
The first case can be minimized on an authoritative-only DNS server by
looking for queries that result in oversized packets. Such queries can
almost always be reduced to 512 bytes or less by:
- Getting rid of data in the "additional records" section.
- Selecting a subset of the answers in the "answer" section, and only
returning that subset in the answer. DjbDNS, for example, only returns eight
records if a given query has more than eight records as answers
- If, after performing the above two techniques to minimize the size of a
packet, a packet is still too lare to fit in the 512-byte packet, refuse to
serve the packet. The system administrator needs to change such packets so
that they can fit in the 512-byte UDP limit.
The only time a DNS server needs to be accessible to anyone any everyone on
the internet is when it is functioning as an authoritative server. If the
DNS server is a caching name server, or providing zone transfers to slaves,
IP-based access restriction can, and should, be performed.
Since it is possible to make all records returned as an authoritative
DNS server fit in 512 bytes or less (see above), access restrictions can
be placed on all connections on TCP port 53 of a DNS server.
When an unauthorized client connects to port 53 of a DNS server, we can
do any of the following five actions, depending on the level of
security we desire:
- We can not respond to the initial TCP SYN request, acting like
the machine is not present at all. While this is the most
secure option, limitations in the UNIX socket library can not
implement this behavior in the application.
- We can immediatly close a TCP connection which originates from
an unauthorized client. This is the behavior that MaraDNS' zone server
currently has.
- We can send a DNS "query refused" message immediately on connection,
then immediately close the connection. This is a little less confusing
to an unauthorized client, since it makes it clear why they are being
disconnected. However, the spec implies that the client sends a
question before sending a reply.
- We can, after getting a query, send the "query refused" message and
disconnect. This violates the spec, which states "it should wait
until the connection has been idle for a period on the order of
two minutes" before disconnecting. (RFC1035, §4.2.2)
- Finally, we can stay connected, returning a "query refused" after
every request they send