]> Cypherpunks repositories - gostls13.git/commitdiff
net: re-implement Interfaces and InterfaceAddrs for IPNet, IPv6 on Windows
authormattn <mattn.jp@gmail.com>
Mon, 19 Jan 2015 07:56:04 +0000 (16:56 +0900)
committerAlex Brainman <alex.brainman@gmail.com>
Tue, 10 Feb 2015 00:44:05 +0000 (00:44 +0000)
Fixes #5395

Change-Id: I4322bc8a974d04d9bae6b48c71c5d32d9252973c
Reviewed-on: https://go-review.googlesource.com/3024
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
src/go/build/deps_test.go
src/internal/syscall/windows/syscall_windows.go [new file with mode: 0644]
src/internal/syscall/windows/zsyscall_windows.go [new file with mode: 0644]
src/net/interface_test.go
src/net/interface_windows.go

index b3c1105156543a78502e95077a629101ffd4d773..98201a5d9635e05187061041070fe035f1ebe046 100644 (file)
@@ -240,7 +240,7 @@ var pkgDeps = map[string][]string{
        // 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 (file)
index 0000000..2541a83
--- /dev/null
@@ -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 (file)
index 0000000..90e2034
--- /dev/null
@@ -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
+}
index fbf15de4d37791f74541c8e0a7d100d71263b7d2..15c0cd7be4dc73b5954d81b31ab24f2886eed034 100644 (file)
@@ -38,8 +38,7 @@ func ipv6LinkLocalUnicastAddr(ifi *Interface) string {
                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 ipv6LinkLocalUnicastAddr(ifi *Interface) string {
 }
 
 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 TestInterfaces(t *testing.T) {
 }
 
 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)
index 0759dc255d4346f2fdb0d6565b2d2327a12288dd..438dc874d62d4c35305779b64769fb1e7a727821 100644 (file)
 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++
-       }
-       return string(a[:i])
-}
+func getAdapters() (*windows.IpAdapterAddresses, error) {
+       block := uint32(unsafe.Sizeof(windows.IpAdapterAddresses{}))
 
-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)
+       // 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 a, nil
+       return &addrs[0], 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
+                       }
+                       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
                        }
-
-                       name := bytePtrToString(&ai.AdapterName[0])
-
                        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 @@ func interfaceTable(ifindex int) ([]Interface, error) {
 // 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
 }