net: pure Go dns does not handle UDP packets > 512 octets #13561
I have found a bug in pure Go resolver that causes Dial to fail even though correct responses are returned from the server. This problem manifested itself as a failure when trying to search for docker images in docker 1.9.1 built on go1.4.3:
I have tested go1.3.0 and it did not exhibit this problem, but at least 1.4.3, 1.5.1 and 1.5.2 are affected.
Tested using the following code:
Note - we only run an IPv4 stack. IPv6 is disabled on the client
Test fails with pure Go resolver
DNS packet capture: PureGO_correct_but_fails.txt
Here we see the AAAA response contains only CNAME RRs but no AAAA RRs. The resolver queries all 4 name servers and all 4 responses contain CNAME and A RRs which the resolver ignores. It then searches using the search domain (from
Test succeeds when forced to use cGo resolver
(By setting LOCALDOMAIN in the environment, as described in the docs)
DNS packet capture: CGO_correct_but_succeeds.txt
Here we see the same AAAA and A RRs are returned but this time the resolver accepts the A records and the dial call succeeds.
The name server returns correct responses for both A and AAAA queries but the pure Go resolver ignores the A response records when the AAAA response contains no RRs.
I would have also expected that since IPv6 is disabled, querying for AAAA records is redundant, but the C library does it too. That may be because many hosts will have both stacks enabled but only have IPv4 routing configured so querying AAAA may be valid and successful but will require an A query anyway. Unfortunately that means the DNS server's load gets doubled for every address resolution.
The text was updated successfully, but these errors were encountered:
I am still seeing it with tip
I discovered that changing the nameservers in
After careful inspection of the response packets from Google's nameservers and ours, I can see no difference in them other than Google (and BIND at least) appear to use an internal "packet pointer" to effectively compress the response packet. When the parser sees a byte in the label where the 2 high bits are set (ie 0xC000 and higher), it uses the next 14 bits as an offset that many characters into the previous label (dots not included) to complete the rest of the current label. It is defined in RFC1035 section 4.1.4 Message Compression and described in O'Reilly's DNS and BIND 4th edition, chapter 15.2.3.
Our nameservers do not use this method of compression and instead include the full label for every RR. I will need to do some more research on that. I still think the problem is in Go but may not be exactly as described in the initial post.
Feel free to change the description correctly and send a patch including a test case (see https://github.com/golang/go/wiki#contributing-to-the-go-project) if you see a bug in https://github.com/golang/go/blob/master/src/net/dnsmsg.go#L452. It's likely because the builtin DNS stub resolver was designed as an auxiliary resolver and changed its role as a primary in Go 1.5.
Well we managed to work around our test case by enabling compression on our name servers which are built with https://github.com/miekg/dns - an awesome Go DNS library.
However, I'm pretty certain that the pure Go resolver does not implement RFC6891 which would allow UDP packet sizes > 512 octets to be requested and handled.