github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/net/net_windows_test.go (about) 1 // Copyright 2014 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 package net 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "regexp" 16 "sort" 17 "strings" 18 "syscall" 19 "testing" 20 "time" 21 ) 22 23 func toErrno(err error) (syscall.Errno, bool) { 24 operr, ok := err.(*OpError) 25 if !ok { 26 return 0, false 27 } 28 syserr, ok := operr.Err.(*os.SyscallError) 29 if !ok { 30 return 0, false 31 } 32 errno, ok := syserr.Err.(syscall.Errno) 33 if !ok { 34 return 0, false 35 } 36 return errno, true 37 } 38 39 // TestAcceptIgnoreSomeErrors tests that windows TCPListener.AcceptTCP 40 // handles broken connections. It verifies that broken connections do 41 // not affect future connections. 42 func TestAcceptIgnoreSomeErrors(t *testing.T) { 43 recv := func(ln Listener, ignoreSomeReadErrors bool) (string, error) { 44 c, err := ln.Accept() 45 if err != nil { 46 // Display windows errno in error message. 47 errno, ok := toErrno(err) 48 if !ok { 49 return "", err 50 } 51 return "", fmt.Errorf("%v (windows errno=%d)", err, errno) 52 } 53 defer c.Close() 54 55 b := make([]byte, 100) 56 n, err := c.Read(b) 57 if err == nil || err == io.EOF { 58 return string(b[:n]), nil 59 } 60 errno, ok := toErrno(err) 61 if ok && ignoreSomeReadErrors && (errno == syscall.ERROR_NETNAME_DELETED || errno == syscall.WSAECONNRESET) { 62 return "", nil 63 } 64 return "", err 65 } 66 67 send := func(addr string, data string) error { 68 c, err := Dial("tcp", addr) 69 if err != nil { 70 return err 71 } 72 defer c.Close() 73 74 b := []byte(data) 75 n, err := c.Write(b) 76 if err != nil { 77 return err 78 } 79 if n != len(b) { 80 return fmt.Errorf(`Only %d chars of string "%s" sent`, n, data) 81 } 82 return nil 83 } 84 85 if envaddr := os.Getenv("GOTEST_DIAL_ADDR"); envaddr != "" { 86 // In child process. 87 c, err := Dial("tcp", envaddr) 88 if err != nil { 89 t.Fatal(err) 90 } 91 fmt.Printf("sleeping\n") 92 time.Sleep(time.Minute) // process will be killed here 93 c.Close() 94 } 95 96 ln, err := Listen("tcp", "127.0.0.1:0") 97 if err != nil { 98 t.Fatal(err) 99 } 100 defer ln.Close() 101 102 // Start child process that connects to our listener. 103 cmd := exec.Command(os.Args[0], "-test.run=TestAcceptIgnoreSomeErrors") 104 cmd.Env = append(os.Environ(), "GOTEST_DIAL_ADDR="+ln.Addr().String()) 105 stdout, err := cmd.StdoutPipe() 106 if err != nil { 107 t.Fatalf("cmd.StdoutPipe failed: %v", err) 108 } 109 err = cmd.Start() 110 if err != nil { 111 t.Fatalf("cmd.Start failed: %v\n", err) 112 } 113 outReader := bufio.NewReader(stdout) 114 for { 115 s, err := outReader.ReadString('\n') 116 if err != nil { 117 t.Fatalf("reading stdout failed: %v", err) 118 } 119 if s == "sleeping\n" { 120 break 121 } 122 } 123 defer cmd.Wait() // ignore error - we know it is getting killed 124 125 const alittle = 100 * time.Millisecond 126 time.Sleep(alittle) 127 cmd.Process.Kill() // the only way to trigger the errors 128 time.Sleep(alittle) 129 130 // Send second connection data (with delay in a separate goroutine). 131 result := make(chan error) 132 go func() { 133 time.Sleep(alittle) 134 err := send(ln.Addr().String(), "abc") 135 if err != nil { 136 result <- err 137 } 138 result <- nil 139 }() 140 defer func() { 141 err := <-result 142 if err != nil { 143 t.Fatalf("send failed: %v", err) 144 } 145 }() 146 147 // Receive first or second connection. 148 s, err := recv(ln, true) 149 if err != nil { 150 t.Fatalf("recv failed: %v", err) 151 } 152 switch s { 153 case "": 154 // First connection data is received, let's get second connection data. 155 case "abc": 156 // First connection is lost forever, but that is ok. 157 return 158 default: 159 t.Fatalf(`"%s" received from recv, but "" or "abc" expected`, s) 160 } 161 162 // Get second connection data. 163 s, err = recv(ln, false) 164 if err != nil { 165 t.Fatalf("recv failed: %v", err) 166 } 167 if s != "abc" { 168 t.Fatalf(`"%s" received from recv, but "abc" expected`, s) 169 } 170 } 171 172 func isWindowsXP(t *testing.T) bool { 173 v, err := syscall.GetVersion() 174 if err != nil { 175 t.Fatalf("GetVersion failed: %v", err) 176 } 177 major := byte(v) 178 return major < 6 179 } 180 181 func runCmd(args ...string) ([]byte, error) { 182 removeUTF8BOM := func(b []byte) []byte { 183 if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF { 184 return b[3:] 185 } 186 return b 187 } 188 f, err := ioutil.TempFile("", "netcmd") 189 if err != nil { 190 return nil, err 191 } 192 f.Close() 193 defer os.Remove(f.Name()) 194 cmd := fmt.Sprintf(`%s | Out-File "%s" -encoding UTF8`, strings.Join(args, " "), f.Name()) 195 out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput() 196 if err != nil { 197 if len(out) != 0 { 198 return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out))) 199 } 200 var err2 error 201 out, err2 = ioutil.ReadFile(f.Name()) 202 if err2 != nil { 203 return nil, err2 204 } 205 if len(out) != 0 { 206 return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out))) 207 } 208 return nil, fmt.Errorf("%s failed: %v", args[0], err) 209 } 210 out, err = ioutil.ReadFile(f.Name()) 211 if err != nil { 212 return nil, err 213 } 214 return removeUTF8BOM(out), nil 215 } 216 217 func netshSpeaksEnglish(t *testing.T) bool { 218 out, err := runCmd("netsh", "help") 219 if err != nil { 220 t.Fatal(err) 221 } 222 return bytes.Contains(out, []byte("The following commands are available:")) 223 } 224 225 func netshInterfaceIPShowInterface(ipver string, ifaces map[string]bool) error { 226 out, err := runCmd("netsh", "interface", ipver, "show", "interface", "level=verbose") 227 if err != nil { 228 return err 229 } 230 // interface information is listed like: 231 // 232 //Interface Local Area Connection Parameters 233 //---------------------------------------------- 234 //IfLuid : ethernet_6 235 //IfIndex : 11 236 //State : connected 237 //Metric : 10 238 //... 239 var name string 240 lines := bytes.Split(out, []byte{'\r', '\n'}) 241 for _, line := range lines { 242 if bytes.HasPrefix(line, []byte("Interface ")) && bytes.HasSuffix(line, []byte(" Parameters")) { 243 f := line[len("Interface "):] 244 f = f[:len(f)-len(" Parameters")] 245 name = string(f) 246 continue 247 } 248 var isup bool 249 switch string(line) { 250 case "State : connected": 251 isup = true 252 case "State : disconnected": 253 isup = false 254 default: 255 continue 256 } 257 if name != "" { 258 if v, ok := ifaces[name]; ok && v != isup { 259 return fmt.Errorf("%s:%s isup=%v: ipv4 and ipv6 report different interface state", ipver, name, isup) 260 } 261 ifaces[name] = isup 262 name = "" 263 } 264 } 265 return nil 266 } 267 268 func TestInterfacesWithNetsh(t *testing.T) { 269 if isWindowsXP(t) { 270 t.Skip("Windows XP netsh command does not provide required functionality") 271 } 272 if !netshSpeaksEnglish(t) { 273 t.Skip("English version of netsh required for this test") 274 } 275 276 toString := func(name string, isup bool) string { 277 if isup { 278 return name + ":up" 279 } 280 return name + ":down" 281 } 282 283 ift, err := Interfaces() 284 if err != nil { 285 t.Fatal(err) 286 } 287 have := make([]string, 0) 288 for _, ifi := range ift { 289 have = append(have, toString(ifi.Name, ifi.Flags&FlagUp != 0)) 290 } 291 sort.Strings(have) 292 293 ifaces := make(map[string]bool) 294 err = netshInterfaceIPShowInterface("ipv6", ifaces) 295 if err != nil { 296 t.Fatal(err) 297 } 298 err = netshInterfaceIPShowInterface("ipv4", ifaces) 299 if err != nil { 300 t.Fatal(err) 301 } 302 want := make([]string, 0) 303 for name, isup := range ifaces { 304 want = append(want, toString(name, isup)) 305 } 306 sort.Strings(want) 307 308 if strings.Join(want, "/") != strings.Join(have, "/") { 309 t.Fatalf("unexpected interface list %q, want %q", have, want) 310 } 311 } 312 313 func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string { 314 // Address information is listed like: 315 // 316 //Configuration for interface "Local Area Connection" 317 // DHCP enabled: Yes 318 // IP Address: 10.0.0.2 319 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) 320 // IP Address: 10.0.0.3 321 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) 322 // Default Gateway: 10.0.0.254 323 // Gateway Metric: 0 324 // InterfaceMetric: 10 325 // 326 //Configuration for interface "Loopback Pseudo-Interface 1" 327 // DHCP enabled: No 328 // IP Address: 127.0.0.1 329 // Subnet Prefix: 127.0.0.0/8 (mask 255.0.0.0) 330 // InterfaceMetric: 50 331 // 332 addrs := make([]string, 0) 333 var addr, subnetprefix string 334 var processingOurInterface bool 335 lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) 336 for _, line := range lines { 337 if !processingOurInterface { 338 if !bytes.HasPrefix(line, []byte("Configuration for interface")) { 339 continue 340 } 341 if !bytes.Contains(line, []byte(`"`+name+`"`)) { 342 continue 343 } 344 processingOurInterface = true 345 continue 346 } 347 if len(line) == 0 { 348 break 349 } 350 if bytes.Contains(line, []byte("Subnet Prefix:")) { 351 f := bytes.Split(line, []byte{':'}) 352 if len(f) == 2 { 353 f = bytes.Split(f[1], []byte{'('}) 354 if len(f) == 2 { 355 f = bytes.Split(f[0], []byte{'/'}) 356 if len(f) == 2 { 357 subnetprefix = string(bytes.TrimSpace(f[1])) 358 if addr != "" && subnetprefix != "" { 359 addrs = append(addrs, addr+"/"+subnetprefix) 360 } 361 } 362 } 363 } 364 } 365 addr = "" 366 if bytes.Contains(line, []byte("IP Address:")) { 367 f := bytes.Split(line, []byte{':'}) 368 if len(f) == 2 { 369 addr = string(bytes.TrimSpace(f[1])) 370 } 371 } 372 } 373 return addrs 374 } 375 376 func netshInterfaceIPv6ShowAddress(name string, netshOutput []byte) []string { 377 // Address information is listed like: 378 // 379 //Address ::1 Parameters 380 //--------------------------------------------------------- 381 //Interface Luid : Loopback Pseudo-Interface 1 382 //Scope Id : 0.0 383 //Valid Lifetime : infinite 384 //Preferred Lifetime : infinite 385 //DAD State : Preferred 386 //Address Type : Other 387 //Skip as Source : false 388 // 389 //Address XXXX::XXXX:XXXX:XXXX:XXXX%11 Parameters 390 //--------------------------------------------------------- 391 //Interface Luid : Local Area Connection 392 //Scope Id : 0.11 393 //Valid Lifetime : infinite 394 //Preferred Lifetime : infinite 395 //DAD State : Preferred 396 //Address Type : Other 397 //Skip as Source : false 398 // 399 400 // TODO: need to test ipv6 netmask too, but netsh does not outputs it 401 var addr string 402 addrs := make([]string, 0) 403 lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) 404 for _, line := range lines { 405 if addr != "" { 406 if len(line) == 0 { 407 addr = "" 408 continue 409 } 410 if string(line) != "Interface Luid : "+name { 411 continue 412 } 413 addrs = append(addrs, addr) 414 addr = "" 415 continue 416 } 417 if !bytes.HasPrefix(line, []byte("Address")) { 418 continue 419 } 420 if !bytes.HasSuffix(line, []byte("Parameters")) { 421 continue 422 } 423 f := bytes.Split(line, []byte{' '}) 424 if len(f) != 3 { 425 continue 426 } 427 // remove scope ID if present 428 f = bytes.Split(f[1], []byte{'%'}) 429 430 // netsh can create IPv4-embedded IPv6 addresses, like fe80::5efe:192.168.140.1. 431 // Convert these to all hexadecimal fe80::5efe:c0a8:8c01 for later string comparisons. 432 ipv4Tail := regexp.MustCompile(`:\d+\.\d+\.\d+\.\d+$`) 433 if ipv4Tail.Match(f[0]) { 434 f[0] = []byte(ParseIP(string(f[0])).String()) 435 } 436 437 addr = string(bytes.ToLower(bytes.TrimSpace(f[0]))) 438 } 439 return addrs 440 } 441 442 func TestInterfaceAddrsWithNetsh(t *testing.T) { 443 if isWindowsXP(t) { 444 t.Skip("Windows XP netsh command does not provide required functionality") 445 } 446 if !netshSpeaksEnglish(t) { 447 t.Skip("English version of netsh required for this test") 448 } 449 450 outIPV4, err := runCmd("netsh", "interface", "ipv4", "show", "address") 451 if err != nil { 452 t.Fatal(err) 453 } 454 outIPV6, err := runCmd("netsh", "interface", "ipv6", "show", "address", "level=verbose") 455 if err != nil { 456 t.Fatal(err) 457 } 458 459 ift, err := Interfaces() 460 if err != nil { 461 t.Fatal(err) 462 } 463 for _, ifi := range ift { 464 // Skip the interface if it's down. 465 if (ifi.Flags & FlagUp) == 0 { 466 continue 467 } 468 have := make([]string, 0) 469 addrs, err := ifi.Addrs() 470 if err != nil { 471 t.Fatal(err) 472 } 473 for _, addr := range addrs { 474 switch addr := addr.(type) { 475 case *IPNet: 476 if addr.IP.To4() != nil { 477 have = append(have, addr.String()) 478 } 479 if addr.IP.To16() != nil && addr.IP.To4() == nil { 480 // netsh does not output netmask for ipv6, so ignore ipv6 mask 481 have = append(have, addr.IP.String()) 482 } 483 case *IPAddr: 484 if addr.IP.To4() != nil { 485 have = append(have, addr.String()) 486 } 487 if addr.IP.To16() != nil && addr.IP.To4() == nil { 488 // netsh does not output netmask for ipv6, so ignore ipv6 mask 489 have = append(have, addr.IP.String()) 490 } 491 } 492 } 493 sort.Strings(have) 494 495 want := netshInterfaceIPv4ShowAddress(ifi.Name, outIPV4) 496 wantIPv6 := netshInterfaceIPv6ShowAddress(ifi.Name, outIPV6) 497 want = append(want, wantIPv6...) 498 sort.Strings(want) 499 500 if strings.Join(want, "/") != strings.Join(have, "/") { 501 t.Errorf("%s: unexpected addresses list %q, want %q", ifi.Name, have, want) 502 } 503 } 504 } 505 506 func getmacSpeaksEnglish(t *testing.T) bool { 507 out, err := runCmd("getmac", "/?") 508 if err != nil { 509 t.Fatal(err) 510 } 511 return bytes.Contains(out, []byte("network adapters on a system")) 512 } 513 514 func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { 515 if isWindowsXP(t) { 516 t.Skip("Windows XP does not have powershell command") 517 } 518 if !getmacSpeaksEnglish(t) { 519 t.Skip("English version of getmac required for this test") 520 } 521 522 ift, err := Interfaces() 523 if err != nil { 524 t.Fatal(err) 525 } 526 have := make(map[string]string) 527 for _, ifi := range ift { 528 if ifi.Flags&FlagLoopback != 0 { 529 // no MAC address for loopback interfaces 530 continue 531 } 532 have[ifi.Name] = ifi.HardwareAddr.String() 533 } 534 535 out, err := runCmd("getmac", "/fo", "list", "/v") 536 if err != nil { 537 t.Fatal(err) 538 } 539 // getmac output looks like: 540 // 541 //Connection Name: Local Area Connection 542 //Network Adapter: Intel Gigabit Network Connection 543 //Physical Address: XX-XX-XX-XX-XX-XX 544 //Transport Name: \Device\Tcpip_{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 545 // 546 //Connection Name: Wireless Network Connection 547 //Network Adapter: Wireles WLAN Card 548 //Physical Address: XX-XX-XX-XX-XX-XX 549 //Transport Name: Media disconnected 550 // 551 //Connection Name: Bluetooth Network Connection 552 //Network Adapter: Bluetooth Device (Personal Area Network) 553 //Physical Address: N/A 554 //Transport Name: Hardware not present 555 // 556 //Connection Name: VMware Network Adapter VMnet8 557 //Network Adapter: VMware Virtual Ethernet Adapter for VMnet8 558 //Physical Address: Disabled 559 //Transport Name: Disconnected 560 // 561 want := make(map[string]string) 562 var name string 563 lines := bytes.Split(out, []byte{'\r', '\n'}) 564 for _, line := range lines { 565 if bytes.Contains(line, []byte("Connection Name:")) { 566 f := bytes.Split(line, []byte{':'}) 567 if len(f) != 2 { 568 t.Fatalf("unexpected \"Connection Name\" line: %q", line) 569 } 570 name = string(bytes.TrimSpace(f[1])) 571 if name == "" { 572 t.Fatalf("empty name on \"Connection Name\" line: %q", line) 573 } 574 } 575 if bytes.Contains(line, []byte("Physical Address:")) { 576 if name == "" { 577 t.Fatalf("no matching name found: %q", string(out)) 578 } 579 f := bytes.Split(line, []byte{':'}) 580 if len(f) != 2 { 581 t.Fatalf("unexpected \"Physical Address\" line: %q", line) 582 } 583 addr := string(bytes.ToLower(bytes.TrimSpace(f[1]))) 584 if addr == "" { 585 t.Fatalf("empty address on \"Physical Address\" line: %q", line) 586 } 587 if addr == "disabled" || addr == "n/a" { 588 continue 589 } 590 addr = strings.Replace(addr, "-", ":", -1) 591 want[name] = addr 592 name = "" 593 } 594 } 595 596 for name, wantAddr := range want { 597 haveAddr, ok := have[name] 598 if !ok { 599 t.Errorf("getmac lists %q, but it could not be found among Go interfaces %v", name, have) 600 continue 601 } 602 if haveAddr != wantAddr { 603 t.Errorf("unexpected MAC address for %q - %v, want %v", name, haveAddr, wantAddr) 604 continue 605 } 606 } 607 }