| // Copyright 2013 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris | 
 |  | 
 | package net | 
 |  | 
 | import ( | 
 | 	"errors" | 
 | 	"os" | 
 | 	"reflect" | 
 | 	"strings" | 
 | 	"testing" | 
 | 	"time" | 
 | ) | 
 |  | 
 | var dnsReadConfigTests = []struct { | 
 | 	name string | 
 | 	want *dnsConfig | 
 | }{ | 
 | 	{ | 
 | 		name: "testdata/resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:    []string{"8.8.8.8:53", "[2001:4860:4860::8888]:53", "[fe80::1%lo0]:53"}, | 
 | 			search:     []string{"localdomain."}, | 
 | 			ndots:      5, | 
 | 			timeout:    10 * time.Second, | 
 | 			attempts:   3, | 
 | 			rotate:     true, | 
 | 			unknownOpt: true, // the "options attempts 3" line | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/domain-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  []string{"8.8.8.8:53"}, | 
 | 			search:   []string{"localdomain."}, | 
 | 			ndots:    1, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/search-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  []string{"8.8.8.8:53"}, | 
 | 			search:   []string{"test.", "invalid."}, | 
 | 			ndots:    1, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/empty-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  defaultNS, | 
 | 			ndots:    1, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 			search:   []string{"domain.local."}, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/invalid-ndots-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  defaultNS, | 
 | 			ndots:    0, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 			search:   []string{"domain.local."}, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/large-ndots-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  defaultNS, | 
 | 			ndots:    15, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 			search:   []string{"domain.local."}, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/negative-ndots-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			servers:  defaultNS, | 
 | 			ndots:    0, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 			search:   []string{"domain.local."}, | 
 | 		}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "testdata/openbsd-resolv.conf", | 
 | 		want: &dnsConfig{ | 
 | 			ndots:    1, | 
 | 			timeout:  5 * time.Second, | 
 | 			attempts: 2, | 
 | 			lookup:   []string{"file", "bind"}, | 
 | 			servers:  []string{"169.254.169.254:53", "10.240.0.1:53"}, | 
 | 			search:   []string{"c.symbolic-datum-552.internal."}, | 
 | 		}, | 
 | 	}, | 
 | } | 
 |  | 
 | func TestDNSReadConfig(t *testing.T) { | 
 | 	origGetHostname := getHostname | 
 | 	defer func() { getHostname = origGetHostname }() | 
 | 	getHostname = func() (string, error) { return "host.domain.local", nil } | 
 |  | 
 | 	for _, tt := range dnsReadConfigTests { | 
 | 		conf := dnsReadConfig(tt.name) | 
 | 		if conf.err != nil { | 
 | 			t.Fatal(conf.err) | 
 | 		} | 
 | 		conf.mtime = time.Time{} | 
 | 		if !reflect.DeepEqual(conf, tt.want) { | 
 | 			t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, tt.want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestDNSReadMissingFile(t *testing.T) { | 
 | 	origGetHostname := getHostname | 
 | 	defer func() { getHostname = origGetHostname }() | 
 | 	getHostname = func() (string, error) { return "host.domain.local", nil } | 
 |  | 
 | 	conf := dnsReadConfig("a-nonexistent-file") | 
 | 	if !os.IsNotExist(conf.err) { | 
 | 		t.Errorf("missing resolv.conf:\ngot: %v\nwant: %v", conf.err, os.ErrNotExist) | 
 | 	} | 
 | 	conf.err = nil | 
 | 	want := &dnsConfig{ | 
 | 		servers:  defaultNS, | 
 | 		ndots:    1, | 
 | 		timeout:  5 * time.Second, | 
 | 		attempts: 2, | 
 | 		search:   []string{"domain.local."}, | 
 | 	} | 
 | 	if !reflect.DeepEqual(conf, want) { | 
 | 		t.Errorf("missing resolv.conf:\ngot: %+v\nwant: %+v", conf, want) | 
 | 	} | 
 | } | 
 |  | 
 | var dnsDefaultSearchTests = []struct { | 
 | 	name string | 
 | 	err  error | 
 | 	want []string | 
 | }{ | 
 | 	{ | 
 | 		name: "host.long.domain.local", | 
 | 		want: []string{"long.domain.local."}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "host.local", | 
 | 		want: []string{"local."}, | 
 | 	}, | 
 | 	{ | 
 | 		name: "host", | 
 | 		want: nil, | 
 | 	}, | 
 | 	{ | 
 | 		name: "host.domain.local", | 
 | 		err:  errors.New("errored"), | 
 | 		want: nil, | 
 | 	}, | 
 | 	{ | 
 | 		// ensures we don't return []string{""} | 
 | 		// which causes duplicate lookups | 
 | 		name: "foo.", | 
 | 		want: nil, | 
 | 	}, | 
 | } | 
 |  | 
 | func TestDNSDefaultSearch(t *testing.T) { | 
 | 	origGetHostname := getHostname | 
 | 	defer func() { getHostname = origGetHostname }() | 
 |  | 
 | 	for _, tt := range dnsDefaultSearchTests { | 
 | 		getHostname = func() (string, error) { return tt.name, tt.err } | 
 | 		got := dnsDefaultSearch() | 
 | 		if !reflect.DeepEqual(got, tt.want) { | 
 | 			t.Errorf("dnsDefaultSearch with hostname %q and error %+v = %q, wanted %q", tt.name, tt.err, got, tt.want) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestDNSNameLength(t *testing.T) { | 
 | 	origGetHostname := getHostname | 
 | 	defer func() { getHostname = origGetHostname }() | 
 | 	getHostname = func() (string, error) { return "host.domain.local", nil } | 
 |  | 
 | 	var char63 = "" | 
 | 	for i := 0; i < 63; i++ { | 
 | 		char63 += "a" | 
 | 	} | 
 | 	longDomain := strings.Repeat(char63+".", 5) + "example" | 
 |  | 
 | 	for _, tt := range dnsReadConfigTests { | 
 | 		conf := dnsReadConfig(tt.name) | 
 | 		if conf.err != nil { | 
 | 			t.Fatal(conf.err) | 
 | 		} | 
 |  | 
 | 		var shortestSuffix int | 
 | 		for _, suffix := range tt.want.search { | 
 | 			if shortestSuffix == 0 || len(suffix) < shortestSuffix { | 
 | 				shortestSuffix = len(suffix) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		// Test a name that will be maximally long when prefixing the shortest | 
 | 		// suffix (accounting for the intervening dot). | 
 | 		longName := longDomain[len(longDomain)-254+1+shortestSuffix:] | 
 | 		if longName[0] == '.' || longName[1] == '.' { | 
 | 			longName = "aa." + longName[3:] | 
 | 		} | 
 | 		for _, fqdn := range conf.nameList(longName) { | 
 | 			if len(fqdn) > 254 { | 
 | 				t.Errorf("got %d; want less than or equal to 254", len(fqdn)) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		// Now test a name that's too long for suffixing. | 
 | 		unsuffixable := "a." + longName[1:] | 
 | 		unsuffixableResults := conf.nameList(unsuffixable) | 
 | 		if len(unsuffixableResults) != 1 { | 
 | 			t.Errorf("suffixed names %v; want []", unsuffixableResults[1:]) | 
 | 		} | 
 |  | 
 | 		// Now test a name that's too long for DNS. | 
 | 		tooLong := "a." + longDomain | 
 | 		tooLongResults := conf.nameList(tooLong) | 
 | 		if tooLongResults != nil { | 
 | 			t.Errorf("suffixed names %v; want nil", tooLongResults) | 
 | 		} | 
 | 	} | 
 | } |