0

How do I configure linux in general to allow dns over tcp?

We discovered today that several different linux servers we use are not able to resolve DNS names with many ip addresses in the response. These response packets would be over 512 bytes, so the server sends back a UDP packet with the truncated bit set. The various processes on the server (in this case, Java, and the nslookup command) simply stop resolving the name at this point. They don't follow up by making the same request over TCP, is I hear they should.

This part is specific to alpinelinux: On alpine linux, I can enable dns lookups over TCP by installing the bind-utils package. that makes other programs on the system start properly handling truncated responses by switching to TCP. I don't know what that particular package is doing to configure this, and I so i'm left with this quesiton:

Bonus question, can this be enabled for just java without changing anything else?

We are seeing this on more than just alpine, though we stumbled upon a package that has this effect on alpine.

Arthur Ulfeldt
  • 3,219
  • 9
  • 31
  • 40
  • 2
    "How do I configure linux in general to allow dns over tcp?" Why do you think you need to do anything? Except if you have a dumb firewall on host or in the network that (falsely) believes (and enforces) that DNS is over UDP only, other than that all DNS software handles UDP and TCP normally correctly. To debug further you would need to do proper `dig` queries with traces of the network exchanges to see if TCP packets are just dropped. – Patrick Mevzek Nov 09 '20 at 18:29
  • I think I need to do something because I have a fleet of docker containers running alpinelinux that are stopping the DNS lookup when they get a truncated packet, and the OS is not establishing the DNS over TCP connection to continue name resolution. I need to configure these servers to do that. Not a network problem. – Arthur Ulfeldt Nov 09 '20 at 23:27
  • As it stands, your situation seems unclear/lacking details. What is "that" in "that are stopping the DNS lookup"? If any program is doing DNS queries by itself, it has to handle the possible UDP TC flag bag and switch to TCP as well as handle timeouts and a lot of other things. If you are using standard `getaddrinfo` or similar, this is of course taken on your behalf automatically. It may also depend on where is the recursive DNS server you request. See https://linux.die.net/man/3/resolver to see what happens in the OS and how RES_IGNTC is not implemented because you have automated fallback. – Patrick Mevzek Nov 10 '20 at 00:05
  • "If you are using standard getaddrinfo or similar, this is of course taken on your behalf automatically." Yes this is using normal DNS resolution by getaddrinfo. when i use tcpdump I see only one inbound and one outbound UDP packet. on systems without this problem, i see one inbound and one outbound UDB packet, followed by a TCP handshake and a TCP dns query and response packet. – Arthur Ulfeldt Nov 10 '20 at 18:00
  • 1
    Could you please paste the output of `dig +noanswer +noedns toomany.ddstreet.org`. The first line should be something like `;; Truncated, retrying in TCP mode.` – Jesús Ángel Nov 11 '20 at 09:17
  • installing *bind-utils* might be circumvent an otherwise configured stub-resolver (are you running a buggy *systemd-resolved* on the host and that is what breaks your containers?) – anx Nov 11 '20 at 12:07
  • Do you have firewalls in the way? Sounds like these are blocking TCP port 53 (outgoing). Perhaps a braindead container-implementation? – Nils Nov 12 '20 at 22:35

2 Answers2

2

As stated in https://www.rfc-editor.org/rfc/rfc5966:

In the absence of EDNS0 (Extension Mechanisms for DNS 0) (see below), the standard
behaviour of any DNS server needing to send a UDP response that would exceed the 
512-byte limit is for the server to truncate the answer so that it fits within that
limit and then set the TC flag in the response header. When the client receives such
a response, it takes the TC flag as an indication that it should retry over TCP 
instead.

This other RFC https://www.ietf.org/rfc/rfc2181.txt also has some clarifications which include the use of the TC (truncated) header bit:

Where TC is set, the partial RRSet that would not completely fit may be left in the
response.  When a DNS client receives a reply with TC set, it should ignore that 
response, and query again, using a mechanism, such as a TCP connection, that will 
permit larger replies.

The client does not know in advance that the response will be too large, so it will query the server via UDP, except for zone transfer queries which always use TCP. The server will respond via UDP and will include as much as possible and set the truncated header bit. The client must then resend the request via TCP and get the full response. So, you don't have to configure anything to force clients to always use TCP to send DNS request because they just do that on receiving an answer with the TC bit set.

You just have to make sure there is not any firewall which is blocking TCP traffic on port 53. You may also check that you have not been bitten by some bug.

Jesús Ángel
  • 518
  • 1
  • 6
1

You might have solved this already by now, but I've been running into the same situation recently with Alpine Linux and a range of Ruby applications.

The issue here is that musl libc doesn't implement DNS over TCP (forgive me the layman terms here). By installing packages like bind-tools you'll get binaries that are shipping their own/a different DNS resolver, so those are able to talk DNS over TCP.

We've solved this by using a custom resolver that supports TCP within our Ruby applications (something that's encouraged by Rich Felker, the maintainer of musl libc). In fact, it ships with Ruby's standard library, so it's not really a big change.

Most environments have their own solver implemented, so you should be able to find one for whatever language/environment you're using.

Here are some resources: