if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
}
- if h.Truncated { // see RFC 5966
+ // RFC 5966 indicates that when a client receives a UDP response with
+ // the TC flag set, it should take the TC flag as an indication that it
+ // should retry over TCP instead.
+ // The case when the TC flag is set in a TCP response is not well specified,
+ // so this implements the glibc resolver behavior, returning the existing
+ // dns response instead of returning a "errNoAnswerFromDNSServer" error.
+ // See go.dev/issue/64896
+ if h.Truncated && network == "udp" {
continue
}
return p, h, nil
}
}
+func TestDNSTransportNoFallbackOnTCP(t *testing.T) {
+ fake := fakeDNSServer{
+ rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
+ r := dnsmessage.Message{
+ Header: dnsmessage.Header{
+ ID: q.Header.ID,
+ Response: true,
+ RCode: dnsmessage.RCodeSuccess,
+ Truncated: true,
+ },
+ Questions: q.Questions,
+ }
+ if n == "tcp" {
+ r.Answers = []dnsmessage.Resource{
+ {
+ Header: dnsmessage.ResourceHeader{
+ Name: q.Questions[0].Name,
+ Type: dnsmessage.TypeA,
+ Class: dnsmessage.ClassINET,
+ Length: 4,
+ },
+ Body: &dnsmessage.AResource{
+ A: TestAddr,
+ },
+ },
+ }
+ }
+ return r, nil
+ },
+ }
+ r := Resolver{PreferGo: true, Dial: fake.DialContext}
+ for _, tt := range dnsTransportFallbackTests {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ p, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if h.RCode != tt.rcode {
+ t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode)
+ continue
+ }
+ a, err := p.AllAnswers()
+ if err != nil {
+ t.Errorf("unexpected error %v getting all answers from %v", err, tt.server)
+ continue
+ }
+ if len(a) != 1 {
+ t.Errorf("got %d answers from %v; want 1", len(a), tt.server)
+ continue
+ }
+ }
+}
+
// See RFC 6761 for further information about the reserved, pseudo
// domain names.
var specialDomainNameTests = []struct {
}
}
+func TestDNSUseTCPTruncated(t *testing.T) {
+ fake := fakeDNSServer{
+ rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
+ r := dnsmessage.Message{
+ Header: dnsmessage.Header{
+ ID: q.Header.ID,
+ Response: true,
+ RCode: dnsmessage.RCodeSuccess,
+ Truncated: true,
+ },
+ Questions: q.Questions,
+ Answers: []dnsmessage.Resource{
+ {
+ Header: dnsmessage.ResourceHeader{
+ Name: q.Questions[0].Name,
+ Type: dnsmessage.TypeA,
+ Class: dnsmessage.ClassINET,
+ Length: 4,
+ },
+ Body: &dnsmessage.AResource{
+ A: TestAddr,
+ },
+ },
+ },
+ }
+ if n == "udp" {
+ t.Fatal("udp protocol was used instead of tcp")
+ }
+ return r, nil
+ },
+ }
+ r := Resolver{PreferGo: true, Dial: fake.DialContext}
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ p, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false)
+ if err != nil {
+ t.Fatal("exchange failed:", err)
+ }
+ a, err := p.AllAnswers()
+ if err != nil {
+ t.Fatalf("unexpected error %v getting all answers", err)
+ }
+ if len(a) != 1 {
+ t.Fatalf("got %d answers; want 1", len(a))
+ }
+}
+
// Issue 34660: PTR response with non-PTR answers should ignore non-PTR
func TestPTRandNonPTR(t *testing.T) {
fake := fakeDNSServer{