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

View as plain text