Source file src/net/dnsclient_unix_test.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build unix
     6  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  	"maps"
    14  	"os"
    15  	"path"
    16  	"path/filepath"
    17  	"reflect"
    18  	"runtime"
    19  	"slices"
    20  	"strings"
    21  	"sync"
    22  	"sync/atomic"
    23  	"testing"
    24  	"time"
    25  
    26  	"golang.org/x/net/dns/dnsmessage"
    27  )
    28  
    29  // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
    30  var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01}
    31  
    32  // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation.
    33  var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
    34  
    35  func mustNewName(name string) dnsmessage.Name {
    36  	nn, err := dnsmessage.NewName(name)
    37  	if err != nil {
    38  		panic(fmt.Sprint("creating name: ", err))
    39  	}
    40  	return nn
    41  }
    42  
    43  func mustQuestion(name string, qtype dnsmessage.Type, class dnsmessage.Class) dnsmessage.Question {
    44  	return dnsmessage.Question{
    45  		Name:  mustNewName(name),
    46  		Type:  qtype,
    47  		Class: class,
    48  	}
    49  }
    50  
    51  var dnsTransportFallbackTests = []struct {
    52  	server   string
    53  	question dnsmessage.Question
    54  	timeout  int
    55  	rcode    dnsmessage.RCode
    56  }{
    57  	// Querying "com." with qtype=255 usually makes an answer
    58  	// which requires more than 512 bytes.
    59  	{"8.8.8.8:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 2, dnsmessage.RCodeSuccess},
    60  	{"8.8.4.4:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 4, dnsmessage.RCodeSuccess},
    61  }
    62  
    63  func TestDNSTransportFallback(t *testing.T) {
    64  	fake := fakeDNSServer{
    65  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
    66  			r := dnsmessage.Message{
    67  				Header: dnsmessage.Header{
    68  					ID:       q.Header.ID,
    69  					Response: true,
    70  					RCode:    dnsmessage.RCodeSuccess,
    71  				},
    72  				Questions: q.Questions,
    73  			}
    74  			if n == "udp" {
    75  				r.Header.Truncated = true
    76  			}
    77  			return r, nil
    78  		},
    79  	}
    80  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
    81  	for _, tt := range dnsTransportFallbackTests {
    82  		ctx, cancel := context.WithCancel(context.Background())
    83  		defer cancel()
    84  		_, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false)
    85  		if err != nil {
    86  			t.Error(err)
    87  			continue
    88  		}
    89  		if h.RCode != tt.rcode {
    90  			t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode)
    91  			continue
    92  		}
    93  	}
    94  }
    95  
    96  func TestDNSTransportNoFallbackOnTCP(t *testing.T) {
    97  	fake := fakeDNSServer{
    98  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
    99  			r := dnsmessage.Message{
   100  				Header: dnsmessage.Header{
   101  					ID:        q.Header.ID,
   102  					Response:  true,
   103  					RCode:     dnsmessage.RCodeSuccess,
   104  					Truncated: true,
   105  				},
   106  				Questions: q.Questions,
   107  			}
   108  			if n == "tcp" {
   109  				r.Answers = []dnsmessage.Resource{
   110  					{
   111  						Header: dnsmessage.ResourceHeader{
   112  							Name:   q.Questions[0].Name,
   113  							Type:   dnsmessage.TypeA,
   114  							Class:  dnsmessage.ClassINET,
   115  							Length: 4,
   116  						},
   117  						Body: &dnsmessage.AResource{
   118  							A: TestAddr,
   119  						},
   120  					},
   121  				}
   122  			}
   123  			return r, nil
   124  		},
   125  	}
   126  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   127  	for _, tt := range dnsTransportFallbackTests {
   128  		ctx, cancel := context.WithCancel(context.Background())
   129  		defer cancel()
   130  		p, h, err := r.exchange(ctx, tt.server, tt.question, time.Second, useUDPOrTCP, false)
   131  		if err != nil {
   132  			t.Error(err)
   133  			continue
   134  		}
   135  		if h.RCode != tt.rcode {
   136  			t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode)
   137  			continue
   138  		}
   139  		a, err := p.AllAnswers()
   140  		if err != nil {
   141  			t.Errorf("unexpected error %v getting all answers from %v", err, tt.server)
   142  			continue
   143  		}
   144  		if len(a) != 1 {
   145  			t.Errorf("got %d answers from %v; want 1", len(a), tt.server)
   146  			continue
   147  		}
   148  	}
   149  }
   150  
   151  // See RFC 6761 for further information about the reserved, pseudo
   152  // domain names.
   153  var specialDomainNameTests = []struct {
   154  	question dnsmessage.Question
   155  	rcode    dnsmessage.RCode
   156  }{
   157  	// Name resolution APIs and libraries should not recognize the
   158  	// followings as special.
   159  	{mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   160  	{mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   161  	{mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess},
   162  
   163  	// Name resolution APIs and libraries should recognize the
   164  	// followings as special and should not send any queries.
   165  	// Though, we test those names here for verifying negative
   166  	// answers at DNS query-response interaction level.
   167  	{mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   168  	{mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   169  }
   170  
   171  func TestSpecialDomainName(t *testing.T) {
   172  	fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   173  		r := dnsmessage.Message{
   174  			Header: dnsmessage.Header{
   175  				ID:       q.ID,
   176  				Response: true,
   177  			},
   178  			Questions: q.Questions,
   179  		}
   180  
   181  		switch q.Questions[0].Name.String() {
   182  		case "example.com.":
   183  			r.Header.RCode = dnsmessage.RCodeSuccess
   184  		default:
   185  			r.Header.RCode = dnsmessage.RCodeNameError
   186  		}
   187  
   188  		return r, nil
   189  	}}
   190  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   191  	server := "8.8.8.8:53"
   192  	for _, tt := range specialDomainNameTests {
   193  		ctx, cancel := context.WithCancel(context.Background())
   194  		defer cancel()
   195  		_, h, err := r.exchange(ctx, server, tt.question, 3*time.Second, useUDPOrTCP, false)
   196  		if err != nil {
   197  			t.Error(err)
   198  			continue
   199  		}
   200  		if h.RCode != tt.rcode {
   201  			t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode)
   202  			continue
   203  		}
   204  	}
   205  }
   206  
   207  // Issue 13705: don't try to resolve onion addresses, etc
   208  func TestAvoidDNSName(t *testing.T) {
   209  	tests := []struct {
   210  		name  string
   211  		avoid bool
   212  	}{
   213  		{"foo.com", false},
   214  		{"foo.com.", false},
   215  
   216  		{"foo.onion.", true},
   217  		{"foo.onion", true},
   218  		{"foo.ONION", true},
   219  		{"foo.ONION.", true},
   220  
   221  		// But do resolve *.local address; Issue 16739
   222  		{"foo.local.", false},
   223  		{"foo.local", false},
   224  		{"foo.LOCAL", false},
   225  		{"foo.LOCAL.", false},
   226  
   227  		{"", true}, // will be rejected earlier too
   228  
   229  		// Without stuff before onion/local, they're fine to
   230  		// use DNS. With a search path,
   231  		// "onion.vegetables.com" can use DNS. Without a
   232  		// search path (or with a trailing dot), the queries
   233  		// are just kinda useless, but don't reveal anything
   234  		// private.
   235  		{"local", false},
   236  		{"onion", false},
   237  		{"local.", false},
   238  		{"onion.", false},
   239  	}
   240  	for _, tt := range tests {
   241  		got := avoidDNS(tt.name)
   242  		if got != tt.avoid {
   243  			t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid)
   244  		}
   245  	}
   246  }
   247  
   248  func TestNameListAvoidDNS(t *testing.T) {
   249  	c := &dnsConfig{search: []string{"go.dev.", "onion."}}
   250  	got := c.nameList("www")
   251  	if !slices.Equal(got, []string{"www.", "www.go.dev."}) {
   252  		t.Fatalf(`nameList("www") = %v, want "www.", "www.go.dev."`, got)
   253  	}
   254  
   255  	got = c.nameList("www.onion")
   256  	if !slices.Equal(got, []string{"www.onion.go.dev."}) {
   257  		t.Fatalf(`nameList("www.onion") = %v, want "www.onion.go.dev."`, got)
   258  	}
   259  }
   260  
   261  var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   262  	r := dnsmessage.Message{
   263  		Header: dnsmessage.Header{
   264  			ID:       q.ID,
   265  			Response: true,
   266  		},
   267  		Questions: q.Questions,
   268  	}
   269  	if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA {
   270  		r.Answers = []dnsmessage.Resource{
   271  			{
   272  				Header: dnsmessage.ResourceHeader{
   273  					Name:   q.Questions[0].Name,
   274  					Type:   dnsmessage.TypeA,
   275  					Class:  dnsmessage.ClassINET,
   276  					Length: 4,
   277  				},
   278  				Body: &dnsmessage.AResource{
   279  					A: TestAddr,
   280  				},
   281  			},
   282  		}
   283  	}
   284  	return r, nil
   285  }}
   286  
   287  // Issue 13705: don't try to resolve onion addresses, etc
   288  func TestLookupTorOnion(t *testing.T) {
   289  	defer dnsWaitGroup.Wait()
   290  	r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
   291  	addrs, err := r.LookupIPAddr(context.Background(), "foo.onion.")
   292  	if err != nil {
   293  		t.Fatalf("lookup = %v; want nil", err)
   294  	}
   295  	if len(addrs) > 0 {
   296  		t.Errorf("unexpected addresses: %v", addrs)
   297  	}
   298  }
   299  
   300  type resolvConfTest struct {
   301  	dir  string
   302  	path string
   303  	*resolverConfig
   304  }
   305  
   306  func newResolvConfTest() (*resolvConfTest, error) {
   307  	dir, err := os.MkdirTemp("", "go-resolvconftest")
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	conf := &resolvConfTest{
   312  		dir:            dir,
   313  		path:           path.Join(dir, "resolv.conf"),
   314  		resolverConfig: &resolvConf,
   315  	}
   316  	conf.initOnce.Do(conf.init)
   317  	return conf, nil
   318  }
   319  
   320  func (conf *resolvConfTest) write(lines []string) error {
   321  	f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil {
   326  		f.Close()
   327  		return err
   328  	}
   329  	f.Close()
   330  	return nil
   331  }
   332  
   333  func (conf *resolvConfTest) writeAndUpdate(lines []string) error {
   334  	return conf.writeAndUpdateWithLastCheckedTime(lines, time.Now().Add(time.Hour))
   335  }
   336  
   337  func (conf *resolvConfTest) writeAndUpdateWithLastCheckedTime(lines []string, lastChecked time.Time) error {
   338  	if err := conf.write(lines); err != nil {
   339  		return err
   340  	}
   341  	return conf.forceUpdate(conf.path, lastChecked)
   342  }
   343  
   344  func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error {
   345  	dnsConf := dnsReadConfig(name)
   346  	if !conf.forceUpdateConf(dnsConf, lastChecked) {
   347  		return fmt.Errorf("tryAcquireSema for %s failed", name)
   348  	}
   349  	return nil
   350  }
   351  
   352  func (conf *resolvConfTest) forceUpdateConf(c *dnsConfig, lastChecked time.Time) bool {
   353  	conf.dnsConfig.Store(c)
   354  	for i := 0; i < 5; i++ {
   355  		if conf.tryAcquireSema() {
   356  			conf.lastChecked = lastChecked
   357  			conf.releaseSema()
   358  			return true
   359  		}
   360  	}
   361  	return false
   362  }
   363  
   364  func (conf *resolvConfTest) servers() []string {
   365  	return conf.dnsConfig.Load().servers
   366  }
   367  
   368  func (conf *resolvConfTest) teardown() error {
   369  	err := conf.forceUpdate("/etc/resolv.conf", time.Time{})
   370  	os.RemoveAll(conf.dir)
   371  	return err
   372  }
   373  
   374  var updateResolvConfTests = []struct {
   375  	name    string   // query name
   376  	lines   []string // resolver configuration lines
   377  	servers []string // expected name servers
   378  }{
   379  	{
   380  		name:    "golang.org",
   381  		lines:   []string{"nameserver 8.8.8.8"},
   382  		servers: []string{"8.8.8.8:53"},
   383  	},
   384  	{
   385  		name:    "",
   386  		lines:   nil, // an empty resolv.conf should use defaultNS as name servers
   387  		servers: defaultNS,
   388  	},
   389  	{
   390  		name:    "www.example.com",
   391  		lines:   []string{"nameserver 8.8.4.4"},
   392  		servers: []string{"8.8.4.4:53"},
   393  	},
   394  }
   395  
   396  func TestUpdateResolvConf(t *testing.T) {
   397  	defer dnsWaitGroup.Wait()
   398  
   399  	r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
   400  
   401  	conf, err := newResolvConfTest()
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  	defer conf.teardown()
   406  
   407  	for i, tt := range updateResolvConfTests {
   408  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   409  			t.Error(err)
   410  			continue
   411  		}
   412  		if tt.name != "" {
   413  			var wg sync.WaitGroup
   414  			const N = 10
   415  			wg.Add(N)
   416  			for j := 0; j < N; j++ {
   417  				go func(name string) {
   418  					defer wg.Done()
   419  					ips, err := r.LookupIPAddr(context.Background(), name)
   420  					if err != nil {
   421  						t.Error(err)
   422  						return
   423  					}
   424  					if len(ips) == 0 {
   425  						t.Errorf("no records for %s", name)
   426  						return
   427  					}
   428  				}(tt.name)
   429  			}
   430  			wg.Wait()
   431  		}
   432  		servers := conf.servers()
   433  		if !slices.Equal(servers, tt.servers) {
   434  			t.Errorf("#%d: got %v; want %v", i, servers, tt.servers)
   435  			continue
   436  		}
   437  	}
   438  }
   439  
   440  var goLookupIPWithResolverConfigTests = []struct {
   441  	name  string
   442  	lines []string // resolver configuration lines
   443  	error
   444  	a, aaaa bool // whether response contains A, AAAA-record
   445  }{
   446  	// no records, transport timeout
   447  	{
   448  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   449  		[]string{
   450  			"options timeout:1 attempts:1",
   451  			"nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
   452  		},
   453  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
   454  		false, false,
   455  	},
   456  
   457  	// no records, non-existent domain
   458  	{
   459  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   460  		[]string{
   461  			"options timeout:3 attempts:1",
   462  			"nameserver 8.8.8.8",
   463  		},
   464  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false},
   465  		false, false,
   466  	},
   467  
   468  	// a few A records, no AAAA records
   469  	{
   470  		"ipv4.google.com.",
   471  		[]string{
   472  			"nameserver 8.8.8.8",
   473  			"nameserver 2001:4860:4860::8888",
   474  		},
   475  		nil,
   476  		true, false,
   477  	},
   478  	{
   479  		"ipv4.google.com",
   480  		[]string{
   481  			"domain golang.org",
   482  			"nameserver 2001:4860:4860::8888",
   483  			"nameserver 8.8.8.8",
   484  		},
   485  		nil,
   486  		true, false,
   487  	},
   488  	{
   489  		"ipv4.google.com",
   490  		[]string{
   491  			"search x.golang.org y.golang.org",
   492  			"nameserver 2001:4860:4860::8888",
   493  			"nameserver 8.8.8.8",
   494  		},
   495  		nil,
   496  		true, false,
   497  	},
   498  
   499  	// no A records, a few AAAA records
   500  	{
   501  		"ipv6.google.com.",
   502  		[]string{
   503  			"nameserver 2001:4860:4860::8888",
   504  			"nameserver 8.8.8.8",
   505  		},
   506  		nil,
   507  		false, true,
   508  	},
   509  	{
   510  		"ipv6.google.com",
   511  		[]string{
   512  			"domain golang.org",
   513  			"nameserver 8.8.8.8",
   514  			"nameserver 2001:4860:4860::8888",
   515  		},
   516  		nil,
   517  		false, true,
   518  	},
   519  	{
   520  		"ipv6.google.com",
   521  		[]string{
   522  			"search x.golang.org y.golang.org",
   523  			"nameserver 8.8.8.8",
   524  			"nameserver 2001:4860:4860::8888",
   525  		},
   526  		nil,
   527  		false, true,
   528  	},
   529  
   530  	// both A and AAAA records
   531  	{
   532  		"hostname.as112.net", // see RFC 7534
   533  		[]string{
   534  			"domain golang.org",
   535  			"nameserver 2001:4860:4860::8888",
   536  			"nameserver 8.8.8.8",
   537  		},
   538  		nil,
   539  		true, true,
   540  	},
   541  	{
   542  		"hostname.as112.net", // see RFC 7534
   543  		[]string{
   544  			"search x.golang.org y.golang.org",
   545  			"nameserver 2001:4860:4860::8888",
   546  			"nameserver 8.8.8.8",
   547  		},
   548  		nil,
   549  		true, true,
   550  	},
   551  }
   552  
   553  func TestGoLookupIPWithResolverConfig(t *testing.T) {
   554  	defer dnsWaitGroup.Wait()
   555  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   556  		switch s {
   557  		case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
   558  			break
   559  		default:
   560  			time.Sleep(10 * time.Millisecond)
   561  			return dnsmessage.Message{}, os.ErrDeadlineExceeded
   562  		}
   563  		r := dnsmessage.Message{
   564  			Header: dnsmessage.Header{
   565  				ID:       q.ID,
   566  				Response: true,
   567  			},
   568  			Questions: q.Questions,
   569  		}
   570  		for _, question := range q.Questions {
   571  			switch question.Type {
   572  			case dnsmessage.TypeA:
   573  				switch question.Name.String() {
   574  				case "hostname.as112.net.":
   575  					break
   576  				case "ipv4.google.com.":
   577  					r.Answers = append(r.Answers, dnsmessage.Resource{
   578  						Header: dnsmessage.ResourceHeader{
   579  							Name:   q.Questions[0].Name,
   580  							Type:   dnsmessage.TypeA,
   581  							Class:  dnsmessage.ClassINET,
   582  							Length: 4,
   583  						},
   584  						Body: &dnsmessage.AResource{
   585  							A: TestAddr,
   586  						},
   587  					})
   588  				default:
   589  
   590  				}
   591  			case dnsmessage.TypeAAAA:
   592  				switch question.Name.String() {
   593  				case "hostname.as112.net.":
   594  					break
   595  				case "ipv6.google.com.":
   596  					r.Answers = append(r.Answers, dnsmessage.Resource{
   597  						Header: dnsmessage.ResourceHeader{
   598  							Name:   q.Questions[0].Name,
   599  							Type:   dnsmessage.TypeAAAA,
   600  							Class:  dnsmessage.ClassINET,
   601  							Length: 16,
   602  						},
   603  						Body: &dnsmessage.AAAAResource{
   604  							AAAA: TestAddr6,
   605  						},
   606  					})
   607  				}
   608  			}
   609  		}
   610  		return r, nil
   611  	}}
   612  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   613  
   614  	conf, err := newResolvConfTest()
   615  	if err != nil {
   616  		t.Fatal(err)
   617  	}
   618  	defer conf.teardown()
   619  
   620  	for _, tt := range goLookupIPWithResolverConfigTests {
   621  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   622  			t.Error(err)
   623  			continue
   624  		}
   625  		addrs, err := r.LookupIPAddr(context.Background(), tt.name)
   626  		if err != nil {
   627  			if err, ok := err.(*DNSError); !ok || tt.error != nil && (err.Name != tt.error.(*DNSError).Name || err.Server != tt.error.(*DNSError).Server || err.IsTimeout != tt.error.(*DNSError).IsTimeout) {
   628  				t.Errorf("got %v; want %v", err, tt.error)
   629  			}
   630  			continue
   631  		}
   632  		if len(addrs) == 0 {
   633  			t.Errorf("no records for %s", tt.name)
   634  		}
   635  		if !tt.a && !tt.aaaa && len(addrs) > 0 {
   636  			t.Errorf("unexpected %v for %s", addrs, tt.name)
   637  		}
   638  		for _, addr := range addrs {
   639  			if !tt.a && addr.IP.To4() != nil {
   640  				t.Errorf("got %v; must not be IPv4 address", addr)
   641  			}
   642  			if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil {
   643  				t.Errorf("got %v; must not be IPv6 address", addr)
   644  			}
   645  		}
   646  	}
   647  }
   648  
   649  // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
   650  func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
   651  	defer dnsWaitGroup.Wait()
   652  
   653  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) {
   654  		r := dnsmessage.Message{
   655  			Header: dnsmessage.Header{
   656  				ID:       q.ID,
   657  				Response: true,
   658  			},
   659  			Questions: q.Questions,
   660  		}
   661  		return r, nil
   662  	}}
   663  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   664  
   665  	// Add a config that simulates no dns servers being available.
   666  	conf, err := newResolvConfTest()
   667  	if err != nil {
   668  		t.Fatal(err)
   669  	}
   670  	defer conf.teardown()
   671  
   672  	if err := conf.writeAndUpdate([]string{}); err != nil {
   673  		t.Fatal(err)
   674  	}
   675  	// Redirect host file lookups.
   676  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
   677  	hostsFilePath = "testdata/hosts"
   678  
   679  	for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
   680  		name := fmt.Sprintf("order %v", order)
   681  		// First ensure that we get an error when contacting a non-existent host.
   682  		_, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "notarealhost", order, nil)
   683  		if err == nil {
   684  			t.Errorf("%s: expected error while looking up name not in hosts file", name)
   685  			continue
   686  		}
   687  
   688  		// Now check that we get an address when the name appears in the hosts file.
   689  		addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", "thor", order, nil) // entry is in "testdata/hosts"
   690  		if err != nil {
   691  			t.Errorf("%s: expected to successfully lookup host entry", name)
   692  			continue
   693  		}
   694  		if len(addrs) != 1 {
   695  			t.Errorf("%s: expected exactly one result, but got %v", name, addrs)
   696  			continue
   697  		}
   698  		if got, want := addrs[0].String(), "127.1.1.1"; got != want {
   699  			t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want)
   700  		}
   701  	}
   702  }
   703  
   704  // Issue 12712.
   705  // When using search domains, return the error encountered
   706  // querying the original name instead of an error encountered
   707  // querying a generated name.
   708  func TestErrorForOriginalNameWhenSearching(t *testing.T) {
   709  	defer dnsWaitGroup.Wait()
   710  
   711  	const fqdn = "doesnotexist.domain"
   712  
   713  	conf, err := newResolvConfTest()
   714  	if err != nil {
   715  		t.Fatal(err)
   716  	}
   717  	defer conf.teardown()
   718  
   719  	if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
   720  		t.Fatal(err)
   721  	}
   722  
   723  	fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   724  		r := dnsmessage.Message{
   725  			Header: dnsmessage.Header{
   726  				ID:       q.ID,
   727  				Response: true,
   728  			},
   729  			Questions: q.Questions,
   730  		}
   731  
   732  		switch q.Questions[0].Name.String() {
   733  		case fqdn + ".servfail.":
   734  			r.Header.RCode = dnsmessage.RCodeServerFailure
   735  		default:
   736  			r.Header.RCode = dnsmessage.RCodeNameError
   737  		}
   738  
   739  		return r, nil
   740  	}}
   741  
   742  	cases := []struct {
   743  		strictErrors bool
   744  		wantErr      *DNSError
   745  	}{
   746  		{true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}},
   747  		{false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error(), IsNotFound: true}},
   748  	}
   749  	for _, tt := range cases {
   750  		r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext}
   751  		_, err = r.LookupIPAddr(context.Background(), fqdn)
   752  		if err == nil {
   753  			t.Fatal("expected an error")
   754  		}
   755  
   756  		want := tt.wantErr
   757  		if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary {
   758  			t.Errorf("got %v; want %v", err, want)
   759  		}
   760  	}
   761  }
   762  
   763  // Issue 15434. If a name server gives a lame referral, continue to the next.
   764  func TestIgnoreLameReferrals(t *testing.T) {
   765  	defer dnsWaitGroup.Wait()
   766  
   767  	conf, err := newResolvConfTest()
   768  	if err != nil {
   769  		t.Fatal(err)
   770  	}
   771  	defer conf.teardown()
   772  
   773  	if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
   774  		"nameserver 192.0.2.2"}); err != nil {
   775  		t.Fatal(err)
   776  	}
   777  
   778  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   779  		t.Log(s, q)
   780  		r := dnsmessage.Message{
   781  			Header: dnsmessage.Header{
   782  				ID:       q.ID,
   783  				Response: true,
   784  			},
   785  			Questions: q.Questions,
   786  		}
   787  
   788  		if s == "192.0.2.2:53" {
   789  			r.Header.RecursionAvailable = true
   790  			if q.Questions[0].Type == dnsmessage.TypeA {
   791  				r.Answers = []dnsmessage.Resource{
   792  					{
   793  						Header: dnsmessage.ResourceHeader{
   794  							Name:   q.Questions[0].Name,
   795  							Type:   dnsmessage.TypeA,
   796  							Class:  dnsmessage.ClassINET,
   797  							Length: 4,
   798  						},
   799  						Body: &dnsmessage.AResource{
   800  							A: TestAddr,
   801  						},
   802  					},
   803  				}
   804  			}
   805  		} else if s == "192.0.2.1:53" {
   806  			if q.Questions[0].Type == dnsmessage.TypeA && strings.HasPrefix(q.Questions[0].Name.String(), "empty.com.") {
   807  				var edns0Hdr dnsmessage.ResourceHeader
   808  				edns0Hdr.SetEDNS0(maxDNSPacketSize, dnsmessage.RCodeSuccess, false)
   809  
   810  				r.Additionals = []dnsmessage.Resource{
   811  					{
   812  						Header: edns0Hdr,
   813  						Body:   &dnsmessage.OPTResource{},
   814  					},
   815  				}
   816  			}
   817  		}
   818  
   819  		return r, nil
   820  	}}
   821  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   822  
   823  	addrs, err := r.LookupIP(context.Background(), "ip4", "www.golang.org")
   824  	if err != nil {
   825  		t.Fatal(err)
   826  	}
   827  
   828  	if got := len(addrs); got != 1 {
   829  		t.Fatalf("got %d addresses, want 1", got)
   830  	}
   831  
   832  	if got, want := addrs[0].String(), "192.0.2.1"; got != want {
   833  		t.Fatalf("got address %v, want %v", got, want)
   834  	}
   835  
   836  	_, err = r.LookupIP(context.Background(), "ip4", "empty.com")
   837  	de, ok := err.(*DNSError)
   838  	if !ok {
   839  		t.Fatalf("err = %#v; wanted a *net.DNSError", err)
   840  	}
   841  	if de.Err != errNoSuchHost.Error() {
   842  		t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error())
   843  	}
   844  }
   845  
   846  func BenchmarkGoLookupIP(b *testing.B) {
   847  	testHookUninstaller.Do(uninstallTestHooks)
   848  	ctx := context.Background()
   849  	b.ReportAllocs()
   850  
   851  	for i := 0; i < b.N; i++ {
   852  		goResolver.LookupIPAddr(ctx, "www.example.com")
   853  	}
   854  }
   855  
   856  func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
   857  	testHookUninstaller.Do(uninstallTestHooks)
   858  	ctx := context.Background()
   859  	b.ReportAllocs()
   860  
   861  	for i := 0; i < b.N; i++ {
   862  		goResolver.LookupIPAddr(ctx, "some.nonexistent")
   863  	}
   864  }
   865  
   866  func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
   867  	testHookUninstaller.Do(uninstallTestHooks)
   868  
   869  	conf, err := newResolvConfTest()
   870  	if err != nil {
   871  		b.Fatal(err)
   872  	}
   873  	defer conf.teardown()
   874  
   875  	lines := []string{
   876  		"nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
   877  		"nameserver 8.8.8.8",
   878  	}
   879  	if err := conf.writeAndUpdate(lines); err != nil {
   880  		b.Fatal(err)
   881  	}
   882  	ctx := context.Background()
   883  	b.ReportAllocs()
   884  
   885  	for i := 0; i < b.N; i++ {
   886  		goResolver.LookupIPAddr(ctx, "www.example.com")
   887  	}
   888  }
   889  
   890  type fakeDNSServer struct {
   891  	rh        func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error)
   892  	alwaysTCP bool
   893  }
   894  
   895  func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) {
   896  	if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" {
   897  		return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil
   898  	}
   899  	return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil
   900  }
   901  
   902  type fakeDNSConn struct {
   903  	Conn
   904  	tcp    bool
   905  	server *fakeDNSServer
   906  	n      string
   907  	s      string
   908  	q      dnsmessage.Message
   909  	t      time.Time
   910  	buf    []byte
   911  }
   912  
   913  func (f *fakeDNSConn) Close() error {
   914  	return nil
   915  }
   916  
   917  func (f *fakeDNSConn) Read(b []byte) (int, error) {
   918  	if len(f.buf) > 0 {
   919  		n := copy(b, f.buf)
   920  		f.buf = f.buf[n:]
   921  		return n, nil
   922  	}
   923  
   924  	resp, err := f.server.rh(f.n, f.s, f.q, f.t)
   925  	if err != nil {
   926  		return 0, err
   927  	}
   928  
   929  	bb := make([]byte, 2, 514)
   930  	bb, err = resp.AppendPack(bb)
   931  	if err != nil {
   932  		return 0, fmt.Errorf("cannot marshal DNS message: %v", err)
   933  	}
   934  
   935  	if f.tcp {
   936  		l := len(bb) - 2
   937  		bb[0] = byte(l >> 8)
   938  		bb[1] = byte(l)
   939  		f.buf = bb
   940  		return f.Read(b)
   941  	}
   942  
   943  	bb = bb[2:]
   944  	if len(b) < len(bb) {
   945  		return 0, errors.New("read would fragment DNS message")
   946  	}
   947  
   948  	copy(b, bb)
   949  	return len(bb), nil
   950  }
   951  
   952  func (f *fakeDNSConn) Write(b []byte) (int, error) {
   953  	if f.tcp && len(b) >= 2 {
   954  		b = b[2:]
   955  	}
   956  	if f.q.Unpack(b) != nil {
   957  		return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b))
   958  	}
   959  	return len(b), nil
   960  }
   961  
   962  func (f *fakeDNSConn) SetDeadline(t time.Time) error {
   963  	f.t = t
   964  	return nil
   965  }
   966  
   967  type fakeDNSPacketConn struct {
   968  	PacketConn
   969  	fakeDNSConn
   970  }
   971  
   972  func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error {
   973  	return f.fakeDNSConn.SetDeadline(t)
   974  }
   975  
   976  func (f *fakeDNSPacketConn) Close() error {
   977  	return f.fakeDNSConn.Close()
   978  }
   979  
   980  // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
   981  func TestIgnoreDNSForgeries(t *testing.T) {
   982  	c, s := Pipe()
   983  	go func() {
   984  		b := make([]byte, maxDNSPacketSize)
   985  		n, err := s.Read(b)
   986  		if err != nil {
   987  			t.Error(err)
   988  			return
   989  		}
   990  
   991  		var msg dnsmessage.Message
   992  		if msg.Unpack(b[:n]) != nil {
   993  			t.Error("invalid DNS query:", err)
   994  			return
   995  		}
   996  
   997  		s.Write([]byte("garbage DNS response packet"))
   998  
   999  		msg.Header.Response = true
  1000  		msg.Header.ID++ // make invalid ID
  1001  
  1002  		if b, err = msg.Pack(); err != nil {
  1003  			t.Error("failed to pack DNS response:", err)
  1004  			return
  1005  		}
  1006  		s.Write(b)
  1007  
  1008  		msg.Header.ID-- // restore original ID
  1009  		msg.Answers = []dnsmessage.Resource{
  1010  			{
  1011  				Header: dnsmessage.ResourceHeader{
  1012  					Name:   mustNewName("www.example.com."),
  1013  					Type:   dnsmessage.TypeA,
  1014  					Class:  dnsmessage.ClassINET,
  1015  					Length: 4,
  1016  				},
  1017  				Body: &dnsmessage.AResource{
  1018  					A: TestAddr,
  1019  				},
  1020  			},
  1021  		}
  1022  
  1023  		b, err = msg.Pack()
  1024  		if err != nil {
  1025  			t.Error("failed to pack DNS response:", err)
  1026  			return
  1027  		}
  1028  		s.Write(b)
  1029  	}()
  1030  
  1031  	msg := dnsmessage.Message{
  1032  		Header: dnsmessage.Header{
  1033  			ID: 42,
  1034  		},
  1035  		Questions: []dnsmessage.Question{
  1036  			{
  1037  				Name:  mustNewName("www.example.com."),
  1038  				Type:  dnsmessage.TypeA,
  1039  				Class: dnsmessage.ClassINET,
  1040  			},
  1041  		},
  1042  	}
  1043  
  1044  	b, err := msg.Pack()
  1045  	if err != nil {
  1046  		t.Fatal("Pack failed:", err)
  1047  	}
  1048  
  1049  	p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b)
  1050  	if err != nil {
  1051  		t.Fatalf("dnsPacketRoundTrip failed: %v", err)
  1052  	}
  1053  
  1054  	p.SkipAllQuestions()
  1055  	as, err := p.AllAnswers()
  1056  	if err != nil {
  1057  		t.Fatal("AllAnswers failed:", err)
  1058  	}
  1059  	if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr {
  1060  		t.Errorf("got address %v, want %v", got, TestAddr)
  1061  	}
  1062  }
  1063  
  1064  // Issue 16865. If a name server times out, continue to the next.
  1065  func TestRetryTimeout(t *testing.T) {
  1066  	defer dnsWaitGroup.Wait()
  1067  
  1068  	conf, err := newResolvConfTest()
  1069  	if err != nil {
  1070  		t.Fatal(err)
  1071  	}
  1072  	defer conf.teardown()
  1073  
  1074  	testConf := []string{
  1075  		"nameserver 192.0.2.1", // the one that will timeout
  1076  		"nameserver 192.0.2.2",
  1077  	}
  1078  	if err := conf.writeAndUpdate(testConf); err != nil {
  1079  		t.Fatal(err)
  1080  	}
  1081  
  1082  	var deadline0 time.Time
  1083  
  1084  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1085  		t.Log(s, q, deadline)
  1086  
  1087  		if deadline.IsZero() {
  1088  			t.Error("zero deadline")
  1089  		}
  1090  
  1091  		if s == "192.0.2.1:53" {
  1092  			deadline0 = deadline
  1093  			time.Sleep(10 * time.Millisecond)
  1094  			return dnsmessage.Message{}, os.ErrDeadlineExceeded
  1095  		}
  1096  
  1097  		if deadline.Equal(deadline0) {
  1098  			t.Error("deadline didn't change")
  1099  		}
  1100  
  1101  		return mockTXTResponse(q), nil
  1102  	}}
  1103  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  1104  
  1105  	_, err = r.LookupTXT(context.Background(), "www.golang.org")
  1106  	if err != nil {
  1107  		t.Fatal(err)
  1108  	}
  1109  
  1110  	if deadline0.IsZero() {
  1111  		t.Error("deadline0 still zero", deadline0)
  1112  	}
  1113  }
  1114  
  1115  func TestRotate(t *testing.T) {
  1116  	// without rotation, always uses the first server
  1117  	testRotate(t, false, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.1:53", "192.0.2.1:53"})
  1118  
  1119  	// with rotation, rotates through back to first
  1120  	testRotate(t, true, []string{"192.0.2.1", "192.0.2.2"}, []string{"192.0.2.1:53", "192.0.2.2:53", "192.0.2.1:53"})
  1121  }
  1122  
  1123  func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
  1124  	defer dnsWaitGroup.Wait()
  1125  
  1126  	conf, err := newResolvConfTest()
  1127  	if err != nil {
  1128  		t.Fatal(err)
  1129  	}
  1130  	defer conf.teardown()
  1131  
  1132  	var confLines []string
  1133  	for _, ns := range nameservers {
  1134  		confLines = append(confLines, "nameserver "+ns)
  1135  	}
  1136  	if rotate {
  1137  		confLines = append(confLines, "options rotate")
  1138  	}
  1139  
  1140  	if err := conf.writeAndUpdate(confLines); err != nil {
  1141  		t.Fatal(err)
  1142  	}
  1143  
  1144  	var usedServers []string
  1145  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1146  		usedServers = append(usedServers, s)
  1147  		return mockTXTResponse(q), nil
  1148  	}}
  1149  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1150  
  1151  	// len(nameservers) + 1 to allow rotation to get back to start
  1152  	for i := 0; i < len(nameservers)+1; i++ {
  1153  		if _, err := r.LookupTXT(context.Background(), "www.golang.org"); err != nil {
  1154  			t.Fatal(err)
  1155  		}
  1156  	}
  1157  
  1158  	if !slices.Equal(usedServers, wantServers) {
  1159  		t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
  1160  	}
  1161  }
  1162  
  1163  func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message {
  1164  	r := dnsmessage.Message{
  1165  		Header: dnsmessage.Header{
  1166  			ID:                 q.ID,
  1167  			Response:           true,
  1168  			RecursionAvailable: true,
  1169  		},
  1170  		Questions: q.Questions,
  1171  		Answers: []dnsmessage.Resource{
  1172  			{
  1173  				Header: dnsmessage.ResourceHeader{
  1174  					Name:  q.Questions[0].Name,
  1175  					Type:  dnsmessage.TypeTXT,
  1176  					Class: dnsmessage.ClassINET,
  1177  				},
  1178  				Body: &dnsmessage.TXTResource{
  1179  					TXT: []string{"ok"},
  1180  				},
  1181  			},
  1182  		},
  1183  	}
  1184  
  1185  	return r
  1186  }
  1187  
  1188  // Issue 17448. With StrictErrors enabled, temporary errors should make
  1189  // LookupIP fail rather than return a partial result.
  1190  func TestStrictErrorsLookupIP(t *testing.T) {
  1191  	defer dnsWaitGroup.Wait()
  1192  
  1193  	conf, err := newResolvConfTest()
  1194  	if err != nil {
  1195  		t.Fatal(err)
  1196  	}
  1197  	defer conf.teardown()
  1198  
  1199  	confData := []string{
  1200  		"nameserver 192.0.2.53",
  1201  		"search x.golang.org y.golang.org",
  1202  	}
  1203  	if err := conf.writeAndUpdate(confData); err != nil {
  1204  		t.Fatal(err)
  1205  	}
  1206  
  1207  	const name = "test-issue19592"
  1208  	const server = "192.0.2.53:53"
  1209  	const searchX = "test-issue19592.x.golang.org."
  1210  	const searchY = "test-issue19592.y.golang.org."
  1211  	const ip4 = "192.0.2.1"
  1212  	const ip6 = "2001:db8::1"
  1213  
  1214  	type resolveWhichEnum int
  1215  	const (
  1216  		resolveOK resolveWhichEnum = iota
  1217  		resolveOpError
  1218  		resolveServfail
  1219  		resolveTimeout
  1220  	)
  1221  
  1222  	makeTempError := func(err string) error {
  1223  		return &DNSError{
  1224  			Err:         err,
  1225  			Name:        name,
  1226  			Server:      server,
  1227  			IsTemporary: true,
  1228  		}
  1229  	}
  1230  	makeTimeout := func() error {
  1231  		return &DNSError{
  1232  			Err:         os.ErrDeadlineExceeded.Error(),
  1233  			Name:        name,
  1234  			Server:      server,
  1235  			IsTimeout:   true,
  1236  			IsTemporary: true,
  1237  		}
  1238  	}
  1239  	makeNxDomain := func() error {
  1240  		return &DNSError{
  1241  			Err:        errNoSuchHost.Error(),
  1242  			Name:       name,
  1243  			Server:     server,
  1244  			IsNotFound: true,
  1245  		}
  1246  	}
  1247  
  1248  	cases := []struct {
  1249  		desc          string
  1250  		resolveWhich  func(quest dnsmessage.Question) resolveWhichEnum
  1251  		wantStrictErr error
  1252  		wantLaxErr    error
  1253  		wantIPs       []string
  1254  	}{
  1255  		{
  1256  			desc: "No errors",
  1257  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1258  				return resolveOK
  1259  			},
  1260  			wantIPs: []string{ip4, ip6},
  1261  		},
  1262  		{
  1263  			desc: "searchX error fails in strict mode",
  1264  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1265  				if quest.Name.String() == searchX {
  1266  					return resolveTimeout
  1267  				}
  1268  				return resolveOK
  1269  			},
  1270  			wantStrictErr: makeTimeout(),
  1271  			wantIPs:       []string{ip4, ip6},
  1272  		},
  1273  		{
  1274  			desc: "searchX IPv4-only timeout fails in strict mode",
  1275  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1276  				if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA {
  1277  					return resolveTimeout
  1278  				}
  1279  				return resolveOK
  1280  			},
  1281  			wantStrictErr: makeTimeout(),
  1282  			wantIPs:       []string{ip4, ip6},
  1283  		},
  1284  		{
  1285  			desc: "searchX IPv6-only servfail fails in strict mode",
  1286  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1287  				if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA {
  1288  					return resolveServfail
  1289  				}
  1290  				return resolveOK
  1291  			},
  1292  			wantStrictErr: makeTempError("server misbehaving"),
  1293  			wantIPs:       []string{ip4, ip6},
  1294  		},
  1295  		{
  1296  			desc: "searchY error always fails",
  1297  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1298  				if quest.Name.String() == searchY {
  1299  					return resolveTimeout
  1300  				}
  1301  				return resolveOK
  1302  			},
  1303  			wantStrictErr: makeTimeout(),
  1304  			wantLaxErr:    makeNxDomain(), // This one reaches the "test." FQDN.
  1305  		},
  1306  		{
  1307  			desc: "searchY IPv4-only socket error fails in strict mode",
  1308  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1309  				if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA {
  1310  					return resolveOpError
  1311  				}
  1312  				return resolveOK
  1313  			},
  1314  			wantStrictErr: makeTempError("write: socket on fire"),
  1315  			wantIPs:       []string{ip6},
  1316  		},
  1317  		{
  1318  			desc: "searchY IPv6-only timeout fails in strict mode",
  1319  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1320  				if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA {
  1321  					return resolveTimeout
  1322  				}
  1323  				return resolveOK
  1324  			},
  1325  			wantStrictErr: makeTimeout(),
  1326  			wantIPs:       []string{ip4},
  1327  		},
  1328  	}
  1329  
  1330  	for i, tt := range cases {
  1331  		fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1332  			t.Log(s, q)
  1333  
  1334  			switch tt.resolveWhich(q.Questions[0]) {
  1335  			case resolveOK:
  1336  				// Handle below.
  1337  			case resolveOpError:
  1338  				return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")}
  1339  			case resolveServfail:
  1340  				return dnsmessage.Message{
  1341  					Header: dnsmessage.Header{
  1342  						ID:       q.ID,
  1343  						Response: true,
  1344  						RCode:    dnsmessage.RCodeServerFailure,
  1345  					},
  1346  					Questions: q.Questions,
  1347  				}, nil
  1348  			case resolveTimeout:
  1349  				return dnsmessage.Message{}, os.ErrDeadlineExceeded
  1350  			default:
  1351  				t.Fatal("Impossible resolveWhich")
  1352  			}
  1353  
  1354  			switch q.Questions[0].Name.String() {
  1355  			case searchX, name + ".":
  1356  				// Return NXDOMAIN to utilize the search list.
  1357  				return dnsmessage.Message{
  1358  					Header: dnsmessage.Header{
  1359  						ID:       q.ID,
  1360  						Response: true,
  1361  						RCode:    dnsmessage.RCodeNameError,
  1362  					},
  1363  					Questions: q.Questions,
  1364  				}, nil
  1365  			case searchY:
  1366  				// Return records below.
  1367  			default:
  1368  				return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
  1369  			}
  1370  
  1371  			r := dnsmessage.Message{
  1372  				Header: dnsmessage.Header{
  1373  					ID:       q.ID,
  1374  					Response: true,
  1375  				},
  1376  				Questions: q.Questions,
  1377  			}
  1378  			switch q.Questions[0].Type {
  1379  			case dnsmessage.TypeA:
  1380  				r.Answers = []dnsmessage.Resource{
  1381  					{
  1382  						Header: dnsmessage.ResourceHeader{
  1383  							Name:   q.Questions[0].Name,
  1384  							Type:   dnsmessage.TypeA,
  1385  							Class:  dnsmessage.ClassINET,
  1386  							Length: 4,
  1387  						},
  1388  						Body: &dnsmessage.AResource{
  1389  							A: TestAddr,
  1390  						},
  1391  					},
  1392  				}
  1393  			case dnsmessage.TypeAAAA:
  1394  				r.Answers = []dnsmessage.Resource{
  1395  					{
  1396  						Header: dnsmessage.ResourceHeader{
  1397  							Name:   q.Questions[0].Name,
  1398  							Type:   dnsmessage.TypeAAAA,
  1399  							Class:  dnsmessage.ClassINET,
  1400  							Length: 16,
  1401  						},
  1402  						Body: &dnsmessage.AAAAResource{
  1403  							AAAA: TestAddr6,
  1404  						},
  1405  					},
  1406  				}
  1407  			default:
  1408  				return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type)
  1409  			}
  1410  			return r, nil
  1411  		}}
  1412  
  1413  		for _, strict := range []bool{true, false} {
  1414  			r := Resolver{PreferGo: true, StrictErrors: strict, Dial: fake.DialContext}
  1415  			ips, err := r.LookupIPAddr(context.Background(), name)
  1416  
  1417  			var wantErr error
  1418  			if strict {
  1419  				wantErr = tt.wantStrictErr
  1420  			} else {
  1421  				wantErr = tt.wantLaxErr
  1422  			}
  1423  			if !reflect.DeepEqual(err, wantErr) {
  1424  				t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr)
  1425  			}
  1426  
  1427  			gotIPs := map[string]struct{}{}
  1428  			for _, ip := range ips {
  1429  				gotIPs[ip.String()] = struct{}{}
  1430  			}
  1431  			wantIPs := map[string]struct{}{}
  1432  			if wantErr == nil {
  1433  				for _, ip := range tt.wantIPs {
  1434  					wantIPs[ip] = struct{}{}
  1435  				}
  1436  			}
  1437  			if !maps.Equal(gotIPs, wantIPs) {
  1438  				t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs)
  1439  			}
  1440  		}
  1441  	}
  1442  }
  1443  
  1444  // Issue 17448. With StrictErrors enabled, temporary errors should make
  1445  // LookupTXT stop walking the search list.
  1446  func TestStrictErrorsLookupTXT(t *testing.T) {
  1447  	defer dnsWaitGroup.Wait()
  1448  
  1449  	conf, err := newResolvConfTest()
  1450  	if err != nil {
  1451  		t.Fatal(err)
  1452  	}
  1453  	defer conf.teardown()
  1454  
  1455  	confData := []string{
  1456  		"nameserver 192.0.2.53",
  1457  		"search x.golang.org y.golang.org",
  1458  	}
  1459  	if err := conf.writeAndUpdate(confData); err != nil {
  1460  		t.Fatal(err)
  1461  	}
  1462  
  1463  	const name = "test"
  1464  	const server = "192.0.2.53:53"
  1465  	const searchX = "test.x.golang.org."
  1466  	const searchY = "test.y.golang.org."
  1467  	const txt = "Hello World"
  1468  
  1469  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1470  		t.Log(s, q)
  1471  
  1472  		switch q.Questions[0].Name.String() {
  1473  		case searchX:
  1474  			return dnsmessage.Message{}, os.ErrDeadlineExceeded
  1475  		case searchY:
  1476  			return mockTXTResponse(q), nil
  1477  		default:
  1478  			return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
  1479  		}
  1480  	}}
  1481  
  1482  	for _, strict := range []bool{true, false} {
  1483  		r := Resolver{StrictErrors: strict, Dial: fake.DialContext}
  1484  		p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT, nil)
  1485  		var wantErr error
  1486  		var wantRRs int
  1487  		if strict {
  1488  			wantErr = &DNSError{
  1489  				Err:         os.ErrDeadlineExceeded.Error(),
  1490  				Name:        name,
  1491  				Server:      server,
  1492  				IsTimeout:   true,
  1493  				IsTemporary: true,
  1494  			}
  1495  		} else {
  1496  			wantRRs = 1
  1497  		}
  1498  		if !reflect.DeepEqual(err, wantErr) {
  1499  			t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr)
  1500  		}
  1501  		a, err := p.AllAnswers()
  1502  		if err != nil {
  1503  			a = nil
  1504  		}
  1505  		if len(a) != wantRRs {
  1506  			t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs)
  1507  		}
  1508  	}
  1509  }
  1510  
  1511  // Test for a race between uninstalling the test hooks and closing a
  1512  // socket connection. This used to fail when testing with -race.
  1513  func TestDNSGoroutineRace(t *testing.T) {
  1514  	defer dnsWaitGroup.Wait()
  1515  
  1516  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) {
  1517  		time.Sleep(10 * time.Microsecond)
  1518  		return dnsmessage.Message{}, os.ErrDeadlineExceeded
  1519  	}}
  1520  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1521  
  1522  	// The timeout here is less than the timeout used by the server,
  1523  	// so the goroutine started to query the (fake) server will hang
  1524  	// around after this test is done if we don't call dnsWaitGroup.Wait.
  1525  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond)
  1526  	defer cancel()
  1527  	_, err := r.LookupIPAddr(ctx, "where.are.they.now")
  1528  	if err == nil {
  1529  		t.Fatal("fake DNS lookup unexpectedly succeeded")
  1530  	}
  1531  }
  1532  
  1533  func lookupWithFake(fake fakeDNSServer, name string, typ dnsmessage.Type) error {
  1534  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1535  
  1536  	conf := getSystemDNSConfig()
  1537  
  1538  	ctx, cancel := context.WithCancel(context.Background())
  1539  	defer cancel()
  1540  
  1541  	_, _, err := r.tryOneName(ctx, conf, name, typ)
  1542  	return err
  1543  }
  1544  
  1545  // Issue 8434: verify that Temporary returns true on an error when rcode
  1546  // is SERVFAIL
  1547  func TestIssue8434(t *testing.T) {
  1548  	err := lookupWithFake(fakeDNSServer{
  1549  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1550  			return dnsmessage.Message{
  1551  				Header: dnsmessage.Header{
  1552  					ID:       q.ID,
  1553  					Response: true,
  1554  					RCode:    dnsmessage.RCodeServerFailure,
  1555  				},
  1556  				Questions: q.Questions,
  1557  			}, nil
  1558  		},
  1559  	}, "golang.org.", dnsmessage.TypeALL)
  1560  	if err == nil {
  1561  		t.Fatal("expected an error")
  1562  	}
  1563  	if ne, ok := err.(Error); !ok {
  1564  		t.Fatalf("err = %#v; wanted something supporting net.Error", err)
  1565  	} else if !ne.Temporary() {
  1566  		t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err)
  1567  	}
  1568  	if de, ok := err.(*DNSError); !ok {
  1569  		t.Fatalf("err = %#v; wanted a *net.DNSError", err)
  1570  	} else if !de.IsTemporary {
  1571  		t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err)
  1572  	}
  1573  }
  1574  
  1575  func TestIssueNoSuchHostExists(t *testing.T) {
  1576  	err := lookupWithFake(fakeDNSServer{
  1577  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1578  			return dnsmessage.Message{
  1579  				Header: dnsmessage.Header{
  1580  					ID:       q.ID,
  1581  					Response: true,
  1582  					RCode:    dnsmessage.RCodeNameError,
  1583  				},
  1584  				Questions: q.Questions,
  1585  			}, nil
  1586  		},
  1587  	}, "golang.org.", dnsmessage.TypeALL)
  1588  	if err == nil {
  1589  		t.Fatal("expected an error")
  1590  	}
  1591  	if _, ok := err.(Error); !ok {
  1592  		t.Fatalf("err = %#v; wanted something supporting net.Error", err)
  1593  	}
  1594  	if de, ok := err.(*DNSError); !ok {
  1595  		t.Fatalf("err = %#v; wanted a *net.DNSError", err)
  1596  	} else if !de.IsNotFound {
  1597  		t.Fatalf("IsNotFound = false for err = %#v; want IsNotFound == true", err)
  1598  	}
  1599  }
  1600  
  1601  // TestNoSuchHost verifies that tryOneName works correctly when the domain does
  1602  // not exist.
  1603  //
  1604  // Issue 12778: verify that NXDOMAIN without RA bit errors as "no such host"
  1605  // and not "server misbehaving"
  1606  //
  1607  // Issue 25336: verify that NXDOMAIN errors fail fast.
  1608  //
  1609  // Issue 27525: verify that empty answers fail fast.
  1610  func TestNoSuchHost(t *testing.T) {
  1611  	tests := []struct {
  1612  		name string
  1613  		f    func(string, string, dnsmessage.Message, time.Time) (dnsmessage.Message, error)
  1614  	}{
  1615  		{
  1616  			"NXDOMAIN",
  1617  			func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1618  				return dnsmessage.Message{
  1619  					Header: dnsmessage.Header{
  1620  						ID:                 q.ID,
  1621  						Response:           true,
  1622  						RCode:              dnsmessage.RCodeNameError,
  1623  						RecursionAvailable: false,
  1624  					},
  1625  					Questions: q.Questions,
  1626  				}, nil
  1627  			},
  1628  		},
  1629  		{
  1630  			"no answers",
  1631  			func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1632  				return dnsmessage.Message{
  1633  					Header: dnsmessage.Header{
  1634  						ID:                 q.ID,
  1635  						Response:           true,
  1636  						RCode:              dnsmessage.RCodeSuccess,
  1637  						RecursionAvailable: false,
  1638  						Authoritative:      true,
  1639  					},
  1640  					Questions: q.Questions,
  1641  				}, nil
  1642  			},
  1643  		},
  1644  	}
  1645  
  1646  	for _, test := range tests {
  1647  		t.Run(test.name, func(t *testing.T) {
  1648  			lookups := 0
  1649  			err := lookupWithFake(fakeDNSServer{
  1650  				rh: func(n, s string, q dnsmessage.Message, d time.Time) (dnsmessage.Message, error) {
  1651  					lookups++
  1652  					return test.f(n, s, q, d)
  1653  				},
  1654  			}, ".", dnsmessage.TypeALL)
  1655  
  1656  			if lookups != 1 {
  1657  				t.Errorf("got %d lookups, wanted 1", lookups)
  1658  			}
  1659  
  1660  			if err == nil {
  1661  				t.Fatal("expected an error")
  1662  			}
  1663  			de, ok := err.(*DNSError)
  1664  			if !ok {
  1665  				t.Fatalf("err = %#v; wanted a *net.DNSError", err)
  1666  			}
  1667  			if de.Err != errNoSuchHost.Error() {
  1668  				t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error())
  1669  			}
  1670  			if !de.IsNotFound {
  1671  				t.Fatalf("IsNotFound = %v wanted true", de.IsNotFound)
  1672  			}
  1673  		})
  1674  	}
  1675  }
  1676  
  1677  // Issue 26573: verify that Conns that don't implement PacketConn are treated
  1678  // as streams even when udp was requested.
  1679  func TestDNSDialTCP(t *testing.T) {
  1680  	fake := fakeDNSServer{
  1681  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1682  			r := dnsmessage.Message{
  1683  				Header: dnsmessage.Header{
  1684  					ID:       q.Header.ID,
  1685  					Response: true,
  1686  					RCode:    dnsmessage.RCodeSuccess,
  1687  				},
  1688  				Questions: q.Questions,
  1689  			}
  1690  			return r, nil
  1691  		},
  1692  		alwaysTCP: true,
  1693  	}
  1694  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1695  	ctx := context.Background()
  1696  	_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useUDPOrTCP, false)
  1697  	if err != nil {
  1698  		t.Fatal("exchange failed:", err)
  1699  	}
  1700  }
  1701  
  1702  // Issue 27763: verify that two strings in one TXT record are concatenated.
  1703  func TestTXTRecordTwoStrings(t *testing.T) {
  1704  	fake := fakeDNSServer{
  1705  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1706  			r := dnsmessage.Message{
  1707  				Header: dnsmessage.Header{
  1708  					ID:       q.Header.ID,
  1709  					Response: true,
  1710  					RCode:    dnsmessage.RCodeSuccess,
  1711  				},
  1712  				Questions: q.Questions,
  1713  				Answers: []dnsmessage.Resource{
  1714  					{
  1715  						Header: dnsmessage.ResourceHeader{
  1716  							Name:  q.Questions[0].Name,
  1717  							Type:  dnsmessage.TypeA,
  1718  							Class: dnsmessage.ClassINET,
  1719  						},
  1720  						Body: &dnsmessage.TXTResource{
  1721  							TXT: []string{"string1 ", "string2"},
  1722  						},
  1723  					},
  1724  					{
  1725  						Header: dnsmessage.ResourceHeader{
  1726  							Name:  q.Questions[0].Name,
  1727  							Type:  dnsmessage.TypeA,
  1728  							Class: dnsmessage.ClassINET,
  1729  						},
  1730  						Body: &dnsmessage.TXTResource{
  1731  							TXT: []string{"onestring"},
  1732  						},
  1733  					},
  1734  				},
  1735  			}
  1736  			return r, nil
  1737  		},
  1738  	}
  1739  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1740  	txt, err := r.lookupTXT(context.Background(), "golang.org")
  1741  	if err != nil {
  1742  		t.Fatal("LookupTXT failed:", err)
  1743  	}
  1744  	if want := 2; len(txt) != want {
  1745  		t.Fatalf("len(txt), got %d, want %d", len(txt), want)
  1746  	}
  1747  	if want := "string1 string2"; txt[0] != want {
  1748  		t.Errorf("txt[0], got %q, want %q", txt[0], want)
  1749  	}
  1750  	if want := "onestring"; txt[1] != want {
  1751  		t.Errorf("txt[1], got %q, want %q", txt[1], want)
  1752  	}
  1753  }
  1754  
  1755  // Issue 29644: support single-request resolv.conf option in pure Go resolver.
  1756  // The A and AAAA queries will be sent sequentially, not in parallel.
  1757  func TestSingleRequestLookup(t *testing.T) {
  1758  	defer dnsWaitGroup.Wait()
  1759  	var (
  1760  		firstcalled int32
  1761  		ipv4        int32 = 1
  1762  		ipv6        int32 = 2
  1763  	)
  1764  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1765  		r := dnsmessage.Message{
  1766  			Header: dnsmessage.Header{
  1767  				ID:       q.ID,
  1768  				Response: true,
  1769  			},
  1770  			Questions: q.Questions,
  1771  		}
  1772  		for _, question := range q.Questions {
  1773  			switch question.Type {
  1774  			case dnsmessage.TypeA:
  1775  				if question.Name.String() == "slowipv4.example.net." {
  1776  					time.Sleep(10 * time.Millisecond)
  1777  				}
  1778  				if !atomic.CompareAndSwapInt32(&firstcalled, 0, ipv4) {
  1779  					t.Errorf("the A query was received after the AAAA query !")
  1780  				}
  1781  				r.Answers = append(r.Answers, dnsmessage.Resource{
  1782  					Header: dnsmessage.ResourceHeader{
  1783  						Name:   q.Questions[0].Name,
  1784  						Type:   dnsmessage.TypeA,
  1785  						Class:  dnsmessage.ClassINET,
  1786  						Length: 4,
  1787  					},
  1788  					Body: &dnsmessage.AResource{
  1789  						A: TestAddr,
  1790  					},
  1791  				})
  1792  			case dnsmessage.TypeAAAA:
  1793  				atomic.CompareAndSwapInt32(&firstcalled, 0, ipv6)
  1794  				r.Answers = append(r.Answers, dnsmessage.Resource{
  1795  					Header: dnsmessage.ResourceHeader{
  1796  						Name:   q.Questions[0].Name,
  1797  						Type:   dnsmessage.TypeAAAA,
  1798  						Class:  dnsmessage.ClassINET,
  1799  						Length: 16,
  1800  					},
  1801  					Body: &dnsmessage.AAAAResource{
  1802  						AAAA: TestAddr6,
  1803  					},
  1804  				})
  1805  			}
  1806  		}
  1807  		return r, nil
  1808  	}}
  1809  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1810  
  1811  	conf, err := newResolvConfTest()
  1812  	if err != nil {
  1813  		t.Fatal(err)
  1814  	}
  1815  	defer conf.teardown()
  1816  	if err := conf.writeAndUpdate([]string{"options single-request"}); err != nil {
  1817  		t.Fatal(err)
  1818  	}
  1819  	for _, name := range []string{"hostname.example.net", "slowipv4.example.net"} {
  1820  		firstcalled = 0
  1821  		_, err := r.LookupIPAddr(context.Background(), name)
  1822  		if err != nil {
  1823  			t.Error(err)
  1824  		}
  1825  	}
  1826  }
  1827  
  1828  // Issue 29358. Add configuration knob to force TCP-only DNS requests in the pure Go resolver.
  1829  func TestDNSUseTCP(t *testing.T) {
  1830  	fake := fakeDNSServer{
  1831  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1832  			r := dnsmessage.Message{
  1833  				Header: dnsmessage.Header{
  1834  					ID:       q.Header.ID,
  1835  					Response: true,
  1836  					RCode:    dnsmessage.RCodeSuccess,
  1837  				},
  1838  				Questions: q.Questions,
  1839  			}
  1840  			if n == "udp" {
  1841  				t.Fatal("udp protocol was used instead of tcp")
  1842  			}
  1843  			return r, nil
  1844  		},
  1845  	}
  1846  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1847  	ctx, cancel := context.WithCancel(context.Background())
  1848  	defer cancel()
  1849  	_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false)
  1850  	if err != nil {
  1851  		t.Fatal("exchange failed:", err)
  1852  	}
  1853  }
  1854  
  1855  func TestDNSUseTCPTruncated(t *testing.T) {
  1856  	fake := fakeDNSServer{
  1857  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1858  			r := dnsmessage.Message{
  1859  				Header: dnsmessage.Header{
  1860  					ID:        q.Header.ID,
  1861  					Response:  true,
  1862  					RCode:     dnsmessage.RCodeSuccess,
  1863  					Truncated: true,
  1864  				},
  1865  				Questions: q.Questions,
  1866  				Answers: []dnsmessage.Resource{
  1867  					{
  1868  						Header: dnsmessage.ResourceHeader{
  1869  							Name:   q.Questions[0].Name,
  1870  							Type:   dnsmessage.TypeA,
  1871  							Class:  dnsmessage.ClassINET,
  1872  							Length: 4,
  1873  						},
  1874  						Body: &dnsmessage.AResource{
  1875  							A: TestAddr,
  1876  						},
  1877  					},
  1878  				},
  1879  			}
  1880  			if n == "udp" {
  1881  				t.Fatal("udp protocol was used instead of tcp")
  1882  			}
  1883  			return r, nil
  1884  		},
  1885  	}
  1886  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1887  	ctx, cancel := context.WithCancel(context.Background())
  1888  	defer cancel()
  1889  	p, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second, useTCPOnly, false)
  1890  	if err != nil {
  1891  		t.Fatal("exchange failed:", err)
  1892  	}
  1893  	a, err := p.AllAnswers()
  1894  	if err != nil {
  1895  		t.Fatalf("unexpected error %v getting all answers", err)
  1896  	}
  1897  	if len(a) != 1 {
  1898  		t.Fatalf("got %d answers; want 1", len(a))
  1899  	}
  1900  }
  1901  
  1902  // Issue 34660: PTR response with non-PTR answers should ignore non-PTR
  1903  func TestPTRandNonPTR(t *testing.T) {
  1904  	fake := fakeDNSServer{
  1905  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1906  			r := dnsmessage.Message{
  1907  				Header: dnsmessage.Header{
  1908  					ID:       q.Header.ID,
  1909  					Response: true,
  1910  					RCode:    dnsmessage.RCodeSuccess,
  1911  				},
  1912  				Questions: q.Questions,
  1913  				Answers: []dnsmessage.Resource{
  1914  					{
  1915  						Header: dnsmessage.ResourceHeader{
  1916  							Name:  q.Questions[0].Name,
  1917  							Type:  dnsmessage.TypePTR,
  1918  							Class: dnsmessage.ClassINET,
  1919  						},
  1920  						Body: &dnsmessage.PTRResource{
  1921  							PTR: dnsmessage.MustNewName("golang.org."),
  1922  						},
  1923  					},
  1924  					{
  1925  						Header: dnsmessage.ResourceHeader{
  1926  							Name:  q.Questions[0].Name,
  1927  							Type:  dnsmessage.TypeTXT,
  1928  							Class: dnsmessage.ClassINET,
  1929  						},
  1930  						Body: &dnsmessage.TXTResource{
  1931  							TXT: []string{"PTR 8 6 60 ..."}, // fake RRSIG
  1932  						},
  1933  					},
  1934  				},
  1935  			}
  1936  			return r, nil
  1937  		},
  1938  	}
  1939  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1940  	names, err := r.lookupAddr(context.Background(), "192.0.2.123")
  1941  	if err != nil {
  1942  		t.Fatalf("LookupAddr: %v", err)
  1943  	}
  1944  	if want := []string{"golang.org."}; !slices.Equal(names, want) {
  1945  		t.Errorf("names = %q; want %q", names, want)
  1946  	}
  1947  }
  1948  
  1949  func TestCVE202133195(t *testing.T) {
  1950  	fake := fakeDNSServer{
  1951  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1952  			r := dnsmessage.Message{
  1953  				Header: dnsmessage.Header{
  1954  					ID:                 q.Header.ID,
  1955  					Response:           true,
  1956  					RCode:              dnsmessage.RCodeSuccess,
  1957  					RecursionAvailable: true,
  1958  				},
  1959  				Questions: q.Questions,
  1960  			}
  1961  			switch q.Questions[0].Type {
  1962  			case dnsmessage.TypeCNAME:
  1963  				r.Answers = []dnsmessage.Resource{}
  1964  			case dnsmessage.TypeA: // CNAME lookup uses a A/AAAA as a proxy
  1965  				r.Answers = append(r.Answers,
  1966  					dnsmessage.Resource{
  1967  						Header: dnsmessage.ResourceHeader{
  1968  							Name:   dnsmessage.MustNewName("<html>.golang.org."),
  1969  							Type:   dnsmessage.TypeA,
  1970  							Class:  dnsmessage.ClassINET,
  1971  							Length: 4,
  1972  						},
  1973  						Body: &dnsmessage.AResource{
  1974  							A: TestAddr,
  1975  						},
  1976  					},
  1977  				)
  1978  			case dnsmessage.TypeSRV:
  1979  				n := q.Questions[0].Name
  1980  				if n.String() == "_hdr._tcp.golang.org." {
  1981  					n = dnsmessage.MustNewName("<html>.golang.org.")
  1982  				}
  1983  				r.Answers = append(r.Answers,
  1984  					dnsmessage.Resource{
  1985  						Header: dnsmessage.ResourceHeader{
  1986  							Name:   n,
  1987  							Type:   dnsmessage.TypeSRV,
  1988  							Class:  dnsmessage.ClassINET,
  1989  							Length: 4,
  1990  						},
  1991  						Body: &dnsmessage.SRVResource{
  1992  							Target: dnsmessage.MustNewName("<html>.golang.org."),
  1993  						},
  1994  					},
  1995  					dnsmessage.Resource{
  1996  						Header: dnsmessage.ResourceHeader{
  1997  							Name:   n,
  1998  							Type:   dnsmessage.TypeSRV,
  1999  							Class:  dnsmessage.ClassINET,
  2000  							Length: 4,
  2001  						},
  2002  						Body: &dnsmessage.SRVResource{
  2003  							Target: dnsmessage.MustNewName("good.golang.org."),
  2004  						},
  2005  					},
  2006  				)
  2007  			case dnsmessage.TypeMX:
  2008  				r.Answers = append(r.Answers,
  2009  					dnsmessage.Resource{
  2010  						Header: dnsmessage.ResourceHeader{
  2011  							Name:   dnsmessage.MustNewName("<html>.golang.org."),
  2012  							Type:   dnsmessage.TypeMX,
  2013  							Class:  dnsmessage.ClassINET,
  2014  							Length: 4,
  2015  						},
  2016  						Body: &dnsmessage.MXResource{
  2017  							MX: dnsmessage.MustNewName("<html>.golang.org."),
  2018  						},
  2019  					},
  2020  					dnsmessage.Resource{
  2021  						Header: dnsmessage.ResourceHeader{
  2022  							Name:   dnsmessage.MustNewName("good.golang.org."),
  2023  							Type:   dnsmessage.TypeMX,
  2024  							Class:  dnsmessage.ClassINET,
  2025  							Length: 4,
  2026  						},
  2027  						Body: &dnsmessage.MXResource{
  2028  							MX: dnsmessage.MustNewName("good.golang.org."),
  2029  						},
  2030  					},
  2031  				)
  2032  			case dnsmessage.TypeNS:
  2033  				r.Answers = append(r.Answers,
  2034  					dnsmessage.Resource{
  2035  						Header: dnsmessage.ResourceHeader{
  2036  							Name:   dnsmessage.MustNewName("<html>.golang.org."),
  2037  							Type:   dnsmessage.TypeNS,
  2038  							Class:  dnsmessage.ClassINET,
  2039  							Length: 4,
  2040  						},
  2041  						Body: &dnsmessage.NSResource{
  2042  							NS: dnsmessage.MustNewName("<html>.golang.org."),
  2043  						},
  2044  					},
  2045  					dnsmessage.Resource{
  2046  						Header: dnsmessage.ResourceHeader{
  2047  							Name:   dnsmessage.MustNewName("good.golang.org."),
  2048  							Type:   dnsmessage.TypeNS,
  2049  							Class:  dnsmessage.ClassINET,
  2050  							Length: 4,
  2051  						},
  2052  						Body: &dnsmessage.NSResource{
  2053  							NS: dnsmessage.MustNewName("good.golang.org."),
  2054  						},
  2055  					},
  2056  				)
  2057  			case dnsmessage.TypePTR:
  2058  				r.Answers = append(r.Answers,
  2059  					dnsmessage.Resource{
  2060  						Header: dnsmessage.ResourceHeader{
  2061  							Name:   dnsmessage.MustNewName("<html>.golang.org."),
  2062  							Type:   dnsmessage.TypePTR,
  2063  							Class:  dnsmessage.ClassINET,
  2064  							Length: 4,
  2065  						},
  2066  						Body: &dnsmessage.PTRResource{
  2067  							PTR: dnsmessage.MustNewName("<html>.golang.org."),
  2068  						},
  2069  					},
  2070  					dnsmessage.Resource{
  2071  						Header: dnsmessage.ResourceHeader{
  2072  							Name:   dnsmessage.MustNewName("good.golang.org."),
  2073  							Type:   dnsmessage.TypePTR,
  2074  							Class:  dnsmessage.ClassINET,
  2075  							Length: 4,
  2076  						},
  2077  						Body: &dnsmessage.PTRResource{
  2078  							PTR: dnsmessage.MustNewName("good.golang.org."),
  2079  						},
  2080  					},
  2081  				)
  2082  			}
  2083  			return r, nil
  2084  		},
  2085  	}
  2086  
  2087  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  2088  	// Change the default resolver to match our manipulated resolver
  2089  	originalDefault := DefaultResolver
  2090  	DefaultResolver = &r
  2091  	defer func() { DefaultResolver = originalDefault }()
  2092  	// Redirect host file lookups.
  2093  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
  2094  	hostsFilePath = "testdata/hosts"
  2095  
  2096  	tests := []struct {
  2097  		name string
  2098  		f    func(*testing.T)
  2099  	}{
  2100  		{
  2101  			name: "CNAME",
  2102  			f: func(t *testing.T) {
  2103  				expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
  2104  				_, err := r.LookupCNAME(context.Background(), "golang.org")
  2105  				if err.Error() != expectedErr.Error() {
  2106  					t.Fatalf("unexpected error: %s", err)
  2107  				}
  2108  				_, err = LookupCNAME("golang.org")
  2109  				if err.Error() != expectedErr.Error() {
  2110  					t.Fatalf("unexpected error: %s", err)
  2111  				}
  2112  			},
  2113  		},
  2114  		{
  2115  			name: "SRV (bad record)",
  2116  			f: func(t *testing.T) {
  2117  				expected := []*SRV{
  2118  					{
  2119  						Target: "good.golang.org.",
  2120  					},
  2121  				}
  2122  				expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
  2123  				_, records, err := r.LookupSRV(context.Background(), "target", "tcp", "golang.org")
  2124  				if err.Error() != expectedErr.Error() {
  2125  					t.Fatalf("unexpected error: %s", err)
  2126  				}
  2127  				if !reflect.DeepEqual(records, expected) {
  2128  					t.Error("Unexpected record set")
  2129  				}
  2130  				_, records, err = LookupSRV("target", "tcp", "golang.org")
  2131  				if err.Error() != expectedErr.Error() {
  2132  					t.Errorf("unexpected error: %s", err)
  2133  				}
  2134  				if !reflect.DeepEqual(records, expected) {
  2135  					t.Error("Unexpected record set")
  2136  				}
  2137  			},
  2138  		},
  2139  		{
  2140  			name: "SRV (bad header)",
  2141  			f: func(t *testing.T) {
  2142  				_, _, err := r.LookupSRV(context.Background(), "hdr", "tcp", "golang.org.")
  2143  				if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
  2144  					t.Errorf("Resolver.LookupSRV returned unexpected error, got %q, want %q", err, expected)
  2145  				}
  2146  				_, _, err = LookupSRV("hdr", "tcp", "golang.org.")
  2147  				if expected := "lookup golang.org.: SRV header name is invalid"; err == nil || err.Error() != expected {
  2148  					t.Errorf("LookupSRV returned unexpected error, got %q, want %q", err, expected)
  2149  				}
  2150  			},
  2151  		},
  2152  		{
  2153  			name: "MX",
  2154  			f: func(t *testing.T) {
  2155  				expected := []*MX{
  2156  					{
  2157  						Host: "good.golang.org.",
  2158  					},
  2159  				}
  2160  				expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
  2161  				records, err := r.LookupMX(context.Background(), "golang.org")
  2162  				if err.Error() != expectedErr.Error() {
  2163  					t.Fatalf("unexpected error: %s", err)
  2164  				}
  2165  				if !reflect.DeepEqual(records, expected) {
  2166  					t.Error("Unexpected record set")
  2167  				}
  2168  				records, err = LookupMX("golang.org")
  2169  				if err.Error() != expectedErr.Error() {
  2170  					t.Fatalf("unexpected error: %s", err)
  2171  				}
  2172  				if !reflect.DeepEqual(records, expected) {
  2173  					t.Error("Unexpected record set")
  2174  				}
  2175  			},
  2176  		},
  2177  		{
  2178  			name: "NS",
  2179  			f: func(t *testing.T) {
  2180  				expected := []*NS{
  2181  					{
  2182  						Host: "good.golang.org.",
  2183  					},
  2184  				}
  2185  				expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "golang.org"}
  2186  				records, err := r.LookupNS(context.Background(), "golang.org")
  2187  				if err.Error() != expectedErr.Error() {
  2188  					t.Fatalf("unexpected error: %s", err)
  2189  				}
  2190  				if !reflect.DeepEqual(records, expected) {
  2191  					t.Error("Unexpected record set")
  2192  				}
  2193  				records, err = LookupNS("golang.org")
  2194  				if err.Error() != expectedErr.Error() {
  2195  					t.Fatalf("unexpected error: %s", err)
  2196  				}
  2197  				if !reflect.DeepEqual(records, expected) {
  2198  					t.Error("Unexpected record set")
  2199  				}
  2200  			},
  2201  		},
  2202  		{
  2203  			name: "Addr",
  2204  			f: func(t *testing.T) {
  2205  				expected := []string{"good.golang.org."}
  2206  				expectedErr := &DNSError{Err: errMalformedDNSRecordsDetail, Name: "192.0.2.42"}
  2207  				records, err := r.LookupAddr(context.Background(), "192.0.2.42")
  2208  				if err.Error() != expectedErr.Error() {
  2209  					t.Fatalf("unexpected error: %s", err)
  2210  				}
  2211  				if !slices.Equal(records, expected) {
  2212  					t.Error("Unexpected record set")
  2213  				}
  2214  				records, err = LookupAddr("192.0.2.42")
  2215  				if err.Error() != expectedErr.Error() {
  2216  					t.Fatalf("unexpected error: %s", err)
  2217  				}
  2218  				if !slices.Equal(records, expected) {
  2219  					t.Error("Unexpected record set")
  2220  				}
  2221  			},
  2222  		},
  2223  	}
  2224  
  2225  	for _, tc := range tests {
  2226  		t.Run(tc.name, tc.f)
  2227  	}
  2228  
  2229  }
  2230  
  2231  func TestNullMX(t *testing.T) {
  2232  	fake := fakeDNSServer{
  2233  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2234  			r := dnsmessage.Message{
  2235  				Header: dnsmessage.Header{
  2236  					ID:       q.Header.ID,
  2237  					Response: true,
  2238  					RCode:    dnsmessage.RCodeSuccess,
  2239  				},
  2240  				Questions: q.Questions,
  2241  				Answers: []dnsmessage.Resource{
  2242  					{
  2243  						Header: dnsmessage.ResourceHeader{
  2244  							Name:  q.Questions[0].Name,
  2245  							Type:  dnsmessage.TypeMX,
  2246  							Class: dnsmessage.ClassINET,
  2247  						},
  2248  						Body: &dnsmessage.MXResource{
  2249  							MX: dnsmessage.MustNewName("."),
  2250  						},
  2251  					},
  2252  				},
  2253  			}
  2254  			return r, nil
  2255  		},
  2256  	}
  2257  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  2258  	rrset, err := r.LookupMX(context.Background(), "golang.org")
  2259  	if err != nil {
  2260  		t.Fatalf("LookupMX: %v", err)
  2261  	}
  2262  	if want := []*MX{&MX{Host: "."}}; !reflect.DeepEqual(rrset, want) {
  2263  		records := []string{}
  2264  		for _, rr := range rrset {
  2265  			records = append(records, fmt.Sprintf("%v", rr))
  2266  		}
  2267  		t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0])
  2268  	}
  2269  }
  2270  
  2271  func TestRootNS(t *testing.T) {
  2272  	// See https://golang.org/issue/45715.
  2273  	fake := fakeDNSServer{
  2274  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2275  			r := dnsmessage.Message{
  2276  				Header: dnsmessage.Header{
  2277  					ID:       q.Header.ID,
  2278  					Response: true,
  2279  					RCode:    dnsmessage.RCodeSuccess,
  2280  				},
  2281  				Questions: q.Questions,
  2282  				Answers: []dnsmessage.Resource{
  2283  					{
  2284  						Header: dnsmessage.ResourceHeader{
  2285  							Name:  q.Questions[0].Name,
  2286  							Type:  dnsmessage.TypeNS,
  2287  							Class: dnsmessage.ClassINET,
  2288  						},
  2289  						Body: &dnsmessage.NSResource{
  2290  							NS: dnsmessage.MustNewName("i.root-servers.net."),
  2291  						},
  2292  					},
  2293  				},
  2294  			}
  2295  			return r, nil
  2296  		},
  2297  	}
  2298  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  2299  	rrset, err := r.LookupNS(context.Background(), ".")
  2300  	if err != nil {
  2301  		t.Fatalf("LookupNS: %v", err)
  2302  	}
  2303  	if want := []*NS{&NS{Host: "i.root-servers.net."}}; !reflect.DeepEqual(rrset, want) {
  2304  		records := []string{}
  2305  		for _, rr := range rrset {
  2306  			records = append(records, fmt.Sprintf("%v", rr))
  2307  		}
  2308  		t.Errorf("records = [%v]; want [%v]", strings.Join(records, " "), want[0])
  2309  	}
  2310  }
  2311  
  2312  func TestGoLookupIPCNAMEOrderHostsAliasesFilesOnlyMode(t *testing.T) {
  2313  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
  2314  	hostsFilePath = "testdata/aliases"
  2315  	mode := hostLookupFiles
  2316  
  2317  	for _, v := range lookupStaticHostAliasesTest {
  2318  		testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
  2319  	}
  2320  }
  2321  
  2322  func TestGoLookupIPCNAMEOrderHostsAliasesFilesDNSMode(t *testing.T) {
  2323  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
  2324  	hostsFilePath = "testdata/aliases"
  2325  	mode := hostLookupFilesDNS
  2326  
  2327  	for _, v := range lookupStaticHostAliasesTest {
  2328  		testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
  2329  	}
  2330  }
  2331  
  2332  var goLookupIPCNAMEOrderDNSFilesModeTests = []struct {
  2333  	lookup, res string
  2334  }{
  2335  	// 127.0.1.1
  2336  	{"invalid.invalid", "invalid.test"},
  2337  }
  2338  
  2339  func TestGoLookupIPCNAMEOrderHostsAliasesDNSFilesMode(t *testing.T) {
  2340  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
  2341  	hostsFilePath = "testdata/aliases"
  2342  	mode := hostLookupDNSFiles
  2343  
  2344  	for _, v := range goLookupIPCNAMEOrderDNSFilesModeTests {
  2345  		testGoLookupIPCNAMEOrderHostsAliases(t, mode, v.lookup, absDomainName(v.res))
  2346  	}
  2347  }
  2348  
  2349  func testGoLookupIPCNAMEOrderHostsAliases(t *testing.T, mode hostLookupOrder, lookup, lookupRes string) {
  2350  	fake := fakeDNSServer{
  2351  		rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2352  			var answers []dnsmessage.Resource
  2353  
  2354  			if mode != hostLookupDNSFiles {
  2355  				t.Fatal("received unexpected DNS query")
  2356  			}
  2357  
  2358  			return dnsmessage.Message{
  2359  				Header: dnsmessage.Header{
  2360  					ID:       q.Header.ID,
  2361  					Response: true,
  2362  				},
  2363  				Questions: []dnsmessage.Question{q.Questions[0]},
  2364  				Answers:   answers,
  2365  			}, nil
  2366  		},
  2367  	}
  2368  
  2369  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  2370  	ins := []string{lookup, absDomainName(lookup), strings.ToLower(lookup), strings.ToUpper(lookup)}
  2371  	for _, in := range ins {
  2372  		_, res, err := r.goLookupIPCNAMEOrder(context.Background(), "ip", in, mode, nil)
  2373  		if err != nil {
  2374  			t.Errorf("expected err == nil, but got error: %v", err)
  2375  		}
  2376  		if res.String() != lookupRes {
  2377  			t.Errorf("goLookupIPCNAMEOrder(%v): got %v, want %v", in, res, lookupRes)
  2378  		}
  2379  	}
  2380  }
  2381  
  2382  // Test that we advertise support for a larger DNS packet size.
  2383  // This isn't a great test as it just tests the dnsmessage package
  2384  // against itself.
  2385  func TestDNSPacketSize(t *testing.T) {
  2386  	t.Run("enabled", func(t *testing.T) {
  2387  		testDNSPacketSize(t, false)
  2388  	})
  2389  	t.Run("disabled", func(t *testing.T) {
  2390  		testDNSPacketSize(t, true)
  2391  	})
  2392  }
  2393  
  2394  func testDNSPacketSize(t *testing.T, disable bool) {
  2395  	fake := fakeDNSServer{
  2396  		rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2397  			if disable {
  2398  				if len(q.Additionals) > 0 {
  2399  					t.Error("unexpected additional record")
  2400  				}
  2401  			} else {
  2402  				if len(q.Additionals) == 0 {
  2403  					t.Error("missing EDNS record")
  2404  				} else if opt, ok := q.Additionals[0].Body.(*dnsmessage.OPTResource); !ok {
  2405  					t.Errorf("additional record type %T, expected OPTResource", q.Additionals[0])
  2406  				} else if len(opt.Options) != 0 {
  2407  					t.Errorf("found %d Options, expected none", len(opt.Options))
  2408  				} else {
  2409  					got := int(q.Additionals[0].Header.Class)
  2410  					t.Logf("EDNS packet size == %d", got)
  2411  					if got != maxDNSPacketSize {
  2412  						t.Errorf("EDNS packet size == %d, want %d", got, maxDNSPacketSize)
  2413  					}
  2414  				}
  2415  			}
  2416  
  2417  			// Hand back a dummy answer to verify that
  2418  			// LookupIPAddr completes.
  2419  			r := dnsmessage.Message{
  2420  				Header: dnsmessage.Header{
  2421  					ID:       q.Header.ID,
  2422  					Response: true,
  2423  					RCode:    dnsmessage.RCodeSuccess,
  2424  				},
  2425  				Questions: q.Questions,
  2426  			}
  2427  			if q.Questions[0].Type == dnsmessage.TypeA {
  2428  				r.Answers = []dnsmessage.Resource{
  2429  					{
  2430  						Header: dnsmessage.ResourceHeader{
  2431  							Name:   q.Questions[0].Name,
  2432  							Type:   dnsmessage.TypeA,
  2433  							Class:  dnsmessage.ClassINET,
  2434  							Length: 4,
  2435  						},
  2436  						Body: &dnsmessage.AResource{
  2437  							A: TestAddr,
  2438  						},
  2439  					},
  2440  				}
  2441  			}
  2442  			return r, nil
  2443  		},
  2444  	}
  2445  
  2446  	if disable {
  2447  		t.Setenv("GODEBUG", "netedns0=0")
  2448  	}
  2449  
  2450  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  2451  	if _, err := r.LookupIPAddr(context.Background(), "go.dev"); err != nil {
  2452  		t.Errorf("lookup failed: %v", err)
  2453  	}
  2454  }
  2455  
  2456  func TestLongDNSNames(t *testing.T) {
  2457  	const longDNSsuffix = ".go.dev."
  2458  	const longDNSsuffixNoEndingDot = ".go.dev"
  2459  
  2460  	var longDNSPrefix = strings.Repeat("verylongdomainlabel.", 20)
  2461  
  2462  	var longDNSNamesTests = []struct {
  2463  		req  string
  2464  		fail bool
  2465  	}{
  2466  		{req: longDNSPrefix[:255-len(longDNSsuffix)] + longDNSsuffix, fail: true},
  2467  		{req: longDNSPrefix[:254-len(longDNSsuffix)] + longDNSsuffix},
  2468  		{req: longDNSPrefix[:253-len(longDNSsuffix)] + longDNSsuffix},
  2469  
  2470  		{req: longDNSPrefix[:253-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot},
  2471  		{req: longDNSPrefix[:254-len(longDNSsuffixNoEndingDot)] + longDNSsuffixNoEndingDot, fail: true},
  2472  	}
  2473  
  2474  	fake := fakeDNSServer{
  2475  		rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2476  			r := dnsmessage.Message{
  2477  				Header: dnsmessage.Header{
  2478  					ID:       q.Header.ID,
  2479  					Response: true,
  2480  					RCode:    dnsmessage.RCodeSuccess,
  2481  				},
  2482  				Questions: q.Questions,
  2483  				Answers: []dnsmessage.Resource{
  2484  					{
  2485  						Header: dnsmessage.ResourceHeader{
  2486  							Name:  q.Questions[0].Name,
  2487  							Type:  q.Questions[0].Type,
  2488  							Class: dnsmessage.ClassINET,
  2489  						},
  2490  					},
  2491  				},
  2492  			}
  2493  
  2494  			switch q.Questions[0].Type {
  2495  			case dnsmessage.TypeA:
  2496  				r.Answers[0].Body = &dnsmessage.AResource{A: TestAddr}
  2497  			case dnsmessage.TypeAAAA:
  2498  				r.Answers[0].Body = &dnsmessage.AAAAResource{AAAA: TestAddr6}
  2499  			case dnsmessage.TypeTXT:
  2500  				r.Answers[0].Body = &dnsmessage.TXTResource{TXT: []string{"."}}
  2501  			case dnsmessage.TypeMX:
  2502  				r.Answers[0].Body = &dnsmessage.MXResource{
  2503  					MX: dnsmessage.MustNewName("go.dev."),
  2504  				}
  2505  			case dnsmessage.TypeNS:
  2506  				r.Answers[0].Body = &dnsmessage.NSResource{
  2507  					NS: dnsmessage.MustNewName("go.dev."),
  2508  				}
  2509  			case dnsmessage.TypeSRV:
  2510  				r.Answers[0].Body = &dnsmessage.SRVResource{
  2511  					Target: dnsmessage.MustNewName("go.dev."),
  2512  				}
  2513  			case dnsmessage.TypeCNAME:
  2514  				r.Answers[0].Body = &dnsmessage.CNAMEResource{
  2515  					CNAME: dnsmessage.MustNewName("fake.cname."),
  2516  				}
  2517  			default:
  2518  				panic("unknown dnsmessage type")
  2519  			}
  2520  
  2521  			return r, nil
  2522  		},
  2523  	}
  2524  
  2525  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  2526  
  2527  	methodTests := []string{"CNAME", "Host", "IP", "IPAddr", "MX", "NS", "NetIP", "SRV", "TXT"}
  2528  	query := func(t string, req string) error {
  2529  		switch t {
  2530  		case "CNAME":
  2531  			_, err := r.LookupCNAME(context.Background(), req)
  2532  			return err
  2533  		case "Host":
  2534  			_, err := r.LookupHost(context.Background(), req)
  2535  			return err
  2536  		case "IP":
  2537  			_, err := r.LookupIP(context.Background(), "ip", req)
  2538  			return err
  2539  		case "IPAddr":
  2540  			_, err := r.LookupIPAddr(context.Background(), req)
  2541  			return err
  2542  		case "MX":
  2543  			_, err := r.LookupMX(context.Background(), req)
  2544  			return err
  2545  		case "NS":
  2546  			_, err := r.LookupNS(context.Background(), req)
  2547  			return err
  2548  		case "NetIP":
  2549  			_, err := r.LookupNetIP(context.Background(), "ip", req)
  2550  			return err
  2551  		case "SRV":
  2552  			const service = "service"
  2553  			const proto = "proto"
  2554  			req = req[len(service)+len(proto)+4:]
  2555  			_, _, err := r.LookupSRV(context.Background(), service, proto, req)
  2556  			return err
  2557  		case "TXT":
  2558  			_, err := r.LookupTXT(context.Background(), req)
  2559  			return err
  2560  		}
  2561  		panic("unknown query method")
  2562  	}
  2563  
  2564  	for i, v := range longDNSNamesTests {
  2565  		for _, testName := range methodTests {
  2566  			err := query(testName, v.req)
  2567  			if v.fail {
  2568  				if err == nil {
  2569  					t.Errorf("%v: Lookup%v: unexpected success", i, testName)
  2570  					break
  2571  				}
  2572  
  2573  				expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: v.req, IsNotFound: true}
  2574  				var dnsErr *DNSError
  2575  				errors.As(err, &dnsErr)
  2576  				if dnsErr == nil || *dnsErr != expectedErr {
  2577  					t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err)
  2578  				}
  2579  				break
  2580  			}
  2581  			if err != nil {
  2582  				t.Errorf("%v: Lookup%v: unexpected error: %v", i, testName, err)
  2583  			}
  2584  		}
  2585  	}
  2586  }
  2587  
  2588  func TestDNSTrustAD(t *testing.T) {
  2589  	fake := fakeDNSServer{
  2590  		rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2591  			if q.Questions[0].Name.String() == "notrustad.go.dev." && q.Header.AuthenticData {
  2592  				t.Error("unexpected AD bit")
  2593  			}
  2594  
  2595  			if q.Questions[0].Name.String() == "trustad.go.dev." && !q.Header.AuthenticData {
  2596  				t.Error("expected AD bit")
  2597  			}
  2598  
  2599  			r := dnsmessage.Message{
  2600  				Header: dnsmessage.Header{
  2601  					ID:       q.Header.ID,
  2602  					Response: true,
  2603  					RCode:    dnsmessage.RCodeSuccess,
  2604  				},
  2605  				Questions: q.Questions,
  2606  			}
  2607  			if q.Questions[0].Type == dnsmessage.TypeA {
  2608  				r.Answers = []dnsmessage.Resource{
  2609  					{
  2610  						Header: dnsmessage.ResourceHeader{
  2611  							Name:   q.Questions[0].Name,
  2612  							Type:   dnsmessage.TypeA,
  2613  							Class:  dnsmessage.ClassINET,
  2614  							Length: 4,
  2615  						},
  2616  						Body: &dnsmessage.AResource{
  2617  							A: TestAddr,
  2618  						},
  2619  					},
  2620  				}
  2621  			}
  2622  
  2623  			return r, nil
  2624  		}}
  2625  
  2626  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  2627  
  2628  	conf, err := newResolvConfTest()
  2629  	if err != nil {
  2630  		t.Fatal(err)
  2631  	}
  2632  	defer conf.teardown()
  2633  
  2634  	err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1"})
  2635  	if err != nil {
  2636  		t.Fatal(err)
  2637  	}
  2638  
  2639  	if _, err := r.LookupIPAddr(context.Background(), "notrustad.go.dev"); err != nil {
  2640  		t.Errorf("lookup failed: %v", err)
  2641  	}
  2642  
  2643  	err = conf.writeAndUpdate([]string{"nameserver 127.0.0.1", "options trust-ad"})
  2644  	if err != nil {
  2645  		t.Fatal(err)
  2646  	}
  2647  
  2648  	if _, err := r.LookupIPAddr(context.Background(), "trustad.go.dev"); err != nil {
  2649  		t.Errorf("lookup failed: %v", err)
  2650  	}
  2651  }
  2652  
  2653  func TestDNSConfigNoReload(t *testing.T) {
  2654  	r := &Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (Conn, error) {
  2655  		if address != "192.0.2.1:53" {
  2656  			return nil, errors.New("configuration unexpectedly changed")
  2657  		}
  2658  		return fakeDNSServerSuccessful.DialContext(ctx, network, address)
  2659  	}}
  2660  
  2661  	conf, err := newResolvConfTest()
  2662  	if err != nil {
  2663  		t.Fatal(err)
  2664  	}
  2665  	defer conf.teardown()
  2666  
  2667  	err = conf.writeAndUpdateWithLastCheckedTime([]string{"nameserver 192.0.2.1", "options no-reload"}, time.Now().Add(-time.Hour))
  2668  	if err != nil {
  2669  		t.Fatal(err)
  2670  	}
  2671  
  2672  	if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil {
  2673  		t.Fatal(err)
  2674  	}
  2675  
  2676  	err = conf.write([]string{"nameserver 192.0.2.200"})
  2677  	if err != nil {
  2678  		t.Fatal(err)
  2679  	}
  2680  
  2681  	if _, err = r.LookupHost(context.Background(), "go.dev"); err != nil {
  2682  		t.Fatal(err)
  2683  	}
  2684  }
  2685  
  2686  func TestLookupOrderFilesNoSuchHost(t *testing.T) {
  2687  	defer func(orig string) { hostsFilePath = orig }(hostsFilePath)
  2688  	if runtime.GOOS != "openbsd" {
  2689  		defer setSystemNSS(getSystemNSS(), 0)
  2690  		setSystemNSS(nssStr(t, "hosts: files"), time.Hour)
  2691  	}
  2692  
  2693  	conf, err := newResolvConfTest()
  2694  	if err != nil {
  2695  		t.Fatal(err)
  2696  	}
  2697  	defer conf.teardown()
  2698  
  2699  	resolvConf := dnsConfig{servers: defaultNS}
  2700  	if runtime.GOOS == "openbsd" {
  2701  		// Set error to ErrNotExist, so that the hostLookupOrder
  2702  		// returns hostLookupFiles for openbsd.
  2703  		resolvConf.err = os.ErrNotExist
  2704  	}
  2705  
  2706  	if !conf.forceUpdateConf(&resolvConf, time.Now().Add(time.Hour)) {
  2707  		t.Fatal("failed to update resolv config")
  2708  	}
  2709  
  2710  	tmpFile := filepath.Join(t.TempDir(), "hosts")
  2711  	if err := os.WriteFile(tmpFile, []byte{}, 0660); err != nil {
  2712  		t.Fatal(err)
  2713  	}
  2714  	hostsFilePath = tmpFile
  2715  
  2716  	const testName = "test.invalid"
  2717  
  2718  	order, _ := systemConf().hostLookupOrder(DefaultResolver, testName)
  2719  	if order != hostLookupFiles {
  2720  		// skip test for systems which do not return hostLookupFiles
  2721  		t.Skipf("hostLookupOrder did not return hostLookupFiles")
  2722  	}
  2723  
  2724  	var lookupTests = []struct {
  2725  		name   string
  2726  		lookup func(name string) error
  2727  	}{
  2728  		{
  2729  			name: "Host",
  2730  			lookup: func(name string) error {
  2731  				_, err = DefaultResolver.LookupHost(context.Background(), name)
  2732  				return err
  2733  			},
  2734  		},
  2735  		{
  2736  			name: "IP",
  2737  			lookup: func(name string) error {
  2738  				_, err = DefaultResolver.LookupIP(context.Background(), "ip", name)
  2739  				return err
  2740  			},
  2741  		},
  2742  		{
  2743  			name: "IPAddr",
  2744  			lookup: func(name string) error {
  2745  				_, err = DefaultResolver.LookupIPAddr(context.Background(), name)
  2746  				return err
  2747  			},
  2748  		},
  2749  		{
  2750  			name: "NetIP",
  2751  			lookup: func(name string) error {
  2752  				_, err = DefaultResolver.LookupNetIP(context.Background(), "ip", name)
  2753  				return err
  2754  			},
  2755  		},
  2756  	}
  2757  
  2758  	for _, v := range lookupTests {
  2759  		err := v.lookup(testName)
  2760  
  2761  		if err == nil {
  2762  			t.Errorf("Lookup%v: unexpected success", v.name)
  2763  			continue
  2764  		}
  2765  
  2766  		expectedErr := DNSError{Err: errNoSuchHost.Error(), Name: testName, IsNotFound: true}
  2767  		var dnsErr *DNSError
  2768  		errors.As(err, &dnsErr)
  2769  		if dnsErr == nil || *dnsErr != expectedErr {
  2770  			t.Errorf("Lookup%v: unexpected error: %v", v.name, err)
  2771  		}
  2772  	}
  2773  }
  2774  
  2775  func TestExtendedRCode(t *testing.T) {
  2776  	fake := fakeDNSServer{
  2777  		rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  2778  			fraudSuccessCode := dnsmessage.RCodeSuccess | 1<<10
  2779  
  2780  			var edns0Hdr dnsmessage.ResourceHeader
  2781  			edns0Hdr.SetEDNS0(maxDNSPacketSize, fraudSuccessCode, false)
  2782  
  2783  			return dnsmessage.Message{
  2784  				Header: dnsmessage.Header{
  2785  					ID:       q.Header.ID,
  2786  					Response: true,
  2787  					RCode:    fraudSuccessCode,
  2788  				},
  2789  				Questions: []dnsmessage.Question{q.Questions[0]},
  2790  				Additionals: []dnsmessage.Resource{{
  2791  					Header: edns0Hdr,
  2792  					Body:   &dnsmessage.OPTResource{},
  2793  				}},
  2794  			}, nil
  2795  		},
  2796  	}
  2797  
  2798  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  2799  	_, _, err := r.tryOneName(context.Background(), getSystemDNSConfig(), "go.dev.", dnsmessage.TypeA)
  2800  	var dnsErr *DNSError
  2801  	if !(errors.As(err, &dnsErr) && dnsErr.Err == errServerMisbehaving.Error()) {
  2802  		t.Fatalf("r.tryOneName(): unexpected error: %v", err)
  2803  	}
  2804  }
  2805  

View as plain text