github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/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  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"path"
    14  	"reflect"
    15  	"testing"
    16  	"time"
    17  )
    18  
    19  var dnsTransportFallbackTests = []struct {
    20  	server  string
    21  	name    string
    22  	qtype   uint16
    23  	timeout int
    24  	rcode   int
    25  }{
    26  	// Querying "com." with qtype=255 usually makes an answer
    27  	// which requires more than 512 bytes.
    28  	{"8.8.8.8:53", "com.", dnsTypeALL, 2, dnsRcodeSuccess},
    29  	{"8.8.4.4:53", "com.", dnsTypeALL, 4, dnsRcodeSuccess},
    30  }
    31  
    32  func TestDNSTransportFallback(t *testing.T) {
    33  	if testing.Short() || !*testExternal {
    34  		t.Skip("skipping test to avoid external network")
    35  	}
    36  
    37  	for _, tt := range dnsTransportFallbackTests {
    38  		timeout := time.Duration(tt.timeout) * time.Second
    39  		msg, err := exchange(tt.server, tt.name, tt.qtype, timeout)
    40  		if err != nil {
    41  			t.Error(err)
    42  			continue
    43  		}
    44  		switch msg.rcode {
    45  		case tt.rcode, dnsRcodeServerFailure:
    46  		default:
    47  			t.Errorf("got %v from %v; want %v", msg.rcode, tt.server, tt.rcode)
    48  			continue
    49  		}
    50  	}
    51  }
    52  
    53  // See RFC 6761 for further information about the reserved, pseudo
    54  // domain names.
    55  var specialDomainNameTests = []struct {
    56  	name  string
    57  	qtype uint16
    58  	rcode int
    59  }{
    60  	// Name resoltion APIs and libraries should not recongnize the
    61  	// followings as special.
    62  	{"1.0.168.192.in-addr.arpa.", dnsTypePTR, dnsRcodeNameError},
    63  	{"test.", dnsTypeALL, dnsRcodeNameError},
    64  	{"example.com.", dnsTypeALL, dnsRcodeSuccess},
    65  
    66  	// Name resoltion APIs and libraries should recongnize the
    67  	// followings as special and should not send any queries.
    68  	// Though, we test those names here for verifying nagative
    69  	// answers at DNS query-response interaction level.
    70  	{"localhost.", dnsTypeALL, dnsRcodeNameError},
    71  	{"invalid.", dnsTypeALL, dnsRcodeNameError},
    72  }
    73  
    74  func TestSpecialDomainName(t *testing.T) {
    75  	if testing.Short() || !*testExternal {
    76  		t.Skip("skipping test to avoid external network")
    77  	}
    78  
    79  	server := "8.8.8.8:53"
    80  	for _, tt := range specialDomainNameTests {
    81  		msg, err := exchange(server, tt.name, tt.qtype, 0)
    82  		if err != nil {
    83  			t.Error(err)
    84  			continue
    85  		}
    86  		switch msg.rcode {
    87  		case tt.rcode, dnsRcodeServerFailure:
    88  		default:
    89  			t.Errorf("got %v from %v; want %v", msg.rcode, server, tt.rcode)
    90  			continue
    91  		}
    92  	}
    93  }
    94  
    95  type resolvConfTest struct {
    96  	*testing.T
    97  	dir     string
    98  	path    string
    99  	started bool
   100  	quitc   chan chan struct{}
   101  }
   102  
   103  func newResolvConfTest(t *testing.T) *resolvConfTest {
   104  	dir, err := ioutil.TempDir("", "resolvConfTest")
   105  	if err != nil {
   106  		t.Fatalf("could not create temp dir: %v", err)
   107  	}
   108  
   109  	// Disable the default loadConfig
   110  	onceLoadConfig.Do(func() {})
   111  
   112  	r := &resolvConfTest{
   113  		T:     t,
   114  		dir:   dir,
   115  		path:  path.Join(dir, "resolv.conf"),
   116  		quitc: make(chan chan struct{}),
   117  	}
   118  
   119  	return r
   120  }
   121  
   122  func (r *resolvConfTest) Start() {
   123  	loadConfig(r.path, 100*time.Millisecond, r.quitc)
   124  	r.started = true
   125  }
   126  
   127  func (r *resolvConfTest) SetConf(s string) {
   128  	// Make sure the file mtime will be different once we're done here,
   129  	// even on systems with coarse (1s) mtime resolution.
   130  	time.Sleep(time.Second)
   131  
   132  	f, err := os.OpenFile(r.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
   133  	if err != nil {
   134  		r.Fatalf("failed to create temp file %s: %v", r.path, err)
   135  	}
   136  	if _, err := io.WriteString(f, s); err != nil {
   137  		f.Close()
   138  		r.Fatalf("failed to write temp file: %v", err)
   139  	}
   140  	f.Close()
   141  
   142  	if r.started {
   143  		cfg.ch <- struct{}{} // fill buffer
   144  		cfg.ch <- struct{}{} // wait for reload to begin
   145  		cfg.ch <- struct{}{} // wait for reload to complete
   146  	}
   147  }
   148  
   149  func (r *resolvConfTest) WantServers(want []string) {
   150  	cfg.mu.RLock()
   151  	defer cfg.mu.RUnlock()
   152  	if got := cfg.dnsConfig.servers; !reflect.DeepEqual(got, want) {
   153  		r.Fatalf("Unexpected dns server loaded, got %v want %v", got, want)
   154  	}
   155  }
   156  
   157  func (r *resolvConfTest) Close() {
   158  	resp := make(chan struct{})
   159  	r.quitc <- resp
   160  	<-resp
   161  	if err := os.RemoveAll(r.dir); err != nil {
   162  		r.Logf("failed to remove temp dir %s: %v", r.dir, err)
   163  	}
   164  }
   165  
   166  func TestReloadResolvConfFail(t *testing.T) {
   167  	if testing.Short() || !*testExternal {
   168  		t.Skip("skipping test to avoid external network")
   169  	}
   170  
   171  	r := newResolvConfTest(t)
   172  	defer r.Close()
   173  
   174  	// resolv.conf.tmp does not exist yet
   175  	r.Start()
   176  	if _, err := goLookupIP("golang.org"); err == nil {
   177  		t.Fatal("goLookupIP(missing) succeeded")
   178  	}
   179  
   180  	r.SetConf("nameserver 8.8.8.8")
   181  	if _, err := goLookupIP("golang.org"); err != nil {
   182  		t.Fatalf("goLookupIP(missing; good) failed: %v", err)
   183  	}
   184  
   185  	// Using a bad resolv.conf while we had a good
   186  	// one before should not update the config
   187  	r.SetConf("")
   188  	if _, err := goLookupIP("golang.org"); err != nil {
   189  		t.Fatalf("goLookupIP(missing; good; bad) failed: %v", err)
   190  	}
   191  }
   192  
   193  func TestReloadResolvConfChange(t *testing.T) {
   194  	if testing.Short() || !*testExternal {
   195  		t.Skip("skipping test to avoid external network")
   196  	}
   197  
   198  	r := newResolvConfTest(t)
   199  	defer r.Close()
   200  
   201  	r.SetConf("nameserver 8.8.8.8")
   202  	r.Start()
   203  
   204  	if _, err := goLookupIP("golang.org"); err != nil {
   205  		t.Fatalf("goLookupIP(good) failed: %v", err)
   206  	}
   207  	r.WantServers([]string{"8.8.8.8"})
   208  
   209  	// Using a bad resolv.conf when we had a good one
   210  	// before should not update the config
   211  	r.SetConf("")
   212  	if _, err := goLookupIP("golang.org"); err != nil {
   213  		t.Fatalf("goLookupIP(good; bad) failed: %v", err)
   214  	}
   215  
   216  	// A new good config should get picked up
   217  	r.SetConf("nameserver 8.8.4.4")
   218  	r.WantServers([]string{"8.8.4.4"})
   219  }
   220  
   221  func BenchmarkGoLookupIP(b *testing.B) {
   222  	for i := 0; i < b.N; i++ {
   223  		goLookupIP("www.example.com")
   224  	}
   225  }
   226  
   227  func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
   228  	for i := 0; i < b.N; i++ {
   229  		goLookupIP("some.nonexistent")
   230  	}
   231  }
   232  
   233  func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
   234  	onceLoadConfig.Do(loadDefaultConfig)
   235  	if cfg.dnserr != nil || cfg.dnsConfig == nil {
   236  		b.Fatalf("loadConfig failed: %v", cfg.dnserr)
   237  	}
   238  	// This looks ugly but it's safe as long as benchmarks are run
   239  	// sequentially in package testing.
   240  	orig := cfg.dnsConfig
   241  	cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
   242  	for i := 0; i < b.N; i++ {
   243  		goLookupIP("www.example.com")
   244  	}
   245  	cfg.dnsConfig = orig
   246  }