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