net: re-implement Interfaces and InterfaceAddrs for IPNet, IPv6 on Windows

Fixes #5395

Change-Id: I4322bc8a974d04d9bae6b48c71c5d32d9252973c
Reviewed-on: https://go-review.googlesource.com/3024
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index b3c1105..98201a5 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -240,7 +240,7 @@
 	// Basic networking.
 	// Because net must be used by any package that wants to
 	// do networking portably, it must have a small dependency set: just L1+basic os.
-	"net": {"L1", "CGO", "os", "syscall", "time"},
+	"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows"},
 
 	// NET enables use of basic network-related packages.
 	"NET": {
diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go
new file mode 100644
index 0000000..2541a83
--- /dev/null
+++ b/src/internal/syscall/windows/syscall_windows.go
@@ -0,0 +1,99 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package windows
+
+import (
+	"syscall"
+)
+
+//go:generate go run ../../../syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
+
+const GAA_FLAG_INCLUDE_PREFIX = 0x00000010
+
+const IF_TYPE_SOFTWARE_LOOPBACK = 24
+
+type SocketAddress struct {
+	Sockaddr       *syscall.RawSockaddrAny
+	SockaddrLength int32
+}
+
+type IpAdapterUnicastAddress struct {
+	Length             uint32
+	Flags              uint32
+	Next               *IpAdapterUnicastAddress
+	Address            SocketAddress
+	PrefixOrigin       int32
+	SuffixOrigin       int32
+	DadState           int32
+	ValidLifetime      uint32
+	PreferredLifetime  uint32
+	LeaseLifetime      uint32
+	OnLinkPrefixLength uint8
+}
+
+type IpAdapterAnycastAddress struct {
+	Length  uint32
+	Flags   uint32
+	Next    *IpAdapterAnycastAddress
+	Address SocketAddress
+}
+
+type IpAdapterMulticastAddress struct {
+	Length  uint32
+	Flags   uint32
+	Next    *IpAdapterMulticastAddress
+	Address SocketAddress
+}
+
+type IpAdapterDnsServerAdapter struct {
+	Length   uint32
+	Reserved uint32
+	Next     *IpAdapterDnsServerAdapter
+	Address  SocketAddress
+}
+
+type IpAdapterPrefix struct {
+	Length       uint32
+	Flags        uint32
+	Next         *IpAdapterPrefix
+	Address      SocketAddress
+	PrefixLength uint32
+}
+
+type IpAdapterAddresses struct {
+	Length                uint32
+	IfIndex               uint32
+	Next                  *IpAdapterAddresses
+	AdapterName           *byte
+	FirstUnicastAddress   *IpAdapterUnicastAddress
+	FirstAnycastAddress   *IpAdapterAnycastAddress
+	FirstMulticastAddress *IpAdapterMulticastAddress
+	FirstDnsServerAddress *IpAdapterDnsServerAdapter
+	DnsSuffix             *uint16
+	Description           *uint16
+	FriendlyName          *uint16
+	PhysicalAddress       [syscall.MAX_ADAPTER_ADDRESS_LENGTH]byte
+	PhysicalAddressLength uint32
+	Flags                 uint32
+	Mtu                   uint32
+	IfType                uint32
+	OperStatus            uint32
+	Ipv6IfIndex           uint32
+	ZoneIndices           [16]uint32
+	FirstPrefix           *IpAdapterPrefix
+	/* more fields might be present here. */
+}
+
+const (
+	IfOperStatusUp             = 1
+	IfOperStatusDown           = 2
+	IfOperStatusTesting        = 3
+	IfOperStatusUnknown        = 4
+	IfOperStatusDormant        = 5
+	IfOperStatusNotPresent     = 6
+	IfOperStatusLowerLayerDown = 7
+)
+
+//sys GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) = iphlpapi.GetAdaptersAddresses
diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go
new file mode 100644
index 0000000..90e2034
--- /dev/null
+++ b/src/internal/syscall/windows/zsyscall_windows.go
@@ -0,0 +1,20 @@
+// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
+
+package windows
+
+import "unsafe"
+import "syscall"
+
+var (
+	modiphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
+
+	procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
+)
+
+func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizeOfPointer *uint32) (errcode error) {
+	r0, _, _ := syscall.Syscall6(procGetAdaptersAddresses.Addr(), 5, uintptr(family), uintptr(flags), uintptr(reserved), uintptr(unsafe.Pointer(adapterAddresses)), uintptr(unsafe.Pointer(sizeOfPointer)), 0)
+	if r0 != 0 {
+		errcode = syscall.Errno(r0)
+	}
+	return
+}
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index fbf15de..15c0cd7 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -38,8 +38,7 @@
 		return ""
 	}
 	for _, ifa := range ifat {
-		switch ifa := ifa.(type) {
-		case *IPNet:
+		if ifa, ok := ifa.(*IPNet); ok {
 			if ifa.IP.To4() == nil && ifa.IP.IsLinkLocalUnicast() {
 				return ifa.IP.String()
 			}
@@ -49,10 +48,6 @@
 }
 
 func TestInterfaces(t *testing.T) {
-	if runtime.GOOS == "windows" {
-		t.Skip("temporarily disabled until golang.org/issue/5395 is fixed")
-	}
-
 	ift, err := Interfaces()
 	if err != nil {
 		t.Fatal(err)
@@ -110,10 +105,6 @@
 }
 
 func TestInterfaceAddrs(t *testing.T) {
-	if runtime.GOOS == "windows" {
-		t.Skip("temporarily disabled until golang.org/issue/5395 is fixed")
-	}
-
 	ift, err := Interfaces()
 	if err != nil {
 		t.Fatal(err)
diff --git a/src/net/interface_windows.go b/src/net/interface_windows.go
index 0759dc2..438dc87 100644
--- a/src/net/interface_windows.go
+++ b/src/net/interface_windows.go
@@ -5,123 +5,139 @@
 package net
 
 import (
+	"internal/syscall/windows"
 	"os"
 	"syscall"
 	"unsafe"
 )
 
-func bytePtrToString(p *uint8) string {
-	a := (*[10000]uint8)(unsafe.Pointer(p))
-	i := 0
-	for a[i] != 0 {
-		i++
+func getAdapters() (*windows.IpAdapterAddresses, error) {
+	block := uint32(unsafe.Sizeof(windows.IpAdapterAddresses{}))
+
+	// pre-allocate a 15KB working buffer pointed to by the AdapterAddresses
+	// parameter.
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx
+	size := uint32(15000)
+
+	var addrs []windows.IpAdapterAddresses
+	for {
+		addrs = make([]windows.IpAdapterAddresses, size/block+1)
+		err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, &addrs[0], &size)
+		if err == nil {
+			break
+		}
+		if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {
+			return nil, os.NewSyscallError("GetAdaptersAddresses", err)
+		}
 	}
-	return string(a[:i])
+	return &addrs[0], nil
 }
 
-func getAdapterList() (*syscall.IpAdapterInfo, error) {
-	b := make([]byte, 1000)
-	l := uint32(len(b))
-	a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
-	// TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
-	// contains IPv4 address list only. We should use another API
-	// for fetching IPv6 stuff from the kernel.
-	err := syscall.GetAdaptersInfo(a, &l)
-	if err == syscall.ERROR_BUFFER_OVERFLOW {
-		b = make([]byte, l)
-		a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
-		err = syscall.GetAdaptersInfo(a, &l)
-	}
-	if err != nil {
-		return nil, os.NewSyscallError("GetAdaptersInfo", err)
-	}
-	return a, nil
-}
-
-func getInterfaceList() ([]syscall.InterfaceInfo, error) {
+func getInterfaceInfos() ([]syscall.InterfaceInfo, error) {
 	s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
 	if err != nil {
 		return nil, os.NewSyscallError("Socket", err)
 	}
 	defer syscall.Closesocket(s)
 
-	ii := [20]syscall.InterfaceInfo{}
+	iia := [20]syscall.InterfaceInfo{}
 	ret := uint32(0)
-	size := uint32(unsafe.Sizeof(ii))
-	err = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&ii[0])), size, &ret, nil, 0)
+	size := uint32(unsafe.Sizeof(iia))
+	err = syscall.WSAIoctl(s, syscall.SIO_GET_INTERFACE_LIST, nil, 0, (*byte)(unsafe.Pointer(&iia[0])), size, &ret, nil, 0)
 	if err != nil {
 		return nil, os.NewSyscallError("WSAIoctl", err)
 	}
-	c := ret / uint32(unsafe.Sizeof(ii[0]))
-	return ii[:c-1], nil
+	iilen := ret / uint32(unsafe.Sizeof(iia[0]))
+	return iia[:iilen-1], nil
+}
+
+func bytesEqualIP(a []byte, b []int8) bool {
+	for i := 0; i < len(a); i++ {
+		if a[i] != byte(b[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+func findInterfaceInfo(iis []syscall.InterfaceInfo, paddr *windows.IpAdapterAddresses) *syscall.InterfaceInfo {
+	for _, ii := range iis {
+		iaddr := (*syscall.RawSockaddr)(unsafe.Pointer(&ii.Address))
+		puni := paddr.FirstUnicastAddress
+		for ; puni != nil; puni = puni.Next {
+			if iaddr.Family == puni.Address.Sockaddr.Addr.Family {
+				switch iaddr.Family {
+				case syscall.AF_INET:
+					a := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr
+					if bytesEqualIP(a[:], puni.Address.Sockaddr.Addr.Data[2:]) {
+						return &ii
+					}
+				case syscall.AF_INET6:
+					a := (*syscall.RawSockaddrInet6)(unsafe.Pointer(&ii.Address)).Addr
+					if bytesEqualIP(a[:], puni.Address.Sockaddr.Addr.Data[2:]) {
+						return &ii
+					}
+				default:
+					continue
+				}
+			}
+		}
+	}
+	return nil
 }
 
 // If the ifindex is zero, interfaceTable returns mappings of all
 // network interfaces.  Otherwise it returns a mapping of a specific
 // interface.
 func interfaceTable(ifindex int) ([]Interface, error) {
-	ai, err := getAdapterList()
+	paddr, err := getAdapters()
 	if err != nil {
 		return nil, err
 	}
 
-	ii, err := getInterfaceList()
+	iis, err := getInterfaceInfos()
 	if err != nil {
 		return nil, err
 	}
 
 	var ift []Interface
-	for ; ai != nil; ai = ai.Next {
-		index := ai.Index
+	for ; paddr != nil; paddr = paddr.Next {
+		index := paddr.IfIndex
+		if paddr.Ipv6IfIndex != 0 {
+			index = paddr.Ipv6IfIndex
+		}
 		if ifindex == 0 || ifindex == int(index) {
+			ii := findInterfaceInfo(iis, paddr)
+			if ii == nil {
+				continue
+			}
 			var flags Flags
-
-			row := syscall.MibIfRow{Index: index}
-			e := syscall.GetIfEntry(&row)
-			if e != nil {
-				return nil, os.NewSyscallError("GetIfEntry", e)
+			if paddr.Flags&windows.IfOperStatusUp != 0 {
+				flags |= FlagUp
 			}
-
-			for _, ii := range ii {
-				ip := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&ii.Address)).Addr
-				ipv4 := IPv4(ip[0], ip[1], ip[2], ip[3])
-				ipl := &ai.IpAddressList
-				for ipl != nil {
-					ips := bytePtrToString(&ipl.IpAddress.String[0])
-					if ipv4.Equal(parseIPv4(ips)) {
-						break
-					}
-					ipl = ipl.Next
-				}
-				if ipl == nil {
-					continue
-				}
-				if ii.Flags&syscall.IFF_UP != 0 {
-					flags |= FlagUp
-				}
-				if ii.Flags&syscall.IFF_LOOPBACK != 0 {
-					flags |= FlagLoopback
-				}
-				if ii.Flags&syscall.IFF_BROADCAST != 0 {
-					flags |= FlagBroadcast
-				}
-				if ii.Flags&syscall.IFF_POINTTOPOINT != 0 {
-					flags |= FlagPointToPoint
-				}
-				if ii.Flags&syscall.IFF_MULTICAST != 0 {
-					flags |= FlagMulticast
-				}
+			if paddr.IfType&windows.IF_TYPE_SOFTWARE_LOOPBACK != 0 {
+				flags |= FlagLoopback
 			}
-
-			name := bytePtrToString(&ai.AdapterName[0])
-
+			if ii.Flags&syscall.IFF_BROADCAST != 0 {
+				flags |= FlagBroadcast
+			}
+			if ii.Flags&syscall.IFF_POINTTOPOINT != 0 {
+				flags |= FlagPointToPoint
+			}
+			if ii.Flags&syscall.IFF_MULTICAST != 0 {
+				flags |= FlagMulticast
+			}
 			ifi := Interface{
 				Index:        int(index),
-				MTU:          int(row.Mtu),
-				Name:         name,
-				HardwareAddr: HardwareAddr(row.PhysAddr[:row.PhysAddrLen]),
-				Flags:        flags}
+				MTU:          int(paddr.Mtu),
+				Name:         syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(paddr.FriendlyName)))[:]),
+				HardwareAddr: HardwareAddr(paddr.PhysicalAddress[:]),
+				Flags:        flags,
+			}
 			ift = append(ift, ifi)
+			if ifindex == int(ifi.Index) {
+				break
+			}
 		}
 	}
 	return ift, nil
@@ -131,28 +147,86 @@
 // network interfaces.  Otherwise it returns addresses for a specific
 // interface.
 func interfaceAddrTable(ifi *Interface) ([]Addr, error) {
-	ai, err := getAdapterList()
+	paddr, err := getAdapters()
 	if err != nil {
 		return nil, err
 	}
 
 	var ifat []Addr
-	for ; ai != nil; ai = ai.Next {
-		index := ai.Index
+	for ; paddr != nil; paddr = paddr.Next {
+		index := paddr.IfIndex
+		if paddr.Ipv6IfIndex != 0 {
+			index = paddr.Ipv6IfIndex
+		}
 		if ifi == nil || ifi.Index == int(index) {
-			ipl := &ai.IpAddressList
-			for ; ipl != nil; ipl = ipl.Next {
-				ifa := IPAddr{IP: parseIPv4(bytePtrToString(&ipl.IpAddress.String[0]))}
-				ifat = append(ifat, ifa.toAddr())
+			puni := paddr.FirstUnicastAddress
+			for ; puni != nil; puni = puni.Next {
+				if sa, err := puni.Address.Sockaddr.Sockaddr(); err == nil {
+					switch sav := sa.(type) {
+					case *syscall.SockaddrInet4:
+						ifa := &IPNet{IP: make(IP, IPv4len), Mask: CIDRMask(int(puni.Address.SockaddrLength), 8*IPv4len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa)
+					case *syscall.SockaddrInet6:
+						ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(puni.Address.SockaddrLength), 8*IPv6len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa)
+					}
+				}
+			}
+			pany := paddr.FirstAnycastAddress
+			for ; pany != nil; pany = pany.Next {
+				if sa, err := pany.Address.Sockaddr.Sockaddr(); err == nil {
+					switch sav := sa.(type) {
+					case *syscall.SockaddrInet4:
+						ifa := &IPNet{IP: make(IP, IPv4len), Mask: CIDRMask(int(pany.Address.SockaddrLength), 8*IPv4len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa)
+					case *syscall.SockaddrInet6:
+						ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(int(pany.Address.SockaddrLength), 8*IPv6len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa)
+					}
+				}
 			}
 		}
 	}
+
 	return ifat, nil
 }
 
 // interfaceMulticastAddrTable returns addresses for a specific
 // interface.
 func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
-	// TODO(mikio): Implement this like other platforms.
-	return nil, nil
+	paddr, err := getAdapters()
+	if err != nil {
+		return nil, err
+	}
+
+	var ifat []Addr
+	for ; paddr != nil; paddr = paddr.Next {
+		index := paddr.IfIndex
+		if paddr.Ipv6IfIndex != 0 {
+			index = paddr.Ipv6IfIndex
+		}
+		if ifi == nil || ifi.Index == int(index) {
+			pmul := paddr.FirstMulticastAddress
+			for ; pmul != nil; pmul = pmul.Next {
+				if sa, err := pmul.Address.Sockaddr.Sockaddr(); err == nil {
+					switch sav := sa.(type) {
+					case *syscall.SockaddrInet4:
+						ifa := &IPAddr{IP: make(IP, IPv4len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa.toAddr())
+					case *syscall.SockaddrInet6:
+						ifa := &IPAddr{IP: make(IP, IPv6len)}
+						copy(ifa.IP, sav.Addr[:])
+						ifat = append(ifat, ifa.toAddr())
+					}
+				}
+			}
+		}
+	}
+
+	return ifat, nil
 }