...
Run Format

Source file src/net/dnsclient_unix_test.go

Documentation: net

     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  	"errors"
    12  	"fmt"
    13  	"internal/poll"
    14  	"io/ioutil"
    15  	"os"
    16  	"path"
    17  	"reflect"
    18  	"strings"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"golang_org/x/net/dns/dnsmessage"
    24  )
    25  
    26  var goResolver = Resolver{PreferGo: true}
    27  
    28  // Test address from 192.0.2.0/24 block, reserved by RFC 5737 for documentation.
    29  var TestAddr = [4]byte{0xc0, 0x00, 0x02, 0x01}
    30  
    31  // Test address from 2001:db8::/32 block, reserved by RFC 3849 for documentation.
    32  var TestAddr6 = [16]byte{0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
    33  
    34  func mustNewName(name string) dnsmessage.Name {
    35  	nn, err := dnsmessage.NewName(name)
    36  	if err != nil {
    37  		panic(fmt.Sprint("creating name: ", err))
    38  	}
    39  	return nn
    40  }
    41  
    42  func mustQuestion(name string, qtype dnsmessage.Type, class dnsmessage.Class) dnsmessage.Question {
    43  	return dnsmessage.Question{
    44  		Name:  mustNewName(name),
    45  		Type:  qtype,
    46  		Class: class,
    47  	}
    48  }
    49  
    50  var dnsTransportFallbackTests = []struct {
    51  	server   string
    52  	question dnsmessage.Question
    53  	timeout  int
    54  	rcode    dnsmessage.RCode
    55  }{
    56  	// Querying "com." with qtype=255 usually makes an answer
    57  	// which requires more than 512 bytes.
    58  	{"8.8.8.8:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 2, dnsmessage.RCodeSuccess},
    59  	{"8.8.4.4:53", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), 4, dnsmessage.RCodeSuccess},
    60  }
    61  
    62  func TestDNSTransportFallback(t *testing.T) {
    63  	fake := fakeDNSServer{
    64  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
    65  			r := dnsmessage.Message{
    66  				Header: dnsmessage.Header{
    67  					ID:       q.Header.ID,
    68  					Response: true,
    69  					RCode:    dnsmessage.RCodeSuccess,
    70  				},
    71  				Questions: q.Questions,
    72  			}
    73  			if n == "udp" {
    74  				r.Header.Truncated = true
    75  			}
    76  			return r, nil
    77  		},
    78  	}
    79  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
    80  	for _, tt := range dnsTransportFallbackTests {
    81  		ctx, cancel := context.WithCancel(context.Background())
    82  		defer cancel()
    83  		_, h, err := r.exchange(ctx, tt.server, tt.question, time.Second)
    84  		if err != nil {
    85  			t.Error(err)
    86  			continue
    87  		}
    88  		if h.RCode != tt.rcode {
    89  			t.Errorf("got %v from %v; want %v", h.RCode, tt.server, tt.rcode)
    90  			continue
    91  		}
    92  	}
    93  }
    94  
    95  // See RFC 6761 for further information about the reserved, pseudo
    96  // domain names.
    97  var specialDomainNameTests = []struct {
    98  	question dnsmessage.Question
    99  	rcode    dnsmessage.RCode
   100  }{
   101  	// Name resolution APIs and libraries should not recognize the
   102  	// followings as special.
   103  	{mustQuestion("1.0.168.192.in-addr.arpa.", dnsmessage.TypePTR, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   104  	{mustQuestion("test.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   105  	{mustQuestion("example.com.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeSuccess},
   106  
   107  	// Name resolution APIs and libraries should recognize the
   108  	// followings as special and should not send any queries.
   109  	// Though, we test those names here for verifying negative
   110  	// answers at DNS query-response interaction level.
   111  	{mustQuestion("localhost.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   112  	{mustQuestion("invalid.", dnsmessage.TypeALL, dnsmessage.ClassINET), dnsmessage.RCodeNameError},
   113  }
   114  
   115  func TestSpecialDomainName(t *testing.T) {
   116  	fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   117  		r := dnsmessage.Message{
   118  			Header: dnsmessage.Header{
   119  				ID:       q.ID,
   120  				Response: true,
   121  			},
   122  			Questions: q.Questions,
   123  		}
   124  
   125  		switch q.Questions[0].Name.String() {
   126  		case "example.com.":
   127  			r.Header.RCode = dnsmessage.RCodeSuccess
   128  		default:
   129  			r.Header.RCode = dnsmessage.RCodeNameError
   130  		}
   131  
   132  		return r, nil
   133  	}}
   134  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   135  	server := "8.8.8.8:53"
   136  	for _, tt := range specialDomainNameTests {
   137  		ctx, cancel := context.WithCancel(context.Background())
   138  		defer cancel()
   139  		_, h, err := r.exchange(ctx, server, tt.question, 3*time.Second)
   140  		if err != nil {
   141  			t.Error(err)
   142  			continue
   143  		}
   144  		if h.RCode != tt.rcode {
   145  			t.Errorf("got %v from %v; want %v", h.RCode, server, tt.rcode)
   146  			continue
   147  		}
   148  	}
   149  }
   150  
   151  // Issue 13705: don't try to resolve onion addresses, etc
   152  func TestAvoidDNSName(t *testing.T) {
   153  	tests := []struct {
   154  		name  string
   155  		avoid bool
   156  	}{
   157  		{"foo.com", false},
   158  		{"foo.com.", false},
   159  
   160  		{"foo.onion.", true},
   161  		{"foo.onion", true},
   162  		{"foo.ONION", true},
   163  		{"foo.ONION.", true},
   164  
   165  		// But do resolve *.local address; Issue 16739
   166  		{"foo.local.", false},
   167  		{"foo.local", false},
   168  		{"foo.LOCAL", false},
   169  		{"foo.LOCAL.", false},
   170  
   171  		{"", true}, // will be rejected earlier too
   172  
   173  		// Without stuff before onion/local, they're fine to
   174  		// use DNS. With a search path,
   175  		// "onion.vegegtables.com" can use DNS. Without a
   176  		// search path (or with a trailing dot), the queries
   177  		// are just kinda useless, but don't reveal anything
   178  		// private.
   179  		{"local", false},
   180  		{"onion", false},
   181  		{"local.", false},
   182  		{"onion.", false},
   183  	}
   184  	for _, tt := range tests {
   185  		got := avoidDNS(tt.name)
   186  		if got != tt.avoid {
   187  			t.Errorf("avoidDNS(%q) = %v; want %v", tt.name, got, tt.avoid)
   188  		}
   189  	}
   190  }
   191  
   192  var fakeDNSServerSuccessful = fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   193  	r := dnsmessage.Message{
   194  		Header: dnsmessage.Header{
   195  			ID:       q.ID,
   196  			Response: true,
   197  		},
   198  		Questions: q.Questions,
   199  	}
   200  	if len(q.Questions) == 1 && q.Questions[0].Type == dnsmessage.TypeA {
   201  		r.Answers = []dnsmessage.Resource{
   202  			{
   203  				Header: dnsmessage.ResourceHeader{
   204  					Name:   q.Questions[0].Name,
   205  					Type:   dnsmessage.TypeA,
   206  					Class:  dnsmessage.ClassINET,
   207  					Length: 4,
   208  				},
   209  				Body: &dnsmessage.AResource{
   210  					A: TestAddr,
   211  				},
   212  			},
   213  		}
   214  	}
   215  	return r, nil
   216  }}
   217  
   218  // Issue 13705: don't try to resolve onion addresses, etc
   219  func TestLookupTorOnion(t *testing.T) {
   220  	defer dnsWaitGroup.Wait()
   221  	r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
   222  	addrs, err := r.LookupIPAddr(context.Background(), "foo.onion")
   223  	if err != nil {
   224  		t.Fatalf("lookup = %v; want nil", err)
   225  	}
   226  	if len(addrs) > 0 {
   227  		t.Errorf("unexpected addresses: %v", addrs)
   228  	}
   229  }
   230  
   231  type resolvConfTest struct {
   232  	dir  string
   233  	path string
   234  	*resolverConfig
   235  }
   236  
   237  func newResolvConfTest() (*resolvConfTest, error) {
   238  	dir, err := ioutil.TempDir("", "go-resolvconftest")
   239  	if err != nil {
   240  		return nil, err
   241  	}
   242  	conf := &resolvConfTest{
   243  		dir:            dir,
   244  		path:           path.Join(dir, "resolv.conf"),
   245  		resolverConfig: &resolvConf,
   246  	}
   247  	conf.initOnce.Do(conf.init)
   248  	return conf, nil
   249  }
   250  
   251  func (conf *resolvConfTest) writeAndUpdate(lines []string) error {
   252  	f, err := os.OpenFile(conf.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
   253  	if err != nil {
   254  		return err
   255  	}
   256  	if _, err := f.WriteString(strings.Join(lines, "\n")); err != nil {
   257  		f.Close()
   258  		return err
   259  	}
   260  	f.Close()
   261  	if err := conf.forceUpdate(conf.path, time.Now().Add(time.Hour)); err != nil {
   262  		return err
   263  	}
   264  	return nil
   265  }
   266  
   267  func (conf *resolvConfTest) forceUpdate(name string, lastChecked time.Time) error {
   268  	dnsConf := dnsReadConfig(name)
   269  	conf.mu.Lock()
   270  	conf.dnsConfig = dnsConf
   271  	conf.mu.Unlock()
   272  	for i := 0; i < 5; i++ {
   273  		if conf.tryAcquireSema() {
   274  			conf.lastChecked = lastChecked
   275  			conf.releaseSema()
   276  			return nil
   277  		}
   278  	}
   279  	return fmt.Errorf("tryAcquireSema for %s failed", name)
   280  }
   281  
   282  func (conf *resolvConfTest) servers() []string {
   283  	conf.mu.RLock()
   284  	servers := conf.dnsConfig.servers
   285  	conf.mu.RUnlock()
   286  	return servers
   287  }
   288  
   289  func (conf *resolvConfTest) teardown() error {
   290  	err := conf.forceUpdate("/etc/resolv.conf", time.Time{})
   291  	os.RemoveAll(conf.dir)
   292  	return err
   293  }
   294  
   295  var updateResolvConfTests = []struct {
   296  	name    string   // query name
   297  	lines   []string // resolver configuration lines
   298  	servers []string // expected name servers
   299  }{
   300  	{
   301  		name:    "golang.org",
   302  		lines:   []string{"nameserver 8.8.8.8"},
   303  		servers: []string{"8.8.8.8:53"},
   304  	},
   305  	{
   306  		name:    "",
   307  		lines:   nil, // an empty resolv.conf should use defaultNS as name servers
   308  		servers: defaultNS,
   309  	},
   310  	{
   311  		name:    "www.example.com",
   312  		lines:   []string{"nameserver 8.8.4.4"},
   313  		servers: []string{"8.8.4.4:53"},
   314  	},
   315  }
   316  
   317  func TestUpdateResolvConf(t *testing.T) {
   318  	defer dnsWaitGroup.Wait()
   319  
   320  	r := Resolver{PreferGo: true, Dial: fakeDNSServerSuccessful.DialContext}
   321  
   322  	conf, err := newResolvConfTest()
   323  	if err != nil {
   324  		t.Fatal(err)
   325  	}
   326  	defer conf.teardown()
   327  
   328  	for i, tt := range updateResolvConfTests {
   329  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   330  			t.Error(err)
   331  			continue
   332  		}
   333  		if tt.name != "" {
   334  			var wg sync.WaitGroup
   335  			const N = 10
   336  			wg.Add(N)
   337  			for j := 0; j < N; j++ {
   338  				go func(name string) {
   339  					defer wg.Done()
   340  					ips, err := r.LookupIPAddr(context.Background(), name)
   341  					if err != nil {
   342  						t.Error(err)
   343  						return
   344  					}
   345  					if len(ips) == 0 {
   346  						t.Errorf("no records for %s", name)
   347  						return
   348  					}
   349  				}(tt.name)
   350  			}
   351  			wg.Wait()
   352  		}
   353  		servers := conf.servers()
   354  		if !reflect.DeepEqual(servers, tt.servers) {
   355  			t.Errorf("#%d: got %v; want %v", i, servers, tt.servers)
   356  			continue
   357  		}
   358  	}
   359  }
   360  
   361  var goLookupIPWithResolverConfigTests = []struct {
   362  	name  string
   363  	lines []string // resolver configuration lines
   364  	error
   365  	a, aaaa bool // whether response contains A, AAAA-record
   366  }{
   367  	// no records, transport timeout
   368  	{
   369  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   370  		[]string{
   371  			"options timeout:1 attempts:1",
   372  			"nameserver 255.255.255.255", // please forgive us for abuse of limited broadcast address
   373  		},
   374  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "255.255.255.255:53", IsTimeout: true},
   375  		false, false,
   376  	},
   377  
   378  	// no records, non-existent domain
   379  	{
   380  		"jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j",
   381  		[]string{
   382  			"options timeout:3 attempts:1",
   383  			"nameserver 8.8.8.8",
   384  		},
   385  		&DNSError{Name: "jgahvsekduiv9bw4b3qhn4ykdfgj0493iohkrjfhdvhjiu4j", Server: "8.8.8.8:53", IsTimeout: false},
   386  		false, false,
   387  	},
   388  
   389  	// a few A records, no AAAA records
   390  	{
   391  		"ipv4.google.com.",
   392  		[]string{
   393  			"nameserver 8.8.8.8",
   394  			"nameserver 2001:4860:4860::8888",
   395  		},
   396  		nil,
   397  		true, false,
   398  	},
   399  	{
   400  		"ipv4.google.com",
   401  		[]string{
   402  			"domain golang.org",
   403  			"nameserver 2001:4860:4860::8888",
   404  			"nameserver 8.8.8.8",
   405  		},
   406  		nil,
   407  		true, false,
   408  	},
   409  	{
   410  		"ipv4.google.com",
   411  		[]string{
   412  			"search x.golang.org y.golang.org",
   413  			"nameserver 2001:4860:4860::8888",
   414  			"nameserver 8.8.8.8",
   415  		},
   416  		nil,
   417  		true, false,
   418  	},
   419  
   420  	// no A records, a few AAAA records
   421  	{
   422  		"ipv6.google.com.",
   423  		[]string{
   424  			"nameserver 2001:4860:4860::8888",
   425  			"nameserver 8.8.8.8",
   426  		},
   427  		nil,
   428  		false, true,
   429  	},
   430  	{
   431  		"ipv6.google.com",
   432  		[]string{
   433  			"domain golang.org",
   434  			"nameserver 8.8.8.8",
   435  			"nameserver 2001:4860:4860::8888",
   436  		},
   437  		nil,
   438  		false, true,
   439  	},
   440  	{
   441  		"ipv6.google.com",
   442  		[]string{
   443  			"search x.golang.org y.golang.org",
   444  			"nameserver 8.8.8.8",
   445  			"nameserver 2001:4860:4860::8888",
   446  		},
   447  		nil,
   448  		false, true,
   449  	},
   450  
   451  	// both A and AAAA records
   452  	{
   453  		"hostname.as112.net", // see RFC 7534
   454  		[]string{
   455  			"domain golang.org",
   456  			"nameserver 2001:4860:4860::8888",
   457  			"nameserver 8.8.8.8",
   458  		},
   459  		nil,
   460  		true, true,
   461  	},
   462  	{
   463  		"hostname.as112.net", // see RFC 7534
   464  		[]string{
   465  			"search x.golang.org y.golang.org",
   466  			"nameserver 2001:4860:4860::8888",
   467  			"nameserver 8.8.8.8",
   468  		},
   469  		nil,
   470  		true, true,
   471  	},
   472  }
   473  
   474  func TestGoLookupIPWithResolverConfig(t *testing.T) {
   475  	defer dnsWaitGroup.Wait()
   476  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   477  		switch s {
   478  		case "[2001:4860:4860::8888]:53", "8.8.8.8:53":
   479  			break
   480  		default:
   481  			time.Sleep(10 * time.Millisecond)
   482  			return dnsmessage.Message{}, poll.ErrTimeout
   483  		}
   484  		r := dnsmessage.Message{
   485  			Header: dnsmessage.Header{
   486  				ID:       q.ID,
   487  				Response: true,
   488  			},
   489  			Questions: q.Questions,
   490  		}
   491  		for _, question := range q.Questions {
   492  			switch question.Type {
   493  			case dnsmessage.TypeA:
   494  				switch question.Name.String() {
   495  				case "hostname.as112.net.":
   496  					break
   497  				case "ipv4.google.com.":
   498  					r.Answers = append(r.Answers, dnsmessage.Resource{
   499  						Header: dnsmessage.ResourceHeader{
   500  							Name:   q.Questions[0].Name,
   501  							Type:   dnsmessage.TypeA,
   502  							Class:  dnsmessage.ClassINET,
   503  							Length: 4,
   504  						},
   505  						Body: &dnsmessage.AResource{
   506  							A: TestAddr,
   507  						},
   508  					})
   509  				default:
   510  
   511  				}
   512  			case dnsmessage.TypeAAAA:
   513  				switch question.Name.String() {
   514  				case "hostname.as112.net.":
   515  					break
   516  				case "ipv6.google.com.":
   517  					r.Answers = append(r.Answers, dnsmessage.Resource{
   518  						Header: dnsmessage.ResourceHeader{
   519  							Name:   q.Questions[0].Name,
   520  							Type:   dnsmessage.TypeAAAA,
   521  							Class:  dnsmessage.ClassINET,
   522  							Length: 16,
   523  						},
   524  						Body: &dnsmessage.AAAAResource{
   525  							AAAA: TestAddr6,
   526  						},
   527  					})
   528  				}
   529  			}
   530  		}
   531  		return r, nil
   532  	}}
   533  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   534  
   535  	conf, err := newResolvConfTest()
   536  	if err != nil {
   537  		t.Fatal(err)
   538  	}
   539  	defer conf.teardown()
   540  
   541  	for _, tt := range goLookupIPWithResolverConfigTests {
   542  		if err := conf.writeAndUpdate(tt.lines); err != nil {
   543  			t.Error(err)
   544  			continue
   545  		}
   546  		addrs, err := r.LookupIPAddr(context.Background(), tt.name)
   547  		if err != nil {
   548  			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) {
   549  				t.Errorf("got %v; want %v", err, tt.error)
   550  			}
   551  			continue
   552  		}
   553  		if len(addrs) == 0 {
   554  			t.Errorf("no records for %s", tt.name)
   555  		}
   556  		if !tt.a && !tt.aaaa && len(addrs) > 0 {
   557  			t.Errorf("unexpected %v for %s", addrs, tt.name)
   558  		}
   559  		for _, addr := range addrs {
   560  			if !tt.a && addr.IP.To4() != nil {
   561  				t.Errorf("got %v; must not be IPv4 address", addr)
   562  			}
   563  			if !tt.aaaa && addr.IP.To16() != nil && addr.IP.To4() == nil {
   564  				t.Errorf("got %v; must not be IPv6 address", addr)
   565  			}
   566  		}
   567  	}
   568  }
   569  
   570  // Test that goLookupIPOrder falls back to the host file when no DNS servers are available.
   571  func TestGoLookupIPOrderFallbackToFile(t *testing.T) {
   572  	defer dnsWaitGroup.Wait()
   573  
   574  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, tm time.Time) (dnsmessage.Message, error) {
   575  		r := dnsmessage.Message{
   576  			Header: dnsmessage.Header{
   577  				ID:       q.ID,
   578  				Response: true,
   579  			},
   580  			Questions: q.Questions,
   581  		}
   582  		return r, nil
   583  	}}
   584  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   585  
   586  	// Add a config that simulates no dns servers being available.
   587  	conf, err := newResolvConfTest()
   588  	if err != nil {
   589  		t.Fatal(err)
   590  	}
   591  	if err := conf.writeAndUpdate([]string{}); err != nil {
   592  		t.Fatal(err)
   593  	}
   594  	// Redirect host file lookups.
   595  	defer func(orig string) { testHookHostsPath = orig }(testHookHostsPath)
   596  	testHookHostsPath = "testdata/hosts"
   597  
   598  	for _, order := range []hostLookupOrder{hostLookupFilesDNS, hostLookupDNSFiles} {
   599  		name := fmt.Sprintf("order %v", order)
   600  
   601  		// First ensure that we get an error when contacting a non-existent host.
   602  		_, _, err := r.goLookupIPCNAMEOrder(context.Background(), "notarealhost", order)
   603  		if err == nil {
   604  			t.Errorf("%s: expected error while looking up name not in hosts file", name)
   605  			continue
   606  		}
   607  
   608  		// Now check that we get an address when the name appears in the hosts file.
   609  		addrs, _, err := r.goLookupIPCNAMEOrder(context.Background(), "thor", order) // entry is in "testdata/hosts"
   610  		if err != nil {
   611  			t.Errorf("%s: expected to successfully lookup host entry", name)
   612  			continue
   613  		}
   614  		if len(addrs) != 1 {
   615  			t.Errorf("%s: expected exactly one result, but got %v", name, addrs)
   616  			continue
   617  		}
   618  		if got, want := addrs[0].String(), "127.1.1.1"; got != want {
   619  			t.Errorf("%s: address doesn't match expectation. got %v, want %v", name, got, want)
   620  		}
   621  	}
   622  	defer conf.teardown()
   623  }
   624  
   625  // Issue 12712.
   626  // When using search domains, return the error encountered
   627  // querying the original name instead of an error encountered
   628  // querying a generated name.
   629  func TestErrorForOriginalNameWhenSearching(t *testing.T) {
   630  	defer dnsWaitGroup.Wait()
   631  
   632  	const fqdn = "doesnotexist.domain"
   633  
   634  	conf, err := newResolvConfTest()
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	defer conf.teardown()
   639  
   640  	if err := conf.writeAndUpdate([]string{"search servfail"}); err != nil {
   641  		t.Fatal(err)
   642  	}
   643  
   644  	fake := fakeDNSServer{rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   645  		r := dnsmessage.Message{
   646  			Header: dnsmessage.Header{
   647  				ID:       q.ID,
   648  				Response: true,
   649  			},
   650  			Questions: q.Questions,
   651  		}
   652  
   653  		switch q.Questions[0].Name.String() {
   654  		case fqdn + ".servfail.":
   655  			r.Header.RCode = dnsmessage.RCodeServerFailure
   656  		default:
   657  			r.Header.RCode = dnsmessage.RCodeNameError
   658  		}
   659  
   660  		return r, nil
   661  	}}
   662  
   663  	cases := []struct {
   664  		strictErrors bool
   665  		wantErr      *DNSError
   666  	}{
   667  		{true, &DNSError{Name: fqdn, Err: "server misbehaving", IsTemporary: true}},
   668  		{false, &DNSError{Name: fqdn, Err: errNoSuchHost.Error()}},
   669  	}
   670  	for _, tt := range cases {
   671  		r := Resolver{PreferGo: true, StrictErrors: tt.strictErrors, Dial: fake.DialContext}
   672  		_, err = r.LookupIPAddr(context.Background(), fqdn)
   673  		if err == nil {
   674  			t.Fatal("expected an error")
   675  		}
   676  
   677  		want := tt.wantErr
   678  		if err, ok := err.(*DNSError); !ok || err.Name != want.Name || err.Err != want.Err || err.IsTemporary != want.IsTemporary {
   679  			t.Errorf("got %v; want %v", err, want)
   680  		}
   681  	}
   682  }
   683  
   684  // Issue 15434. If a name server gives a lame referral, continue to the next.
   685  func TestIgnoreLameReferrals(t *testing.T) {
   686  	defer dnsWaitGroup.Wait()
   687  
   688  	conf, err := newResolvConfTest()
   689  	if err != nil {
   690  		t.Fatal(err)
   691  	}
   692  	defer conf.teardown()
   693  
   694  	if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
   695  		"nameserver 192.0.2.2"}); err != nil {
   696  		t.Fatal(err)
   697  	}
   698  
   699  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
   700  		t.Log(s, q)
   701  		r := dnsmessage.Message{
   702  			Header: dnsmessage.Header{
   703  				ID:       q.ID,
   704  				Response: true,
   705  			},
   706  			Questions: q.Questions,
   707  		}
   708  
   709  		if s == "192.0.2.2:53" {
   710  			r.Header.RecursionAvailable = true
   711  			if q.Questions[0].Type == dnsmessage.TypeA {
   712  				r.Answers = []dnsmessage.Resource{
   713  					{
   714  						Header: dnsmessage.ResourceHeader{
   715  							Name:   q.Questions[0].Name,
   716  							Type:   dnsmessage.TypeA,
   717  							Class:  dnsmessage.ClassINET,
   718  							Length: 4,
   719  						},
   720  						Body: &dnsmessage.AResource{
   721  							A: TestAddr,
   722  						},
   723  					},
   724  				}
   725  			}
   726  		}
   727  
   728  		return r, nil
   729  	}}
   730  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
   731  
   732  	addrs, err := r.LookupIPAddr(context.Background(), "www.golang.org")
   733  	if err != nil {
   734  		t.Fatal(err)
   735  	}
   736  
   737  	if got := len(addrs); got != 1 {
   738  		t.Fatalf("got %d addresses, want 1", got)
   739  	}
   740  
   741  	if got, want := addrs[0].String(), "192.0.2.1"; got != want {
   742  		t.Fatalf("got address %v, want %v", got, want)
   743  	}
   744  }
   745  
   746  func BenchmarkGoLookupIP(b *testing.B) {
   747  	testHookUninstaller.Do(uninstallTestHooks)
   748  	ctx := context.Background()
   749  	b.ReportAllocs()
   750  
   751  	for i := 0; i < b.N; i++ {
   752  		goResolver.LookupIPAddr(ctx, "www.example.com")
   753  	}
   754  }
   755  
   756  func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
   757  	testHookUninstaller.Do(uninstallTestHooks)
   758  	ctx := context.Background()
   759  	b.ReportAllocs()
   760  
   761  	for i := 0; i < b.N; i++ {
   762  		goResolver.LookupIPAddr(ctx, "some.nonexistent")
   763  	}
   764  }
   765  
   766  func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
   767  	testHookUninstaller.Do(uninstallTestHooks)
   768  
   769  	conf, err := newResolvConfTest()
   770  	if err != nil {
   771  		b.Fatal(err)
   772  	}
   773  	defer conf.teardown()
   774  
   775  	lines := []string{
   776  		"nameserver 203.0.113.254", // use TEST-NET-3 block, see RFC 5737
   777  		"nameserver 8.8.8.8",
   778  	}
   779  	if err := conf.writeAndUpdate(lines); err != nil {
   780  		b.Fatal(err)
   781  	}
   782  	ctx := context.Background()
   783  	b.ReportAllocs()
   784  
   785  	for i := 0; i < b.N; i++ {
   786  		goResolver.LookupIPAddr(ctx, "www.example.com")
   787  	}
   788  }
   789  
   790  type fakeDNSServer struct {
   791  	rh        func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error)
   792  	alwaysTCP bool
   793  }
   794  
   795  func (server *fakeDNSServer) DialContext(_ context.Context, n, s string) (Conn, error) {
   796  	if server.alwaysTCP || n == "tcp" || n == "tcp4" || n == "tcp6" {
   797  		return &fakeDNSConn{tcp: true, server: server, n: n, s: s}, nil
   798  	}
   799  	return &fakeDNSPacketConn{fakeDNSConn: fakeDNSConn{tcp: false, server: server, n: n, s: s}}, nil
   800  }
   801  
   802  type fakeDNSConn struct {
   803  	Conn
   804  	tcp    bool
   805  	server *fakeDNSServer
   806  	n      string
   807  	s      string
   808  	q      dnsmessage.Message
   809  	t      time.Time
   810  	buf    []byte
   811  }
   812  
   813  func (f *fakeDNSConn) Close() error {
   814  	return nil
   815  }
   816  
   817  func (f *fakeDNSConn) Read(b []byte) (int, error) {
   818  	if len(f.buf) > 0 {
   819  		n := copy(b, f.buf)
   820  		f.buf = f.buf[n:]
   821  		return n, nil
   822  	}
   823  
   824  	resp, err := f.server.rh(f.n, f.s, f.q, f.t)
   825  	if err != nil {
   826  		return 0, err
   827  	}
   828  
   829  	bb := make([]byte, 2, 514)
   830  	bb, err = resp.AppendPack(bb)
   831  	if err != nil {
   832  		return 0, fmt.Errorf("cannot marshal DNS message: %v", err)
   833  	}
   834  
   835  	if f.tcp {
   836  		l := len(bb) - 2
   837  		bb[0] = byte(l >> 8)
   838  		bb[1] = byte(l)
   839  		f.buf = bb
   840  		return f.Read(b)
   841  	}
   842  
   843  	bb = bb[2:]
   844  	if len(b) < len(bb) {
   845  		return 0, errors.New("read would fragment DNS message")
   846  	}
   847  
   848  	copy(b, bb)
   849  	return len(bb), nil
   850  }
   851  
   852  func (f *fakeDNSConn) Write(b []byte) (int, error) {
   853  	if f.tcp && len(b) >= 2 {
   854  		b = b[2:]
   855  	}
   856  	if f.q.Unpack(b) != nil {
   857  		return 0, fmt.Errorf("cannot unmarshal DNS message fake %s (%d)", f.n, len(b))
   858  	}
   859  	return len(b), nil
   860  }
   861  
   862  func (f *fakeDNSConn) SetDeadline(t time.Time) error {
   863  	f.t = t
   864  	return nil
   865  }
   866  
   867  type fakeDNSPacketConn struct {
   868  	PacketConn
   869  	fakeDNSConn
   870  }
   871  
   872  func (f *fakeDNSPacketConn) SetDeadline(t time.Time) error {
   873  	return f.fakeDNSConn.SetDeadline(t)
   874  }
   875  
   876  func (f *fakeDNSPacketConn) Close() error {
   877  	return f.fakeDNSConn.Close()
   878  }
   879  
   880  // UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
   881  func TestIgnoreDNSForgeries(t *testing.T) {
   882  	c, s := Pipe()
   883  	go func() {
   884  		b := make([]byte, 512)
   885  		n, err := s.Read(b)
   886  		if err != nil {
   887  			t.Error(err)
   888  			return
   889  		}
   890  
   891  		var msg dnsmessage.Message
   892  		if msg.Unpack(b[:n]) != nil {
   893  			t.Error("invalid DNS query:", err)
   894  			return
   895  		}
   896  
   897  		s.Write([]byte("garbage DNS response packet"))
   898  
   899  		msg.Header.Response = true
   900  		msg.Header.ID++ // make invalid ID
   901  
   902  		if b, err = msg.Pack(); err != nil {
   903  			t.Error("failed to pack DNS response:", err)
   904  			return
   905  		}
   906  		s.Write(b)
   907  
   908  		msg.Header.ID-- // restore original ID
   909  		msg.Answers = []dnsmessage.Resource{
   910  			{
   911  				Header: dnsmessage.ResourceHeader{
   912  					Name:   mustNewName("www.example.com."),
   913  					Type:   dnsmessage.TypeA,
   914  					Class:  dnsmessage.ClassINET,
   915  					Length: 4,
   916  				},
   917  				Body: &dnsmessage.AResource{
   918  					A: TestAddr,
   919  				},
   920  			},
   921  		}
   922  
   923  		b, err = msg.Pack()
   924  		if err != nil {
   925  			t.Error("failed to pack DNS response:", err)
   926  			return
   927  		}
   928  		s.Write(b)
   929  	}()
   930  
   931  	msg := dnsmessage.Message{
   932  		Header: dnsmessage.Header{
   933  			ID: 42,
   934  		},
   935  		Questions: []dnsmessage.Question{
   936  			{
   937  				Name:  mustNewName("www.example.com."),
   938  				Type:  dnsmessage.TypeA,
   939  				Class: dnsmessage.ClassINET,
   940  			},
   941  		},
   942  	}
   943  
   944  	b, err := msg.Pack()
   945  	if err != nil {
   946  		t.Fatal("Pack failed:", err)
   947  	}
   948  
   949  	p, _, err := dnsPacketRoundTrip(c, 42, msg.Questions[0], b)
   950  	if err != nil {
   951  		t.Fatalf("dnsPacketRoundTrip failed: %v", err)
   952  	}
   953  
   954  	p.SkipAllQuestions()
   955  	as, err := p.AllAnswers()
   956  	if err != nil {
   957  		t.Fatal("AllAnswers failed:", err)
   958  	}
   959  	if got := as[0].Body.(*dnsmessage.AResource).A; got != TestAddr {
   960  		t.Errorf("got address %v, want %v", got, TestAddr)
   961  	}
   962  }
   963  
   964  // Issue 16865. If a name server times out, continue to the next.
   965  func TestRetryTimeout(t *testing.T) {
   966  	defer dnsWaitGroup.Wait()
   967  
   968  	conf, err := newResolvConfTest()
   969  	if err != nil {
   970  		t.Fatal(err)
   971  	}
   972  	defer conf.teardown()
   973  
   974  	testConf := []string{
   975  		"nameserver 192.0.2.1", // the one that will timeout
   976  		"nameserver 192.0.2.2",
   977  	}
   978  	if err := conf.writeAndUpdate(testConf); err != nil {
   979  		t.Fatal(err)
   980  	}
   981  
   982  	var deadline0 time.Time
   983  
   984  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
   985  		t.Log(s, q, deadline)
   986  
   987  		if deadline.IsZero() {
   988  			t.Error("zero deadline")
   989  		}
   990  
   991  		if s == "192.0.2.1:53" {
   992  			deadline0 = deadline
   993  			time.Sleep(10 * time.Millisecond)
   994  			return dnsmessage.Message{}, poll.ErrTimeout
   995  		}
   996  
   997  		if deadline.Equal(deadline0) {
   998  			t.Error("deadline didn't change")
   999  		}
  1000  
  1001  		return mockTXTResponse(q), nil
  1002  	}}
  1003  	r := &Resolver{PreferGo: true, Dial: fake.DialContext}
  1004  
  1005  	_, err = r.LookupTXT(context.Background(), "www.golang.org")
  1006  	if err != nil {
  1007  		t.Fatal(err)
  1008  	}
  1009  
  1010  	if deadline0.IsZero() {
  1011  		t.Error("deadline0 still zero", deadline0)
  1012  	}
  1013  }
  1014  
  1015  func TestRotate(t *testing.T) {
  1016  	// without rotation, always uses the first server
  1017  	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"})
  1018  
  1019  	// with rotation, rotates through back to first
  1020  	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"})
  1021  }
  1022  
  1023  func testRotate(t *testing.T, rotate bool, nameservers, wantServers []string) {
  1024  	defer dnsWaitGroup.Wait()
  1025  
  1026  	conf, err := newResolvConfTest()
  1027  	if err != nil {
  1028  		t.Fatal(err)
  1029  	}
  1030  	defer conf.teardown()
  1031  
  1032  	var confLines []string
  1033  	for _, ns := range nameservers {
  1034  		confLines = append(confLines, "nameserver "+ns)
  1035  	}
  1036  	if rotate {
  1037  		confLines = append(confLines, "options rotate")
  1038  	}
  1039  
  1040  	if err := conf.writeAndUpdate(confLines); err != nil {
  1041  		t.Fatal(err)
  1042  	}
  1043  
  1044  	var usedServers []string
  1045  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1046  		usedServers = append(usedServers, s)
  1047  		return mockTXTResponse(q), nil
  1048  	}}
  1049  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1050  
  1051  	// len(nameservers) + 1 to allow rotation to get back to start
  1052  	for i := 0; i < len(nameservers)+1; i++ {
  1053  		if _, err := r.LookupTXT(context.Background(), "www.golang.org"); err != nil {
  1054  			t.Fatal(err)
  1055  		}
  1056  	}
  1057  
  1058  	if !reflect.DeepEqual(usedServers, wantServers) {
  1059  		t.Errorf("rotate=%t got used servers:\n%v\nwant:\n%v", rotate, usedServers, wantServers)
  1060  	}
  1061  }
  1062  
  1063  func mockTXTResponse(q dnsmessage.Message) dnsmessage.Message {
  1064  	r := dnsmessage.Message{
  1065  		Header: dnsmessage.Header{
  1066  			ID:                 q.ID,
  1067  			Response:           true,
  1068  			RecursionAvailable: true,
  1069  		},
  1070  		Questions: q.Questions,
  1071  		Answers: []dnsmessage.Resource{
  1072  			{
  1073  				Header: dnsmessage.ResourceHeader{
  1074  					Name:  q.Questions[0].Name,
  1075  					Type:  dnsmessage.TypeTXT,
  1076  					Class: dnsmessage.ClassINET,
  1077  				},
  1078  				Body: &dnsmessage.TXTResource{
  1079  					TXT: []string{"ok"},
  1080  				},
  1081  			},
  1082  		},
  1083  	}
  1084  
  1085  	return r
  1086  }
  1087  
  1088  // Issue 17448. With StrictErrors enabled, temporary errors should make
  1089  // LookupIP fail rather than return a partial result.
  1090  func TestStrictErrorsLookupIP(t *testing.T) {
  1091  	defer dnsWaitGroup.Wait()
  1092  
  1093  	conf, err := newResolvConfTest()
  1094  	if err != nil {
  1095  		t.Fatal(err)
  1096  	}
  1097  	defer conf.teardown()
  1098  
  1099  	confData := []string{
  1100  		"nameserver 192.0.2.53",
  1101  		"search x.golang.org y.golang.org",
  1102  	}
  1103  	if err := conf.writeAndUpdate(confData); err != nil {
  1104  		t.Fatal(err)
  1105  	}
  1106  
  1107  	const name = "test-issue19592"
  1108  	const server = "192.0.2.53:53"
  1109  	const searchX = "test-issue19592.x.golang.org."
  1110  	const searchY = "test-issue19592.y.golang.org."
  1111  	const ip4 = "192.0.2.1"
  1112  	const ip6 = "2001:db8::1"
  1113  
  1114  	type resolveWhichEnum int
  1115  	const (
  1116  		resolveOK resolveWhichEnum = iota
  1117  		resolveOpError
  1118  		resolveServfail
  1119  		resolveTimeout
  1120  	)
  1121  
  1122  	makeTempError := func(err string) error {
  1123  		return &DNSError{
  1124  			Err:         err,
  1125  			Name:        name,
  1126  			Server:      server,
  1127  			IsTemporary: true,
  1128  		}
  1129  	}
  1130  	makeTimeout := func() error {
  1131  		return &DNSError{
  1132  			Err:       poll.ErrTimeout.Error(),
  1133  			Name:      name,
  1134  			Server:    server,
  1135  			IsTimeout: true,
  1136  		}
  1137  	}
  1138  	makeNxDomain := func() error {
  1139  		return &DNSError{
  1140  			Err:    errNoSuchHost.Error(),
  1141  			Name:   name,
  1142  			Server: server,
  1143  		}
  1144  	}
  1145  
  1146  	cases := []struct {
  1147  		desc          string
  1148  		resolveWhich  func(quest dnsmessage.Question) resolveWhichEnum
  1149  		wantStrictErr error
  1150  		wantLaxErr    error
  1151  		wantIPs       []string
  1152  	}{
  1153  		{
  1154  			desc: "No errors",
  1155  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1156  				return resolveOK
  1157  			},
  1158  			wantIPs: []string{ip4, ip6},
  1159  		},
  1160  		{
  1161  			desc: "searchX error fails in strict mode",
  1162  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1163  				if quest.Name.String() == searchX {
  1164  					return resolveTimeout
  1165  				}
  1166  				return resolveOK
  1167  			},
  1168  			wantStrictErr: makeTimeout(),
  1169  			wantIPs:       []string{ip4, ip6},
  1170  		},
  1171  		{
  1172  			desc: "searchX IPv4-only timeout fails in strict mode",
  1173  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1174  				if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeA {
  1175  					return resolveTimeout
  1176  				}
  1177  				return resolveOK
  1178  			},
  1179  			wantStrictErr: makeTimeout(),
  1180  			wantIPs:       []string{ip4, ip6},
  1181  		},
  1182  		{
  1183  			desc: "searchX IPv6-only servfail fails in strict mode",
  1184  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1185  				if quest.Name.String() == searchX && quest.Type == dnsmessage.TypeAAAA {
  1186  					return resolveServfail
  1187  				}
  1188  				return resolveOK
  1189  			},
  1190  			wantStrictErr: makeTempError("server misbehaving"),
  1191  			wantIPs:       []string{ip4, ip6},
  1192  		},
  1193  		{
  1194  			desc: "searchY error always fails",
  1195  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1196  				if quest.Name.String() == searchY {
  1197  					return resolveTimeout
  1198  				}
  1199  				return resolveOK
  1200  			},
  1201  			wantStrictErr: makeTimeout(),
  1202  			wantLaxErr:    makeNxDomain(), // This one reaches the "test." FQDN.
  1203  		},
  1204  		{
  1205  			desc: "searchY IPv4-only socket error fails in strict mode",
  1206  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1207  				if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeA {
  1208  					return resolveOpError
  1209  				}
  1210  				return resolveOK
  1211  			},
  1212  			wantStrictErr: makeTempError("write: socket on fire"),
  1213  			wantIPs:       []string{ip6},
  1214  		},
  1215  		{
  1216  			desc: "searchY IPv6-only timeout fails in strict mode",
  1217  			resolveWhich: func(quest dnsmessage.Question) resolveWhichEnum {
  1218  				if quest.Name.String() == searchY && quest.Type == dnsmessage.TypeAAAA {
  1219  					return resolveTimeout
  1220  				}
  1221  				return resolveOK
  1222  			},
  1223  			wantStrictErr: makeTimeout(),
  1224  			wantIPs:       []string{ip4},
  1225  		},
  1226  	}
  1227  
  1228  	for i, tt := range cases {
  1229  		fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1230  			t.Log(s, q)
  1231  
  1232  			switch tt.resolveWhich(q.Questions[0]) {
  1233  			case resolveOK:
  1234  				// Handle below.
  1235  			case resolveOpError:
  1236  				return dnsmessage.Message{}, &OpError{Op: "write", Err: fmt.Errorf("socket on fire")}
  1237  			case resolveServfail:
  1238  				return dnsmessage.Message{
  1239  					Header: dnsmessage.Header{
  1240  						ID:       q.ID,
  1241  						Response: true,
  1242  						RCode:    dnsmessage.RCodeServerFailure,
  1243  					},
  1244  					Questions: q.Questions,
  1245  				}, nil
  1246  			case resolveTimeout:
  1247  				return dnsmessage.Message{}, poll.ErrTimeout
  1248  			default:
  1249  				t.Fatal("Impossible resolveWhich")
  1250  			}
  1251  
  1252  			switch q.Questions[0].Name.String() {
  1253  			case searchX, name + ".":
  1254  				// Return NXDOMAIN to utilize the search list.
  1255  				return dnsmessage.Message{
  1256  					Header: dnsmessage.Header{
  1257  						ID:       q.ID,
  1258  						Response: true,
  1259  						RCode:    dnsmessage.RCodeNameError,
  1260  					},
  1261  					Questions: q.Questions,
  1262  				}, nil
  1263  			case searchY:
  1264  				// Return records below.
  1265  			default:
  1266  				return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
  1267  			}
  1268  
  1269  			r := dnsmessage.Message{
  1270  				Header: dnsmessage.Header{
  1271  					ID:       q.ID,
  1272  					Response: true,
  1273  				},
  1274  				Questions: q.Questions,
  1275  			}
  1276  			switch q.Questions[0].Type {
  1277  			case dnsmessage.TypeA:
  1278  				r.Answers = []dnsmessage.Resource{
  1279  					{
  1280  						Header: dnsmessage.ResourceHeader{
  1281  							Name:   q.Questions[0].Name,
  1282  							Type:   dnsmessage.TypeA,
  1283  							Class:  dnsmessage.ClassINET,
  1284  							Length: 4,
  1285  						},
  1286  						Body: &dnsmessage.AResource{
  1287  							A: TestAddr,
  1288  						},
  1289  					},
  1290  				}
  1291  			case dnsmessage.TypeAAAA:
  1292  				r.Answers = []dnsmessage.Resource{
  1293  					{
  1294  						Header: dnsmessage.ResourceHeader{
  1295  							Name:   q.Questions[0].Name,
  1296  							Type:   dnsmessage.TypeAAAA,
  1297  							Class:  dnsmessage.ClassINET,
  1298  							Length: 16,
  1299  						},
  1300  						Body: &dnsmessage.AAAAResource{
  1301  							AAAA: TestAddr6,
  1302  						},
  1303  					},
  1304  				}
  1305  			default:
  1306  				return dnsmessage.Message{}, fmt.Errorf("Unexpected Type: %v", q.Questions[0].Type)
  1307  			}
  1308  			return r, nil
  1309  		}}
  1310  
  1311  		for _, strict := range []bool{true, false} {
  1312  			r := Resolver{PreferGo: true, StrictErrors: strict, Dial: fake.DialContext}
  1313  			ips, err := r.LookupIPAddr(context.Background(), name)
  1314  
  1315  			var wantErr error
  1316  			if strict {
  1317  				wantErr = tt.wantStrictErr
  1318  			} else {
  1319  				wantErr = tt.wantLaxErr
  1320  			}
  1321  			if !reflect.DeepEqual(err, wantErr) {
  1322  				t.Errorf("#%d (%s) strict=%v: got err %#v; want %#v", i, tt.desc, strict, err, wantErr)
  1323  			}
  1324  
  1325  			gotIPs := map[string]struct{}{}
  1326  			for _, ip := range ips {
  1327  				gotIPs[ip.String()] = struct{}{}
  1328  			}
  1329  			wantIPs := map[string]struct{}{}
  1330  			if wantErr == nil {
  1331  				for _, ip := range tt.wantIPs {
  1332  					wantIPs[ip] = struct{}{}
  1333  				}
  1334  			}
  1335  			if !reflect.DeepEqual(gotIPs, wantIPs) {
  1336  				t.Errorf("#%d (%s) strict=%v: got ips %v; want %v", i, tt.desc, strict, gotIPs, wantIPs)
  1337  			}
  1338  		}
  1339  	}
  1340  }
  1341  
  1342  // Issue 17448. With StrictErrors enabled, temporary errors should make
  1343  // LookupTXT stop walking the search list.
  1344  func TestStrictErrorsLookupTXT(t *testing.T) {
  1345  	defer dnsWaitGroup.Wait()
  1346  
  1347  	conf, err := newResolvConfTest()
  1348  	if err != nil {
  1349  		t.Fatal(err)
  1350  	}
  1351  	defer conf.teardown()
  1352  
  1353  	confData := []string{
  1354  		"nameserver 192.0.2.53",
  1355  		"search x.golang.org y.golang.org",
  1356  	}
  1357  	if err := conf.writeAndUpdate(confData); err != nil {
  1358  		t.Fatal(err)
  1359  	}
  1360  
  1361  	const name = "test"
  1362  	const server = "192.0.2.53:53"
  1363  	const searchX = "test.x.golang.org."
  1364  	const searchY = "test.y.golang.org."
  1365  	const txt = "Hello World"
  1366  
  1367  	fake := fakeDNSServer{rh: func(_, s string, q dnsmessage.Message, deadline time.Time) (dnsmessage.Message, error) {
  1368  		t.Log(s, q)
  1369  
  1370  		switch q.Questions[0].Name.String() {
  1371  		case searchX:
  1372  			return dnsmessage.Message{}, poll.ErrTimeout
  1373  		case searchY:
  1374  			return mockTXTResponse(q), nil
  1375  		default:
  1376  			return dnsmessage.Message{}, fmt.Errorf("Unexpected Name: %v", q.Questions[0].Name)
  1377  		}
  1378  	}}
  1379  
  1380  	for _, strict := range []bool{true, false} {
  1381  		r := Resolver{StrictErrors: strict, Dial: fake.DialContext}
  1382  		p, _, err := r.lookup(context.Background(), name, dnsmessage.TypeTXT)
  1383  		var wantErr error
  1384  		var wantRRs int
  1385  		if strict {
  1386  			wantErr = &DNSError{
  1387  				Err:       poll.ErrTimeout.Error(),
  1388  				Name:      name,
  1389  				Server:    server,
  1390  				IsTimeout: true,
  1391  			}
  1392  		} else {
  1393  			wantRRs = 1
  1394  		}
  1395  		if !reflect.DeepEqual(err, wantErr) {
  1396  			t.Errorf("strict=%v: got err %#v; want %#v", strict, err, wantErr)
  1397  		}
  1398  		a, err := p.AllAnswers()
  1399  		if err != nil {
  1400  			a = nil
  1401  		}
  1402  		if len(a) != wantRRs {
  1403  			t.Errorf("strict=%v: got %v; want %v", strict, len(a), wantRRs)
  1404  		}
  1405  	}
  1406  }
  1407  
  1408  // Test for a race between uninstalling the test hooks and closing a
  1409  // socket connection. This used to fail when testing with -race.
  1410  func TestDNSGoroutineRace(t *testing.T) {
  1411  	defer dnsWaitGroup.Wait()
  1412  
  1413  	fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) {
  1414  		time.Sleep(10 * time.Microsecond)
  1415  		return dnsmessage.Message{}, poll.ErrTimeout
  1416  	}}
  1417  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1418  
  1419  	// The timeout here is less than the timeout used by the server,
  1420  	// so the goroutine started to query the (fake) server will hang
  1421  	// around after this test is done if we don't call dnsWaitGroup.Wait.
  1422  	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Microsecond)
  1423  	defer cancel()
  1424  	_, err := r.LookupIPAddr(ctx, "where.are.they.now")
  1425  	if err == nil {
  1426  		t.Fatal("fake DNS lookup unexpectedly succeeded")
  1427  	}
  1428  }
  1429  
  1430  func lookupWithFake(fake fakeDNSServer, name string, typ dnsmessage.Type) error {
  1431  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1432  
  1433  	resolvConf.mu.RLock()
  1434  	conf := resolvConf.dnsConfig
  1435  	resolvConf.mu.RUnlock()
  1436  
  1437  	ctx, cancel := context.WithCancel(context.Background())
  1438  	defer cancel()
  1439  
  1440  	_, _, err := r.tryOneName(ctx, conf, name, typ)
  1441  	return err
  1442  }
  1443  
  1444  // Issue 8434: verify that Temporary returns true on an error when rcode
  1445  // is SERVFAIL
  1446  func TestIssue8434(t *testing.T) {
  1447  	err := lookupWithFake(fakeDNSServer{
  1448  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1449  			return dnsmessage.Message{
  1450  				Header: dnsmessage.Header{
  1451  					ID:       q.ID,
  1452  					Response: true,
  1453  					RCode:    dnsmessage.RCodeServerFailure,
  1454  				},
  1455  				Questions: q.Questions,
  1456  			}, nil
  1457  		},
  1458  	}, "golang.org.", dnsmessage.TypeALL)
  1459  	if err == nil {
  1460  		t.Fatal("expected an error")
  1461  	}
  1462  	if ne, ok := err.(Error); !ok {
  1463  		t.Fatalf("err = %#v; wanted something supporting net.Error", err)
  1464  	} else if !ne.Temporary() {
  1465  		t.Fatalf("Temporary = false for err = %#v; want Temporary == true", err)
  1466  	}
  1467  	if de, ok := err.(*DNSError); !ok {
  1468  		t.Fatalf("err = %#v; wanted a *net.DNSError", err)
  1469  	} else if !de.IsTemporary {
  1470  		t.Fatalf("IsTemporary = false for err = %#v; want IsTemporary == true", err)
  1471  	}
  1472  }
  1473  
  1474  // TestNoSuchHost verifies that tryOneName works correctly when the domain does
  1475  // not exist.
  1476  //
  1477  // Issue 12778: verify that NXDOMAIN without RA bit errors as "no such host"
  1478  // and not "server misbehaving"
  1479  //
  1480  // Issue 25336: verify that NXDOMAIN errors fail fast.
  1481  //
  1482  // Issue 27525: verify that empty answers fail fast.
  1483  func TestNoSuchHost(t *testing.T) {
  1484  	tests := []struct {
  1485  		name string
  1486  		f    func(string, string, dnsmessage.Message, time.Time) (dnsmessage.Message, error)
  1487  	}{
  1488  		{
  1489  			"NXDOMAIN",
  1490  			func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1491  				return dnsmessage.Message{
  1492  					Header: dnsmessage.Header{
  1493  						ID:                 q.ID,
  1494  						Response:           true,
  1495  						RCode:              dnsmessage.RCodeNameError,
  1496  						RecursionAvailable: false,
  1497  					},
  1498  					Questions: q.Questions,
  1499  				}, nil
  1500  			},
  1501  		},
  1502  		{
  1503  			"no answers",
  1504  			func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1505  				return dnsmessage.Message{
  1506  					Header: dnsmessage.Header{
  1507  						ID:                 q.ID,
  1508  						Response:           true,
  1509  						RCode:              dnsmessage.RCodeSuccess,
  1510  						RecursionAvailable: false,
  1511  						Authoritative:      true,
  1512  					},
  1513  					Questions: q.Questions,
  1514  				}, nil
  1515  			},
  1516  		},
  1517  	}
  1518  
  1519  	for _, test := range tests {
  1520  		t.Run(test.name, func(t *testing.T) {
  1521  			lookups := 0
  1522  			err := lookupWithFake(fakeDNSServer{
  1523  				rh: func(n, s string, q dnsmessage.Message, d time.Time) (dnsmessage.Message, error) {
  1524  					lookups++
  1525  					return test.f(n, s, q, d)
  1526  				},
  1527  			}, ".", dnsmessage.TypeALL)
  1528  
  1529  			if lookups != 1 {
  1530  				t.Errorf("got %d lookups, wanted 1", lookups)
  1531  			}
  1532  
  1533  			if err == nil {
  1534  				t.Fatal("expected an error")
  1535  			}
  1536  			de, ok := err.(*DNSError)
  1537  			if !ok {
  1538  				t.Fatalf("err = %#v; wanted a *net.DNSError", err)
  1539  			}
  1540  			if de.Err != errNoSuchHost.Error() {
  1541  				t.Fatalf("Err = %#v; wanted %q", de.Err, errNoSuchHost.Error())
  1542  			}
  1543  		})
  1544  	}
  1545  }
  1546  
  1547  // Issue 26573: verify that Conns that don't implement PacketConn are treated
  1548  // as streams even when udp was requested.
  1549  func TestDNSDialTCP(t *testing.T) {
  1550  	fake := fakeDNSServer{
  1551  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1552  			r := dnsmessage.Message{
  1553  				Header: dnsmessage.Header{
  1554  					ID:       q.Header.ID,
  1555  					Response: true,
  1556  					RCode:    dnsmessage.RCodeSuccess,
  1557  				},
  1558  				Questions: q.Questions,
  1559  			}
  1560  			return r, nil
  1561  		},
  1562  		alwaysTCP: true,
  1563  	}
  1564  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1565  	ctx := context.Background()
  1566  	_, _, err := r.exchange(ctx, "0.0.0.0", mustQuestion("com.", dnsmessage.TypeALL, dnsmessage.ClassINET), time.Second)
  1567  	if err != nil {
  1568  		t.Fatal("exhange failed:", err)
  1569  	}
  1570  }
  1571  
  1572  // Issue 27763: verify that two strings in one TXT record are concatenated.
  1573  func TestTXTRecordTwoStrings(t *testing.T) {
  1574  	fake := fakeDNSServer{
  1575  		rh: func(n, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
  1576  			r := dnsmessage.Message{
  1577  				Header: dnsmessage.Header{
  1578  					ID:       q.Header.ID,
  1579  					Response: true,
  1580  					RCode:    dnsmessage.RCodeSuccess,
  1581  				},
  1582  				Questions: q.Questions,
  1583  				Answers: []dnsmessage.Resource{
  1584  					{
  1585  						Header: dnsmessage.ResourceHeader{
  1586  							Name:  q.Questions[0].Name,
  1587  							Type:  dnsmessage.TypeA,
  1588  							Class: dnsmessage.ClassINET,
  1589  						},
  1590  						Body: &dnsmessage.TXTResource{
  1591  							TXT: []string{"string1 ", "string2"},
  1592  						},
  1593  					},
  1594  					{
  1595  						Header: dnsmessage.ResourceHeader{
  1596  							Name:  q.Questions[0].Name,
  1597  							Type:  dnsmessage.TypeA,
  1598  							Class: dnsmessage.ClassINET,
  1599  						},
  1600  						Body: &dnsmessage.TXTResource{
  1601  							TXT: []string{"onestring"},
  1602  						},
  1603  					},
  1604  				},
  1605  			}
  1606  			return r, nil
  1607  		},
  1608  	}
  1609  	r := Resolver{PreferGo: true, Dial: fake.DialContext}
  1610  	txt, err := r.lookupTXT(context.Background(), "golang.org")
  1611  	if err != nil {
  1612  		t.Fatal("LookupTXT failed:", err)
  1613  	}
  1614  	if want := 2; len(txt) != want {
  1615  		t.Fatalf("len(txt), got %d, want %d", len(txt), want)
  1616  	}
  1617  	if want := "string1 string2"; txt[0] != want {
  1618  		t.Errorf("txt[0], got %q, want %q", txt[0], want)
  1619  	}
  1620  	if want := "onestring"; txt[1] != want {
  1621  		t.Errorf("txt[1], got %q, want %q", txt[1], want)
  1622  	}
  1623  }
  1624  

View as plain text