github.com/moby/docker@v26.1.3+incompatible/libnetwork/resolvconf/resolvconf_unix_test.go (about) 1 //go:build !windows 2 3 package resolvconf 4 5 import ( 6 "bytes" 7 "os" 8 "strings" 9 "testing" 10 11 "github.com/opencontainers/go-digest" 12 "gotest.tools/v3/assert" 13 is "gotest.tools/v3/assert/cmp" 14 ) 15 16 func TestGet(t *testing.T) { 17 actual, err := Get() 18 if err != nil { 19 t.Fatal(err) 20 } 21 expected, err := os.ReadFile(Path()) 22 if err != nil { 23 t.Fatal(err) 24 } 25 if !bytes.Equal(actual.Content, expected) { 26 t.Errorf("%s and GetResolvConf have different content.", Path()) 27 } 28 hash := digest.FromBytes(expected) 29 if !bytes.Equal(actual.Hash, []byte(hash)) { 30 t.Errorf("%s and GetResolvConf have different hashes.", Path()) 31 } 32 } 33 34 func TestGetNameservers(t *testing.T) { 35 for _, tc := range []struct { 36 input string 37 result []string 38 }{ 39 { 40 input: ``, 41 }, 42 { 43 input: `search example.com`, 44 }, 45 { 46 input: ` nameserver 1.2.3.4 `, 47 result: []string{"1.2.3.4"}, 48 }, 49 { 50 input: ` 51 nameserver 1.2.3.4 52 nameserver 40.3.200.10 53 search example.com`, 54 result: []string{"1.2.3.4", "40.3.200.10"}, 55 }, 56 { 57 input: `nameserver 1.2.3.4 58 search example.com 59 nameserver 4.30.20.100`, 60 result: []string{"1.2.3.4", "4.30.20.100"}, 61 }, 62 { 63 input: `search example.com 64 nameserver 1.2.3.4 65 #nameserver 4.3.2.1`, 66 result: []string{"1.2.3.4"}, 67 }, 68 { 69 input: `search example.com 70 nameserver 1.2.3.4 # not 4.3.2.1`, 71 result: []string{"1.2.3.4"}, 72 }, 73 } { 74 test := GetNameservers([]byte(tc.input), IP) 75 if !strSlicesEqual(test, tc.result) { 76 t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input) 77 } 78 } 79 } 80 81 func TestGetNameserversAsCIDR(t *testing.T) { 82 for _, tc := range []struct { 83 input string 84 result []string 85 }{ 86 { 87 input: ``, 88 }, 89 { 90 input: `search example.com`, 91 }, 92 { 93 input: ` nameserver 1.2.3.4 `, 94 result: []string{"1.2.3.4/32"}, 95 }, 96 { 97 input: ` 98 nameserver 1.2.3.4 99 nameserver 40.3.200.10 100 search example.com`, 101 result: []string{"1.2.3.4/32", "40.3.200.10/32"}, 102 }, 103 { 104 input: `nameserver 1.2.3.4 105 search example.com 106 nameserver 4.30.20.100`, 107 result: []string{"1.2.3.4/32", "4.30.20.100/32"}, 108 }, 109 { 110 input: `search example.com 111 nameserver 1.2.3.4 112 #nameserver 4.3.2.1`, 113 result: []string{"1.2.3.4/32"}, 114 }, 115 { 116 input: `search example.com 117 nameserver 1.2.3.4 # not 4.3.2.1`, 118 result: []string{"1.2.3.4/32"}, 119 }, 120 { 121 input: `nameserver fd6f:c490:ec68::1`, 122 result: []string{"fd6f:c490:ec68::1/128"}, 123 }, 124 { 125 input: `nameserver fe80::1234%eth0`, 126 result: []string{"fe80::1234/128"}, 127 }, 128 } { 129 test := GetNameserversAsCIDR([]byte(tc.input)) 130 if !strSlicesEqual(test, tc.result) { 131 t.Errorf("Wrong nameserver string {%s} should be %v. Input: %s", test, tc.result, tc.input) 132 } 133 } 134 } 135 136 func TestGetSearchDomains(t *testing.T) { 137 for _, tc := range []struct { 138 input string 139 result []string 140 }{ 141 { 142 input: ``, 143 }, 144 { 145 input: `# ignored`, 146 }, 147 { 148 input: `search example.com`, 149 result: []string{"example.com"}, 150 }, 151 { 152 input: `search example.com # notignored`, 153 result: []string{"example.com", "#", "notignored"}, 154 }, 155 { 156 input: ` search example.com `, 157 result: []string{"example.com"}, 158 }, 159 { 160 input: ` search example.com # notignored`, 161 result: []string{"example.com", "#", "notignored"}, 162 }, 163 { 164 input: `search foo.example.com example.com`, 165 result: []string{"foo.example.com", "example.com"}, 166 }, 167 { 168 input: ` search foo.example.com example.com `, 169 result: []string{"foo.example.com", "example.com"}, 170 }, 171 { 172 input: ` search foo.example.com example.com # notignored`, 173 result: []string{"foo.example.com", "example.com", "#", "notignored"}, 174 }, 175 { 176 input: `nameserver 1.2.3.4 177 search foo.example.com example.com`, 178 result: []string{"foo.example.com", "example.com"}, 179 }, 180 { 181 input: `nameserver 1.2.3.4 182 search dup1.example.com dup2.example.com 183 search foo.example.com example.com`, 184 result: []string{"foo.example.com", "example.com"}, 185 }, 186 { 187 input: `nameserver 1.2.3.4 188 search foo.example.com example.com 189 nameserver 4.30.20.100`, 190 result: []string{"foo.example.com", "example.com"}, 191 }, 192 { 193 input: `domain an.example`, 194 result: []string{"an.example"}, 195 }, 196 } { 197 test := GetSearchDomains([]byte(tc.input)) 198 if !strSlicesEqual(test, tc.result) { 199 t.Errorf("Wrong search domain string {%s} should be %v. Input: %s", test, tc.result, tc.input) 200 } 201 } 202 } 203 204 func TestGetOptions(t *testing.T) { 205 for _, tc := range []struct { 206 input string 207 result []string 208 }{ 209 { 210 input: ``, 211 }, 212 { 213 input: `# ignored`, 214 }, 215 { 216 input: `; ignored`, 217 }, 218 { 219 input: `nameserver 1.2.3.4`, 220 }, 221 { 222 input: `options opt1`, 223 result: []string{"opt1"}, 224 }, 225 { 226 input: `options opt1 # notignored`, 227 result: []string{"opt1", "#", "notignored"}, 228 }, 229 { 230 input: `options opt1 ; notignored`, 231 result: []string{"opt1", ";", "notignored"}, 232 }, 233 { 234 input: ` options opt1 `, 235 result: []string{"opt1"}, 236 }, 237 { 238 input: ` options opt1 # notignored`, 239 result: []string{"opt1", "#", "notignored"}, 240 }, 241 { 242 input: `options opt1 opt2 opt3`, 243 result: []string{"opt1", "opt2", "opt3"}, 244 }, 245 { 246 input: `options opt1 opt2 opt3 # notignored`, 247 result: []string{"opt1", "opt2", "opt3", "#", "notignored"}, 248 }, 249 { 250 input: ` options opt1 opt2 opt3 `, 251 result: []string{"opt1", "opt2", "opt3"}, 252 }, 253 { 254 input: ` options opt1 opt2 opt3 # notignored`, 255 result: []string{"opt1", "opt2", "opt3", "#", "notignored"}, 256 }, 257 { 258 input: `nameserver 1.2.3.4 259 options opt1 opt2 opt3`, 260 result: []string{"opt1", "opt2", "opt3"}, 261 }, 262 { 263 input: `nameserver 1.2.3.4 264 options opt1 opt2 265 options opt3 opt4`, 266 result: []string{"opt1", "opt2", "opt3", "opt4"}, 267 }, 268 } { 269 test := GetOptions([]byte(tc.input)) 270 if !strSlicesEqual(test, tc.result) { 271 t.Errorf("Wrong options string {%s} should be %v. Input: %s", test, tc.result, tc.input) 272 } 273 } 274 } 275 276 func strSlicesEqual(a, b []string) bool { 277 if len(a) != len(b) { 278 return false 279 } 280 281 for i, v := range a { 282 if v != b[i] { 283 return false 284 } 285 } 286 287 return true 288 } 289 290 func TestBuild(t *testing.T) { 291 tmpDir := t.TempDir() 292 file, err := os.CreateTemp(tmpDir, "") 293 if err != nil { 294 t.Fatal(err) 295 } 296 297 f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{"opt1"}) 298 if err != nil { 299 t.Fatal(err) 300 } 301 302 const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n" 303 if !bytes.Equal(f.Content, []byte(expected)) { 304 t.Errorf("Expected to find '%s' got '%s'", expected, f.Content) 305 } 306 content, err := os.ReadFile(file.Name()) 307 if err != nil { 308 t.Fatal(err) 309 } 310 if !bytes.Equal(content, []byte(expected)) { 311 t.Errorf("Expected to find '%s' got '%s'", expected, content) 312 } 313 } 314 315 func TestBuildWithZeroLengthDomainSearch(t *testing.T) { 316 tmpDir := t.TempDir() 317 file, err := os.CreateTemp(tmpDir, "") 318 if err != nil { 319 t.Fatal(err) 320 } 321 322 f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"."}, []string{"opt1"}) 323 if err != nil { 324 t.Fatal(err) 325 } 326 327 const expected = "nameserver ns1\nnameserver ns2\nnameserver ns3\noptions opt1\n" 328 if !bytes.Equal(f.Content, []byte(expected)) { 329 t.Errorf("Expected to find '%s' got '%s'", expected, f.Content) 330 } 331 content, err := os.ReadFile(file.Name()) 332 if err != nil { 333 t.Fatal(err) 334 } 335 if !bytes.Equal(content, []byte(expected)) { 336 t.Errorf("Expected to find '%s' got '%s'", expected, content) 337 } 338 } 339 340 func TestBuildWithNoOptions(t *testing.T) { 341 tmpDir := t.TempDir() 342 file, err := os.CreateTemp(tmpDir, "") 343 if err != nil { 344 t.Fatal(err) 345 } 346 347 f, err := Build(file.Name(), []string{"ns1", "ns2", "ns3"}, []string{"search1"}, []string{}) 348 if err != nil { 349 t.Fatal(err) 350 } 351 352 const expected = "search search1\nnameserver ns1\nnameserver ns2\nnameserver ns3\n" 353 if !bytes.Equal(f.Content, []byte(expected)) { 354 t.Errorf("Expected to find '%s' got '%s'", expected, f.Content) 355 } 356 content, err := os.ReadFile(file.Name()) 357 if err != nil { 358 t.Fatal(err) 359 } 360 if !bytes.Equal(content, []byte(expected)) { 361 t.Errorf("Expected to find '%s' got '%s'", expected, content) 362 } 363 } 364 365 func TestFilterResolvDNS(t *testing.T) { 366 testcases := []struct { 367 name string 368 input string 369 ipv6Enabled bool 370 expOut string 371 }{ 372 { 373 name: "No localhost", 374 input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\n", 375 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 376 }, 377 { 378 name: "Localhost last", 379 input: "nameserver 10.16.60.14\nnameserver 10.16.60.21\nnameserver 127.0.0.1\n", 380 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 381 }, 382 { 383 name: "Localhost middle", 384 input: "nameserver 10.16.60.14\nnameserver 127.0.0.1\nnameserver 10.16.60.21\n", 385 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 386 }, 387 { 388 name: "Localhost first", 389 input: "nameserver 127.0.1.1\nnameserver 10.16.60.14\nnameserver 10.16.60.21\n", 390 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 391 }, 392 { 393 name: "IPv6 Localhost", 394 input: "nameserver ::1\nnameserver 10.16.60.14\nnameserver 127.0.2.1\nnameserver 10.16.60.21\n", 395 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 396 }, 397 { 398 name: "Two IPv6 Localhosts", 399 input: "nameserver 10.16.60.14\nnameserver ::1\nnameserver 10.16.60.21\nnameserver ::1", 400 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 401 }, 402 { 403 name: "IPv6 disabled", 404 input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1", 405 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 406 }, 407 { 408 name: "IPv6 link-local disabled", 409 input: "nameserver 10.16.60.14\nnameserver FE80::BB1%1\nnameserver FE80::BB1%eth0\nnameserver 10.16.60.21", 410 expOut: "nameserver 10.16.60.14\nnameserver 10.16.60.21", 411 }, 412 { 413 name: "IPv6 enabled", 414 input: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21\nnameserver ::1\n", 415 ipv6Enabled: true, 416 expOut: "nameserver 10.16.60.14\nnameserver 2002:dead:beef::1\nnameserver 10.16.60.21", 417 }, 418 { 419 // with IPv6 enabled, and no non-localhost servers, Google defaults (both IPv4+IPv6) should be added 420 name: "localhost only IPv6", 421 input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1", 422 ipv6Enabled: true, 423 expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4\nnameserver 2001:4860:4860::8888\nnameserver 2001:4860:4860::8844", 424 }, 425 { 426 // with IPv6 disabled, and no non-localhost servers, Google defaults (only IPv4) should be added 427 name: "localhost only no IPv6", 428 input: "nameserver 127.0.0.1\nnameserver ::1\nnameserver 127.0.2.1", 429 expOut: "nameserver 8.8.8.8\nnameserver 8.8.4.4", 430 }, 431 } 432 433 for _, tc := range testcases { 434 t.Run(tc.name, func(t *testing.T) { 435 f, err := FilterResolvDNS([]byte(tc.input), tc.ipv6Enabled) 436 assert.Check(t, is.Nil(err)) 437 out := strings.TrimSpace(string(f.Content)) 438 assert.Check(t, is.Equal(out, tc.expOut)) 439 }) 440 } 441 }