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

View as plain text