github.com/fjballest/golang@v0.0.0-20151209143359-e4c5fe594ca8/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 "sort" 16 "strings" 17 "syscall" 18 "testing" 19 "time" 20 ) 21 22 func toErrno(err error) (syscall.Errno, bool) { 23 operr, ok := err.(*OpError) 24 if !ok { 25 return 0, false 26 } 27 syserr, ok := operr.Err.(*os.SyscallError) 28 if !ok { 29 return 0, false 30 } 31 errno, ok := syserr.Err.(syscall.Errno) 32 if !ok { 33 return 0, false 34 } 35 return errno, true 36 } 37 38 // TestAcceptIgnoreSomeErrors tests that windows TCPListener.AcceptTCP 39 // handles broken connections. It verifies that broken connections do 40 // not affect future connections. 41 func TestAcceptIgnoreSomeErrors(t *testing.T) { 42 recv := func(ln Listener, ignoreSomeReadErrors bool) (string, error) { 43 c, err := ln.Accept() 44 if err != nil { 45 // Display windows errno in error message. 46 errno, ok := toErrno(err) 47 if !ok { 48 return "", err 49 } 50 return "", fmt.Errorf("%v (windows errno=%d)", err, errno) 51 } 52 defer c.Close() 53 54 b := make([]byte, 100) 55 n, err := c.Read(b) 56 if err == nil || err == io.EOF { 57 return string(b[:n]), nil 58 } 59 errno, ok := toErrno(err) 60 if ok && ignoreSomeReadErrors && (errno == syscall.ERROR_NETNAME_DELETED || errno == syscall.WSAECONNRESET) { 61 return "", nil 62 } 63 return "", err 64 } 65 66 send := func(addr string, data string) error { 67 c, err := Dial("tcp", addr) 68 if err != nil { 69 return err 70 } 71 defer c.Close() 72 73 b := []byte(data) 74 n, err := c.Write(b) 75 if err != nil { 76 return err 77 } 78 if n != len(b) { 79 return fmt.Errorf(`Only %d chars of string "%s" sent`, n, data) 80 } 81 return nil 82 } 83 84 if envaddr := os.Getenv("GOTEST_DIAL_ADDR"); envaddr != "" { 85 // In child process. 86 c, err := Dial("tcp", envaddr) 87 if err != nil { 88 t.Fatal(err) 89 } 90 fmt.Printf("sleeping\n") 91 time.Sleep(time.Minute) // process will be killed here 92 c.Close() 93 } 94 95 ln, err := Listen("tcp", "127.0.0.1:0") 96 if err != nil { 97 t.Fatal(err) 98 } 99 defer ln.Close() 100 101 // Start child process that connects to our listener. 102 cmd := exec.Command(os.Args[0], "-test.run=TestAcceptIgnoreSomeErrors") 103 cmd.Env = append(os.Environ(), "GOTEST_DIAL_ADDR="+ln.Addr().String()) 104 stdout, err := cmd.StdoutPipe() 105 if err != nil { 106 t.Fatalf("cmd.StdoutPipe failed: %v", err) 107 } 108 err = cmd.Start() 109 if err != nil { 110 t.Fatalf("cmd.Start failed: %v\n", err) 111 } 112 outReader := bufio.NewReader(stdout) 113 for { 114 s, err := outReader.ReadString('\n') 115 if err != nil { 116 t.Fatalf("reading stdout failed: %v", err) 117 } 118 if s == "sleeping\n" { 119 break 120 } 121 } 122 defer cmd.Wait() // ignore error - we know it is getting killed 123 124 const alittle = 100 * time.Millisecond 125 time.Sleep(alittle) 126 cmd.Process.Kill() // the only way to trigger the errors 127 time.Sleep(alittle) 128 129 // Send second connection data (with delay in a separate goroutine). 130 result := make(chan error) 131 go func() { 132 time.Sleep(alittle) 133 err := send(ln.Addr().String(), "abc") 134 if err != nil { 135 result <- err 136 } 137 result <- nil 138 }() 139 defer func() { 140 err := <-result 141 if err != nil { 142 t.Fatalf("send failed: %v", err) 143 } 144 }() 145 146 // Receive first or second connection. 147 s, err := recv(ln, true) 148 if err != nil { 149 t.Fatalf("recv failed: %v", err) 150 } 151 switch s { 152 case "": 153 // First connection data is received, let's get second connection data. 154 case "abc": 155 // First connection is lost forever, but that is ok. 156 return 157 default: 158 t.Fatalf(`"%s" received from recv, but "" or "abc" expected`, s) 159 } 160 161 // Get second connection data. 162 s, err = recv(ln, false) 163 if err != nil { 164 t.Fatalf("recv failed: %v", err) 165 } 166 if s != "abc" { 167 t.Fatalf(`"%s" received from recv, but "abc" expected`, s) 168 } 169 } 170 171 func isWindowsXP(t *testing.T) bool { 172 v, err := syscall.GetVersion() 173 if err != nil { 174 t.Fatalf("GetVersion failed: %v", err) 175 } 176 major := byte(v) 177 return major < 6 178 } 179 180 func runCmd(args ...string) ([]byte, error) { 181 removeUTF8BOM := func(b []byte) []byte { 182 if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF { 183 return b[3:] 184 } 185 return b 186 } 187 f, err := ioutil.TempFile("", "netcmd") 188 if err != nil { 189 return nil, err 190 } 191 f.Close() 192 defer os.Remove(f.Name()) 193 cmd := fmt.Sprintf(`%s | Out-File "%s" -encoding UTF8`, strings.Join(args, " "), f.Name()) 194 out, err := exec.Command("powershell", "-Command", cmd).CombinedOutput() 195 if err != nil { 196 if len(out) != 0 { 197 return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out))) 198 } 199 var err2 error 200 out, err2 = ioutil.ReadFile(f.Name()) 201 if err2 != nil { 202 return nil, err2 203 } 204 if len(out) != 0 { 205 return nil, fmt.Errorf("%s failed: %v: %q", args[0], err, string(removeUTF8BOM(out))) 206 } 207 return nil, fmt.Errorf("%s failed: %v", args[0], err) 208 } 209 out, err = ioutil.ReadFile(f.Name()) 210 if err != nil { 211 return nil, err 212 } 213 return removeUTF8BOM(out), nil 214 } 215 216 func netshInterfaceIPShowConfig() ([]string, error) { 217 out, err := runCmd("netsh", "interface", "ip", "show", "config") 218 if err != nil { 219 return nil, err 220 } 221 lines := bytes.Split(out, []byte{'\r', '\n'}) 222 names := make([]string, 0) 223 for _, line := range lines { 224 f := bytes.Split(line, []byte{'"'}) 225 if len(f) == 3 { 226 names = append(names, string(f[1])) 227 } 228 } 229 return names, nil 230 } 231 232 func TestInterfacesWithNetsh(t *testing.T) { 233 if isWindowsXP(t) { 234 t.Skip("Windows XP netsh command does not provide required functionality") 235 } 236 ift, err := Interfaces() 237 if err != nil { 238 t.Fatal(err) 239 } 240 have := make([]string, 0) 241 for _, ifi := range ift { 242 have = append(have, ifi.Name) 243 } 244 sort.Strings(have) 245 246 want, err := netshInterfaceIPShowConfig() 247 if err != nil { 248 t.Fatal(err) 249 } 250 sort.Strings(want) 251 252 if strings.Join(want, "/") != strings.Join(have, "/") { 253 t.Fatalf("unexpected interface list %q, want %q", have, want) 254 } 255 } 256 257 func netshInterfaceIPv4ShowAddress(name string) ([]string, error) { 258 out, err := runCmd("netsh", "interface", "ipv4", "show", "address", "name=\""+name+"\"") 259 if err != nil { 260 return nil, err 261 } 262 // adress information is listed like: 263 // IP Address: 10.0.0.2 264 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) 265 // IP Address: 10.0.0.3 266 // Subnet Prefix: 10.0.0.0/24 (mask 255.255.255.0) 267 addrs := make([]string, 0) 268 var addr, subnetprefix string 269 lines := bytes.Split(out, []byte{'\r', '\n'}) 270 for _, line := range lines { 271 if bytes.Contains(line, []byte("Subnet Prefix:")) { 272 f := bytes.Split(line, []byte{':'}) 273 if len(f) == 2 { 274 f = bytes.Split(f[1], []byte{'('}) 275 if len(f) == 2 { 276 f = bytes.Split(f[0], []byte{'/'}) 277 if len(f) == 2 { 278 subnetprefix = string(bytes.TrimSpace(f[1])) 279 if addr != "" && subnetprefix != "" { 280 addrs = append(addrs, addr+"/"+subnetprefix) 281 } 282 } 283 } 284 } 285 } 286 addr = "" 287 if bytes.Contains(line, []byte("IP Address:")) { 288 f := bytes.Split(line, []byte{':'}) 289 if len(f) == 2 { 290 addr = string(bytes.TrimSpace(f[1])) 291 } 292 } 293 } 294 return addrs, nil 295 } 296 297 func netshInterfaceIPv6ShowAddress(name string) ([]string, error) { 298 // TODO: need to test ipv6 netmask too, but netsh does not outputs it 299 out, err := runCmd("netsh", "interface", "ipv6", "show", "address", "interface=\""+name+"\"") 300 if err != nil { 301 return nil, err 302 } 303 addrs := make([]string, 0) 304 lines := bytes.Split(out, []byte{'\r', '\n'}) 305 for _, line := range lines { 306 if !bytes.HasPrefix(line, []byte("Address")) { 307 continue 308 } 309 if !bytes.HasSuffix(line, []byte("Parameters")) { 310 continue 311 } 312 f := bytes.Split(line, []byte{' '}) 313 if len(f) != 3 { 314 continue 315 } 316 // remove scope ID if present 317 f = bytes.Split(f[1], []byte{'%'}) 318 addrs = append(addrs, string(bytes.TrimSpace(f[0]))) 319 } 320 return addrs, nil 321 } 322 323 func TestInterfaceAddrsWithNetsh(t *testing.T) { 324 t.Skip("skipping test; see https://golang.org/issue/12811") 325 if isWindowsXP(t) { 326 t.Skip("Windows XP netsh command does not provide required functionality") 327 } 328 ift, err := Interfaces() 329 if err != nil { 330 t.Fatal(err) 331 } 332 for _, ifi := range ift { 333 have := make([]string, 0) 334 addrs, err := ifi.Addrs() 335 if err != nil { 336 t.Fatal(err) 337 } 338 for _, addr := range addrs { 339 switch addr := addr.(type) { 340 case *IPNet: 341 if addr.IP.To4() != nil { 342 have = append(have, addr.String()) 343 } 344 if addr.IP.To16() != nil && addr.IP.To4() == nil { 345 // netsh does not output netmask for ipv6, so ignore ipv6 mask 346 have = append(have, addr.IP.String()) 347 } 348 case *IPAddr: 349 if addr.IP.To4() != nil { 350 have = append(have, addr.String()) 351 } 352 if addr.IP.To16() != nil && addr.IP.To4() == nil { 353 // netsh does not output netmask for ipv6, so ignore ipv6 mask 354 have = append(have, addr.IP.String()) 355 } 356 } 357 } 358 sort.Strings(have) 359 360 want, err := netshInterfaceIPv4ShowAddress(ifi.Name) 361 if err != nil { 362 t.Fatal(err) 363 } 364 wantIPv6, err := netshInterfaceIPv6ShowAddress(ifi.Name) 365 if err != nil { 366 t.Fatal(err) 367 } 368 want = append(want, wantIPv6...) 369 sort.Strings(want) 370 371 if strings.Join(want, "/") != strings.Join(have, "/") { 372 t.Errorf("%s: unexpected addresses list %q, want %q", ifi.Name, have, want) 373 } 374 } 375 } 376 377 func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { 378 t.Skip("skipping test; see https://golang.org/issue/12691") 379 if isWindowsXP(t) { 380 t.Skip("Windows XP does not have powershell command") 381 } 382 ift, err := Interfaces() 383 if err != nil { 384 t.Fatal(err) 385 } 386 have := make([]string, 0) 387 for _, ifi := range ift { 388 if ifi.Flags&FlagLoopback != 0 { 389 // no MAC for loopback interfaces 390 continue 391 } 392 have = append(have, ifi.Name+"="+ifi.HardwareAddr.String()) 393 } 394 sort.Strings(have) 395 396 out, err := runCmd("getmac", "/fo", "list", "/v") 397 if err != nil { 398 t.Fatal(err) 399 } 400 // getmac output looks like: 401 // 402 //Connection Name: Local Area Connection 403 //Network Adapter: Intel Gigabit Network Connection 404 //Physical Address: XX-XX-XX-XX-XX-XX 405 //Transport Name: \Device\Tcpip_{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 406 // 407 //Connection Name: Wireless Network Connection 408 //Network Adapter: Wireles WLAN Card 409 //Physical Address: XX-XX-XX-XX-XX-XX 410 //Transport Name: Media disconnected 411 // 412 //Connection Name: Bluetooth Network Connection 413 //Network Adapter: Bluetooth Device (Personal Area Network) 414 //Physical Address: XX-XX-XX-XX-XX-XX 415 //Transport Name: Media disconnected 416 // 417 want := make([]string, 0) 418 var name string 419 lines := bytes.Split(out, []byte{'\r', '\n'}) 420 for _, line := range lines { 421 if bytes.Contains(line, []byte("Connection Name:")) { 422 f := bytes.Split(line, []byte{':'}) 423 if len(f) != 2 { 424 t.Fatal("unexpected \"Connection Name\" line: %q", line) 425 } 426 name = string(bytes.TrimSpace(f[1])) 427 if name == "" { 428 t.Fatal("empty name on \"Connection Name\" line: %q", line) 429 } 430 } 431 if bytes.Contains(line, []byte("Physical Address:")) { 432 if name == "" { 433 t.Fatal("no matching name found: %q", string(out)) 434 } 435 f := bytes.Split(line, []byte{':'}) 436 if len(f) != 2 { 437 t.Fatal("unexpected \"Physical Address\" line: %q", line) 438 } 439 addr := string(bytes.TrimSpace(f[1])) 440 if addr == "" { 441 t.Fatal("empty address on \"Physical Address\" line: %q", line) 442 } 443 addr = strings.Replace(addr, "-", ":", -1) 444 want = append(want, name+"="+addr) 445 name = "" 446 } 447 } 448 sort.Strings(want) 449 450 if strings.Join(want, "/") != strings.Join(have, "/") { 451 t.Fatalf("unexpected MAC addresses %q, want %q", have, want) 452 } 453 }