github.com/liujq9674git/golang-src-1.7@v0.0.0-20230517174348-17f6ec47f3f8/src/net/dnsclient_unix_test.go (about)

     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  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"fmt"
    12  	"internal/testenv"
    13  	"io/ioutil"
    14  	"os"
    15  	"path"
    16  	"reflect"
    17  	"strings"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
    24  const TestAddr uint32 = 0xc0000201
    25  
    26  var dnsTransportFallbackTests = []struct {
    27  	server  string
    28  	name    string
    29  	qtype   uint16
    30  	timeout int
    31  	rcode   int
    32  }{
    33  	// Querying "com." with qtype=255 usually makes an answer
    34  	// which requires more than 512 bytes.
    35  	{"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess},
    36  	{"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess},
    37  }
    38  
    39  func TestDNSTransportFallback(t *testing.T) {
    40  	testenv.MustHaveExternalNetwork(t)
    41  
    42  	for _, tt := range dnsTransportFallbackTests {
    43  		ctx, cancel := context.WithCancel(context.Background())
    44  		defer cancel()
    45  		msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second)
    46  		if err != nil {
    47  			t.Error(err)
    48  			continue
    49  		}
    50  		switch msg.rcode {
    51  		case tt.rcode, dnsRcodeServerFailure:
    52  		default:
    53  			t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode)
    54  			continue
    55  		}
    56  	}
    57  }
    58  
    59  // See RFC 6761 for further information about the reserved, pseudo
    60  // domain names.
    61  var specialDomainNameTests = []struct {
    62  	name  string
    63  	qtype uint16
    64  	rcode int
    65  }{
    66  	// Name resolution APIs and libraries should not recognize the
    67  	// followings as special.
    68  	{"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError},
    69  	{"test.", dnsTypeALL, dnsRcodeNameError},
    70  	{"example.com.", dnsTypeALL, dnsRcodeSuccess},
    71  
    72  	// Name resolution APIs and libraries should recognize the
    73  	// followings as special and should not send any queries.
    74  	// Though, we test those names here for verifying negative
    75  	// answers at DNS query-response interaction level.
    76  	{"localhost.", dnsTypeALL, dnsRcodeNameError},
    77  	{"invalid.", dnsTypeALL, dnsRcodeNameError},
    78  }
    79  
    80  func TestSpecialDomainName(t *testing.T) {
    81  	testenv.MustHaveExternalNetwork(t)
    82  
    83  	server := "8.8.8.8:53"
    84  	for _, tt := range specialDomainNameTests {
    85  		ctx, cancel := context.WithCancel(context.Background())
    86  		defer cancel()
    87  		msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second)
    88  		if err != nil {
    89  			t.Error(err)
    90  			continue
    91  		}
    92  		switch msg.rcode {
    93  		case tt.rcode, dnsRcodeServerFailure:
    94  		default:
    95  			t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode)
    96  			continue
    97  		}
    98  	}
    99  }
   100  
   101  // Issue 13705: don't try to resolve onion addresses, etc
   102  func TestAvoidDNSName(t *testing.T) {
   103  	tests := []struct {
   104  		name  string
   105  		avoid bool
   106  	}{
   107  		{"foo.com", false},
   108  		{"foo.com.", false},
   109  
   110  		{"foo.onion.", true},
   111  		{"foo.onion", true},
   112  		{"foo.ONION", true},
   113  		{"foo.ONION.", true},
   114  
   115  		// But do resolve *.local address; Issue 16739
   116  		{"foo.local.", false},
   117  		{"foo.local", false},
   118  		{"foo.LOCAL", false},
   119  		{"foo.LOCAL.", false},
   120  
   121  		{"", true}, // will be rejected earlier too
   122  
   123  		// Without stuff before onion/local, they're fine to
   124  		// use DNS. With a search path,
   125  		// "onion.vegegtables.com" can use DNS. Without a
   126  		// search path (or with a trailing dot), the queries
   127  		// are just kinda useless, but don't reveal anything
   128  		// private.
   129  		{"local", false},
   130  		{"onion", false},
   131  		{"local.", false},
   132  		{"onion.", false},
   133  	}
   134  	for _, tt := range tests {
   135  		got := avoidDNS(tt.name)
   136  		if got != tt.avoid {
   137  			t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid)
   138  		}
   139  	}
   140  }
   141  
   142  // Issue 13705: don't try to resolve onion addresses, etc
   143  func TestLookupTorOnion(t *testing.T) {
   144  	addrs, err := goLookupIP(context.Background(), "foo.onion")
   145  	if len(addrs) > 0 {
   146  		t.Errorf("unexpected addresses: %v", addrs)
   147  	}
   148  	if err != nil {
   149  		t.Fatalf("lookup = %v; want nil", err)
   150  	}
   151  }
   152  
   153  type resolvConfTest struct {
   154  	dir  string
   155  	path string
   156  	*resolverConfig
   157  }
   158  
   159  func newResolvConfTest() (*resolvConfTest, error) {
   160  	dir, err := ioutil.TempDir("", "go-resolvconftest")
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	conf := &resolvConfTest{
   165  		dir:            dir,
   166  		path:           path.Join(dir, "resolv.conf"),
   167  		resolverConfig: &resolvConf,
   168  	}
   169  	conf.initOnce.Do(conf.init)
   170  	return conf, nil
   171  }
   172  
   173  func (conf *resolvConfTest) writeAndUpdate(lines []string) error {
   174  	f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil {
   179  		f.Close()
   180  		return err
   181  	}
   182  	f.Close()
   183  	if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil {
   184  		return err
   185  	}
   186  	return nil
   187  }
   188  
   189  func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error {
   190  	dnsConf := dnsReadConfig(name)
   191  	conf.mu.Lock()
   192  	conf.dnsConfig = dnsConf
   193  	conf.mu.Unlock()
   194  	for i := 0; i < 5; i++ {
   195  		if conf.tryAcquireSema() {
   196  			conf.lastChecked = lastChecked
   197  			conf.releaseSema()
   198  			return nil
   199  		}
   200  	}
   201  	return fmt.Errorf("tryAcquireSema for %s failed", name)
   202  }
   203  
   204  func (conf *resolvConfTest) servers() []string {
   205  	conf.mu.RLock()
   206  	servers := conf.dnsConfig.servers
   207  	conf.mu.RUnlock()
   208  	return servers
   209  }
   210  
   211  func (conf *resolvConfTest) teardown() error {
   212  	err := conf.forceUpdate("/etc/resolv.conf", time.Time{})
   213  	os.RemoveAll(conf.dir)
   214  	return err
   215  }
   216  
   217  var updateResolvConfTests = []struct {
   218  	name    string   // query name
   219  	lines   []string // resolver configuration lines
   220  	servers []string // expected name servers
   221  }{
   222  	{
   223  		name:    "golang.org",
   224  		lines:   []string{"nameserver 8.8.8.8"},
   225  		servers: []string{"8.8.8.8:53"},
   226  	},
   227  	{
   228  		name:    "",
   229  		lines:   nil, // an empty resolv.conf should use defaultNS as name servers
   230  		servers: defaultNS,
   231  	},
   232  	{
   233  		name:    "www.example.com",
   234  		lines:   []string{"nameserver 8.8.4.4"},
   235  		servers: []string{"8.8.4.4:53"},
   236  	},
   237  }
   238  
   239  func TestUpdateResolvConf(t *testing.T) {
   240  	testenv.MustHaveExternalNetwork(t)
   241  
   242  	conf, err := newResolvConfTest()
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	defer conf.teardown()
   247  
   248  	for i, tt := range updateResolvConfTests {
   249  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   250  			t.Error(err)
   251  			continue
   252  		}
   253  		if tt.name != "" {
   254  			var wg sync.WaitGroup
   255  			const N = 10
   256  			wg.Add(N)
   257  			for j := 0; j < N; j++ {
   258  				go func(name string) {
   259  					defer wg.Done()
   260  					ips, err := goLookupIP(context.Background(), name)
   261  					if err != nil {
   262  						t.Error(err)
   263  						return
   264  					}
   265  					if len(ips) == 0 {
   266  						t.Errorf("no records for %s", name)
   267  						return
   268  					}
   269  				}(tt.name)
   270  			}
   271  			wg.Wait()
   272  		}
   273  		servers := conf.servers()
   274  		if !reflect.DeepEqual(servers, tt.servers) {
   275  			t.Errorf("#%d: got %v; want %v", i, servers, tt.servers)
   276  			continue
   277  		}
   278  	}
   279  }
   280  
   281  var goLookupIPWithResolverConfigTests = []struct {
   282  	name  string
   283  	lines []string // resolver configuration lines
   284  	error
   285  	a, aaaa bool // whether response contains A, AAAA-record
   286  }{
   287  	// no records, transport timeout
   288  	{
   289  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   290  		[]string{
   291  			"options timeout:1 attempts:1",
   292  			"nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
   293  		},
   294  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
   295  		false, false,
   296  	},
   297  
   298  	// no records, non-existent domain
   299  	{
   300  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   301  		[]string{
   302  			"options timeout:3 attempts:1",
   303  			"nameserver 8.8.8.8",
   304  		},
   305  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false},
   306  		false, false,
   307  	},
   308  
   309  	// a few A records, no AAAA records
   310  	{
   311  		"ipv4.google.com.",
   312  		[]string{
   313  			"nameserver 8.8.8.8",
   314  			"nameserver 2001:4860:4860::8888",
   315  		},
   316  		nil,
   317  		true, false,
   318  	},
   319  	{
   320  		"ipv4.google.com",
   321  		[]string{
   322  			"domain golang.org",
   323  			"nameserver 2001:4860:4860::8888",
   324  			"nameserver 8.8.8.8",
   325  		},
   326  		nil,
   327  		true, false,
   328  	},
   329  	{
   330  		"ipv4.google.com",
   331  		[]string{
   332  			"search x.golang.org y.golang.org",
   333  			"nameserver 2001:4860:4860::8888",
   334  			"nameserver 8.8.8.8",
   335  		},
   336  		nil,
   337  		true, false,
   338  	},
   339  
   340  	// no A records, a few AAAA records
   341  	{
   342  		"ipv6.google.com.",
   343  		[]string{
   344  			"nameserver 2001:4860:4860::8888",
   345  			"nameserver 8.8.8.8",
   346  		},
   347  		nil,
   348  		false, true,
   349  	},
   350  	{
   351  		"ipv6.google.com",
   352  		[]string{
   353  			"domain golang.org",
   354  			"nameserver 8.8.8.8",
   355  			"nameserver 2001:4860:4860::8888",
   356  		},
   357  		nil,
   358  		false, true,
   359  	},
   360  	{
   361  		"ipv6.google.com",
   362  		[]string{
   363  			"search x.golang.org y.golang.org",
   364  			"nameserver 8.8.8.8",
   365  			"nameserver 2001:4860:4860::8888",
   366  		},
   367  		nil,
   368  		false, true,
   369  	},
   370  
   371  	// both A and AAAA records
   372  	{
   373  		"hostname.as112.net", // see RFC 7534
   374  		[]string{
   375  			"domain golang.org",
   376  			"nameserver 2001:4860:4860::8888",
   377  			"nameserver 8.8.8.8",
   378  		},
   379  		nil,
   380  		true, true,
   381  	},
   382  	{
   383  		"hostname.as112.net", // see RFC 7534
   384  		[]string{
   385  			"search x.golang.org y.golang.org",
   386  			"nameserver 2001:4860:4860::8888",
   387  			"nameserver 8.8.8.8",
   388  		},
   389  		nil,
   390  		true, true,
   391  	},
   392  }
   393  
   394  func TestGoLookupIPWithResolverConfig(t *testing.T) {
   395  	testenv.MustHaveExternalNetwork(t)
   396  
   397  	conf, err := newResolvConfTest()
   398  	if err != nil {
   399  		t.Fatal(err)
   400  	}
   401  	defer conf.teardown()
   402  
   403  	for _, tt := range goLookupIPWithResolverConfigTests {
   404  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   405  			t.Error(err)
   406  			continue
   407  		}
   408  		addrs, err := goLookupIP(context.Background(), tt.name)
   409  		if err != nil {
   410  			// This test uses external network connectivity.
   411  			// We need to take care with errors on both
   412  			// DNS message exchange layer and DNS
   413  			// transport layer because goLookupIP may fail
   414  			// when the IP connectivty on node under test
   415  			// gets lost during its run.
   416  			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) {
   417  				t.Errorf("got %v; want %v", err, tt.error)
   418  			}
   419  			continue
   420  		}
   421  		if len(addrs) == 0 {
   422  			t.Errorf("no records for %s", tt.name)
   423  		}
   424  		if !tt.a && !tt.aaaa && len(addrs) > 0 {
   425  			t.Errorf("unexpected %v for %s", addrs, tt.name)
   426  		}
   427  		for _, addr := range addrs {
   428  			if !tt.a && addr.IP.To4() != nil {
   429  				t.Errorf("got %v; must not be IPv4 address", addr)
   430  			}
   431  			if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil {
   432  				t.Errorf("got %v; must not be IPv6 address", addr)
   433  			}
   434  		}
   435  	}
   436  }
   437  
   438  // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
   439  func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
   440  	testenv.MustHaveExternalNetwork(t)
   441  
   442  	// Add a config that simulates no dns servers being available.
   443  	conf, err := newResolvConfTest()
   444  	if err != nil {
   445  		t.Fatal(err)
   446  	}
   447  	if err := conf.writeAndUpdate([]string{}); err != nil {
   448  		t.Fatal(err)
   449  	}
   450  	// Redirect host file lookups.
   451  	defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
   452  	testHookHostsPath = "testdata/hosts"
   453  
   454  	for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
   455  		name := fmt.Sprintf("order %v", order)
   456  
   457  		// First ensure that we get an error when contacting a non-existent host.
   458  		_, err := goLookupIPOrder(context.Background(), "notarealhost", order)
   459  		if err == nil {
   460  			t.Errorf("%s: expected error while looking up name not in hosts file", name)
   461  			continue
   462  		}
   463  
   464  		// Now check that we get an address when the name appears in the hosts file.
   465  		addrs, err := goLookupIPOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
   466  		if err != nil {
   467  			t.Errorf("%s: expected to successfully lookup host entry", name)
   468  			continue
   469  		}
   470  		if len(addrs) != 1 {
   471  			t.Errorf("%s: expected exactly one result, but got %v", name, addrs)
   472  			continue
   473  		}
   474  		if got, want := addrs[0].String(), "127.1.1.1"; got != want {
   475  			t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want)
   476  		}
   477  	}
   478  	defer conf.teardown()
   479  }
   480  
   481  // Issue 12712.
   482  // When using search domains, return the error encountered
   483  // querying the original name instead of an error encountered
   484  // querying a generated name.
   485  func TestErrorForOriginalNameWhenSearching(t *testing.T) {
   486  	const fqdn = "doesnotexist.domain"
   487  
   488  	origTestHookDNSDialer := testHookDNSDialer
   489  	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
   490  
   491  	conf, err := newResolvConfTest()
   492  	if err != nil {
   493  		t.Fatal(err)
   494  	}
   495  	defer conf.teardown()
   496  
   497  	if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
   498  		t.Fatal(err)
   499  	}
   500  
   501  	d := &fakeDNSDialer{}
   502  	testHookDNSDialer = func() dnsDialer { return d }
   503  
   504  	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
   505  		r := &dnsMsg{
   506  			dnsMsgHdr: dnsMsgHdr{
   507  				id: q.id,
   508  			},
   509  		}
   510  
   511  		switch q.question[0].Name {
   512  		case fqdn + ".servfail.":
   513  			r.rcode = dnsRcodeServerFailure
   514  		default:
   515  			r.rcode = dnsRcodeNameError
   516  		}
   517  
   518  		return r, nil
   519  	}
   520  
   521  	_, err = goLookupIP(context.Background(), fqdn)
   522  	if err == nil {
   523  		t.Fatal("expected an error")
   524  	}
   525  
   526  	want := &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}
   527  	if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err {
   528  		t.Errorf("got %v; want %v", err, want)
   529  	}
   530  }
   531  
   532  // Issue 15434. If a name server gives a lame referral, continue to the next.
   533  func TestIgnoreLameReferrals(t *testing.T) {
   534  	origTestHookDNSDialer := testHookDNSDialer
   535  	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
   536  
   537  	conf, err := newResolvConfTest()
   538  	if err != nil {
   539  		t.Fatal(err)
   540  	}
   541  	defer conf.teardown()
   542  
   543  	if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
   544  		"nameserver 192.0.2.2"}); err != nil {
   545  		t.Fatal(err)
   546  	}
   547  
   548  	d := &fakeDNSDialer{}
   549  	testHookDNSDialer = func() dnsDialer { return d }
   550  
   551  	d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
   552  		t.Log(s, q)
   553  		r := &dnsMsg{
   554  			dnsMsgHdr: dnsMsgHdr{
   555  				id:       q.id,
   556  				response: true,
   557  			},
   558  			question: q.question,
   559  		}
   560  
   561  		if s == "192.0.2.2:53" {
   562  			r.recursion_available = true
   563  			if q.question[0].Qtype == dnsTypeA {
   564  				r.answer = []dnsRR{
   565  					&dnsRR_A{
   566  						Hdr: dnsRR_Header{
   567  							Name:     q.question[0].Name,
   568  							Rrtype:   dnsTypeA,
   569  							Class:    dnsClassINET,
   570  							Rdlength: 4,
   571  						},
   572  						A: TestAddr,
   573  					},
   574  				}
   575  			}
   576  		}
   577  
   578  		return r, nil
   579  	}
   580  
   581  	addrs, err := goLookupIP(context.Background(), "www.golang.org")
   582  	if err != nil {
   583  		t.Fatal(err)
   584  	}
   585  
   586  	if got := len(addrs); got != 1 {
   587  		t.Fatalf("got %d addresses, want 1", got)
   588  	}
   589  
   590  	if got, want := addrs[0].String(), "192.0.2.1"; got != want {
   591  		t.Fatalf("got address %v, want %v", got, want)
   592  	}
   593  }
   594  
   595  func BenchmarkGoLookupIP(b *testing.B) {
   596  	testHookUninstaller.Do(uninstallTestHooks)
   597  	ctx := context.Background()
   598  
   599  	for i := 0; i < b.N; i++ {
   600  		goLookupIP(ctx, "www.example.com")
   601  	}
   602  }
   603  
   604  func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
   605  	testHookUninstaller.Do(uninstallTestHooks)
   606  	ctx := context.Background()
   607  
   608  	for i := 0; i < b.N; i++ {
   609  		goLookupIP(ctx, "some.nonexistent")
   610  	}
   611  }
   612  
   613  func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
   614  	testHookUninstaller.Do(uninstallTestHooks)
   615  
   616  	conf, err := newResolvConfTest()
   617  	if err != nil {
   618  		b.Fatal(err)
   619  	}
   620  	defer conf.teardown()
   621  
   622  	lines := []string{
   623  		"nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
   624  		"nameserver 8.8.8.8",
   625  	}
   626  	if err := conf.writeAndUpdate(lines); err != nil {
   627  		b.Fatal(err)
   628  	}
   629  	ctx := context.Background()
   630  
   631  	for i := 0; i < b.N; i++ {
   632  		goLookupIP(ctx, "www.example.com")
   633  	}
   634  }
   635  
   636  type fakeDNSDialer struct {
   637  	// reply handler
   638  	rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
   639  }
   640  
   641  func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
   642  	return &fakeDNSConn{f.rh, s, time.Time{}}, nil
   643  }
   644  
   645  type fakeDNSConn struct {
   646  	rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
   647  	s  string
   648  	t  time.Time
   649  }
   650  
   651  func (f *fakeDNSConn) Close() error {
   652  	return nil
   653  }
   654  
   655  func (f *fakeDNSConn) SetDeadline(t time.Time) error {
   656  	f.t = t
   657  	return nil
   658  }
   659  
   660  func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
   661  	return f.rh(f.s, q, f.t)
   662  }
   663  
   664  // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
   665  func TestIgnoreDNSForgeries(t *testing.T) {
   666  	c, s := Pipe()
   667  	go func() {
   668  		b := make([]byte, 512)
   669  		n, err := s.Read(b)
   670  		if err != nil {
   671  			t.Fatal(err)
   672  		}
   673  
   674  		msg := &dnsMsg{}
   675  		if !msg.Unpack(b[:n]) {
   676  			t.Fatal("invalid DNS query")
   677  		}
   678  
   679  		s.Write([]byte("garbage DNS response packet"))
   680  
   681  		msg.response = true
   682  		msg.id++ // make invalid ID
   683  		b, ok := msg.Pack()
   684  		if !ok {
   685  			t.Fatal("failed to pack DNS response")
   686  		}
   687  		s.Write(b)
   688  
   689  		msg.id-- // restore original ID
   690  		msg.answer = []dnsRR{
   691  			&dnsRR_A{
   692  				Hdr: dnsRR_Header{
   693  					Name:     "www.example.com.",
   694  					Rrtype:   dnsTypeA,
   695  					Class:    dnsClassINET,
   696  					Rdlength: 4,
   697  				},
   698  				A: TestAddr,
   699  			},
   700  		}
   701  
   702  		b, ok = msg.Pack()
   703  		if !ok {
   704  			t.Fatal("failed to pack DNS response")
   705  		}
   706  		s.Write(b)
   707  	}()
   708  
   709  	msg := &dnsMsg{
   710  		dnsMsgHdr: dnsMsgHdr{
   711  			id: 42,
   712  		},
   713  		question: []dnsQuestion{
   714  			{
   715  				Name:   "www.example.com.",
   716  				Qtype:  dnsTypeA,
   717  				Qclass: dnsClassINET,
   718  			},
   719  		},
   720  	}
   721  
   722  	resp, err := dnsRoundTripUDP(c, msg)
   723  	if err != nil {
   724  		t.Fatalf("dnsRoundTripUDP failed: %v", err)
   725  	}
   726  
   727  	if got := resp.answer[0].(*dnsRR_A).A; got != TestAddr {
   728  		t.Errorf("got address %v, want %v", got, TestAddr)
   729  	}
   730  }
   731  
   732  // Issue 16865. If a name server times out, continue to the next.
   733  func TestRetryTimeout(t *testing.T) {
   734  	origTestHookDNSDialer := testHookDNSDialer
   735  	defer func() { testHookDNSDialer = origTestHookDNSDialer }()
   736  
   737  	conf, err := newResolvConfTest()
   738  	if err != nil {
   739  		t.Fatal(err)
   740  	}
   741  	defer conf.teardown()
   742  
   743  	if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will timeout
   744  		"nameserver 192.0.2.2"}); err != nil {
   745  		t.Fatal(err)
   746  	}
   747  
   748  	d := &fakeDNSDialer{}
   749  	testHookDNSDialer = func() dnsDialer { return d }
   750  
   751  	var deadline0 time.Time
   752  
   753  	d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
   754  		t.Log(s, q, deadline)
   755  
   756  		if deadline.IsZero() {
   757  			t.Error("zero deadline")
   758  		}
   759  
   760  		if s == "192.0.2.1:53" {
   761  			deadline0 = deadline
   762  			time.Sleep(10 * time.Millisecond)
   763  			return nil, errTimeout
   764  		}
   765  
   766  		if deadline == deadline0 {
   767  			t.Error("deadline didn't change")
   768  		}
   769  
   770  		r := &dnsMsg{
   771  			dnsMsgHdr: dnsMsgHdr{
   772  				id:                  q.id,
   773  				response:            true,
   774  				recursion_available: true,
   775  			},
   776  			question: q.question,
   777  			answer: []dnsRR{
   778  				&dnsRR_CNAME{
   779  					Hdr: dnsRR_Header{
   780  						Name:   q.question[0].Name,
   781  						Rrtype: dnsTypeCNAME,
   782  						Class:  dnsClassINET,
   783  					},
   784  					Cname: "golang.org",
   785  				},
   786  			},
   787  		}
   788  		return r, nil
   789  	}
   790  
   791  	_, err = goLookupCNAME(context.Background(), "www.golang.org")
   792  	if err != nil {
   793  		t.Fatal(err)
   794  	}
   795  
   796  	if deadline0.IsZero() {
   797  		t.Error("deadline0 still zero", deadline0)
   798  	}
   799  }