github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/net/dnsclient_unix_test.go (about)

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