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