Home | History | Annotate | Download | only in net
      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris
      6 
      7 package net
      8 
      9 import (
     10 	"errors"
     11 	"os"
     12 	"reflect"
     13 	"strings"
     14 	"testing"
     15 	"time"
     16 )
     17 
     18 var dnsReadConfigTests = []struct {
     19 	name string
     20 	want *dnsConfig
     21 }{
     22 	{
     23 		name: "testdata/resolv.conf",
     24 		want: &dnsConfig{
     25 			servers:    []string{"8.8.8.8:53", "[2001:4860:4860::8888]:53", "[fe80::1%lo0]:53"},
     26 			search:     []string{"localdomain."},
     27 			ndots:      5,
     28 			timeout:    10 * time.Second,
     29 			attempts:   3,
     30 			rotate:     true,
     31 			unknownOpt: true, // the "options attempts 3" line
     32 		},
     33 	},
     34 	{
     35 		name: "testdata/domain-resolv.conf",
     36 		want: &dnsConfig{
     37 			servers:  []string{"8.8.8.8:53"},
     38 			search:   []string{"localdomain."},
     39 			ndots:    1,
     40 			timeout:  5 * time.Second,
     41 			attempts: 2,
     42 		},
     43 	},
     44 	{
     45 		name: "testdata/search-resolv.conf",
     46 		want: &dnsConfig{
     47 			servers:  []string{"8.8.8.8:53"},
     48 			search:   []string{"test.", "invalid."},
     49 			ndots:    1,
     50 			timeout:  5 * time.Second,
     51 			attempts: 2,
     52 		},
     53 	},
     54 	{
     55 		name: "testdata/empty-resolv.conf",
     56 		want: &dnsConfig{
     57 			servers:  defaultNS,
     58 			ndots:    1,
     59 			timeout:  5 * time.Second,
     60 			attempts: 2,
     61 			search:   []string{"domain.local."},
     62 		},
     63 	},
     64 	{
     65 		name: "testdata/invalid-ndots-resolv.conf",
     66 		want: &dnsConfig{
     67 			servers:  defaultNS,
     68 			ndots:    0,
     69 			timeout:  5 * time.Second,
     70 			attempts: 2,
     71 			search:   []string{"domain.local."},
     72 		},
     73 	},
     74 	{
     75 		name: "testdata/large-ndots-resolv.conf",
     76 		want: &dnsConfig{
     77 			servers:  defaultNS,
     78 			ndots:    15,
     79 			timeout:  5 * time.Second,
     80 			attempts: 2,
     81 			search:   []string{"domain.local."},
     82 		},
     83 	},
     84 	{
     85 		name: "testdata/negative-ndots-resolv.conf",
     86 		want: &dnsConfig{
     87 			servers:  defaultNS,
     88 			ndots:    0,
     89 			timeout:  5 * time.Second,
     90 			attempts: 2,
     91 			search:   []string{"domain.local."},
     92 		},
     93 	},
     94 	{
     95 		name: "testdata/openbsd-resolv.conf",
     96 		want: &dnsConfig{
     97 			ndots:    1,
     98 			timeout:  5 * time.Second,
     99 			attempts: 2,
    100 			lookup:   []string{"file", "bind"},
    101 			servers:  []string{"169.254.169.254:53", "10.240.0.1:53"},
    102 			search:   []string{"c.symbolic-datum-552.internal."},
    103 		},
    104 	},
    105 }
    106 
    107 func TestDNSReadConfig(t *testing.T) {
    108 	origGetHostname := getHostname
    109 	defer func() { getHostname = origGetHostname }()
    110 	getHostname = func() (string, error) { return "host.domain.local", nil }
    111 
    112 	for _, tt := range dnsReadConfigTests {
    113 		conf := dnsReadConfig(tt.name)
    114 		if conf.err != nil {
    115 			t.Fatal(conf.err)
    116 		}
    117 		conf.mtime = time.Time{}
    118 		if !reflect.DeepEqual(conf, tt.want) {
    119 			t.Errorf("%s:\ngot: %+v\nwant: %+v", tt.name, conf, tt.want)
    120 		}
    121 	}
    122 }
    123 
    124 func TestDNSReadMissingFile(t *testing.T) {
    125 	origGetHostname := getHostname
    126 	defer func() { getHostname = origGetHostname }()
    127 	getHostname = func() (string, error) { return "host.domain.local", nil }
    128 
    129 	conf := dnsReadConfig("a-nonexistent-file")
    130 	if !os.IsNotExist(conf.err) {
    131 		t.Errorf("missing resolv.conf:\ngot: %v\nwant: %v", conf.err, os.ErrNotExist)
    132 	}
    133 	conf.err = nil
    134 	want := &dnsConfig{
    135 		servers:  defaultNS,
    136 		ndots:    1,
    137 		timeout:  5 * time.Second,
    138 		attempts: 2,
    139 		search:   []string{"domain.local."},
    140 	}
    141 	if !reflect.DeepEqual(conf, want) {
    142 		t.Errorf("missing resolv.conf:\ngot: %+v\nwant: %+v", conf, want)
    143 	}
    144 }
    145 
    146 var dnsDefaultSearchTests = []struct {
    147 	name string
    148 	err  error
    149 	want []string
    150 }{
    151 	{
    152 		name: "host.long.domain.local",
    153 		want: []string{"long.domain.local."},
    154 	},
    155 	{
    156 		name: "host.local",
    157 		want: []string{"local."},
    158 	},
    159 	{
    160 		name: "host",
    161 		want: nil,
    162 	},
    163 	{
    164 		name: "host.domain.local",
    165 		err:  errors.New("errored"),
    166 		want: nil,
    167 	},
    168 	{
    169 		// ensures we don't return []string{""}
    170 		// which causes duplicate lookups
    171 		name: "foo.",
    172 		want: nil,
    173 	},
    174 }
    175 
    176 func TestDNSDefaultSearch(t *testing.T) {
    177 	origGetHostname := getHostname
    178 	defer func() { getHostname = origGetHostname }()
    179 
    180 	for _, tt := range dnsDefaultSearchTests {
    181 		getHostname = func() (string, error) { return tt.name, tt.err }
    182 		got := dnsDefaultSearch()
    183 		if !reflect.DeepEqual(got, tt.want) {
    184 			t.Errorf("dnsDefaultSearch with hostname %q and error %+v = %q, wanted %q", tt.name, tt.err, got, tt.want)
    185 		}
    186 	}
    187 }
    188 
    189 func TestDNSNameLength(t *testing.T) {
    190 	origGetHostname := getHostname
    191 	defer func() { getHostname = origGetHostname }()
    192 	getHostname = func() (string, error) { return "host.domain.local", nil }
    193 
    194 	var char63 = ""
    195 	for i := 0; i < 63; i++ {
    196 		char63 += "a"
    197 	}
    198 	longDomain := strings.Repeat(char63+".", 5) + "example"
    199 
    200 	for _, tt := range dnsReadConfigTests {
    201 		conf := dnsReadConfig(tt.name)
    202 		if conf.err != nil {
    203 			t.Fatal(conf.err)
    204 		}
    205 
    206 		var shortestSuffix int
    207 		for _, suffix := range tt.want.search {
    208 			if shortestSuffix == 0 || len(suffix) < shortestSuffix {
    209 				shortestSuffix = len(suffix)
    210 			}
    211 		}
    212 
    213 		// Test a name that will be maximally long when prefixing the shortest
    214 		// suffix (accounting for the intervening dot).
    215 		longName := longDomain[len(longDomain)-254+1+shortestSuffix:]
    216 		if longName[0] == '.' || longName[1] == '.' {
    217 			longName = "aa." + longName[3:]
    218 		}
    219 		for _, fqdn := range conf.nameList(longName) {
    220 			if len(fqdn) > 254 {
    221 				t.Errorf("got %d; want less than or equal to 254", len(fqdn))
    222 			}
    223 		}
    224 
    225 		// Now test a name that's too long for suffixing.
    226 		unsuffixable := "a." + longName[1:]
    227 		unsuffixableResults := conf.nameList(unsuffixable)
    228 		if len(unsuffixableResults) != 1 {
    229 			t.Errorf("suffixed names %v; want []", unsuffixableResults[1:])
    230 		}
    231 
    232 		// Now test a name that's too long for DNS.
    233 		tooLong := "a." + longDomain
    234 		tooLongResults := conf.nameList(tooLong)
    235 		if tooLongResults != nil {
    236 			t.Errorf("suffixed names %v; want nil", tooLongResults)
    237 		}
    238 	}
    239 }
    240