mirror of
https://github.com/golang/go
synced 2024-11-18 13:44:48 -07:00
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>
This commit is contained in:
parent
e6fbce3596
commit
ea22a08fd4
@ -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": {
|
||||
|
99
src/internal/syscall/windows/syscall_windows.go
Normal file
99
src/internal/syscall/windows/syscall_windows.go
Normal file
@ -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
|
20
src/internal/syscall/windows/zsyscall_windows.go
Normal file
20
src/internal/syscall/windows/zsyscall_windows.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
|
@ -5,101 +5,117 @@
|
||||
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
|
||||
}
|
||||
return string(a[:i])
|
||||
if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {
|
||||
return nil, os.NewSyscallError("GetAdaptersAddresses", err)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
var flags Flags
|
||||
|
||||
row := syscall.MibIfRow{Index: index}
|
||||
e := syscall.GetIfEntry(&row)
|
||||
if e != nil {
|
||||
return nil, os.NewSyscallError("GetIfEntry", e)
|
||||
}
|
||||
|
||||
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 {
|
||||
ii := findInterfaceInfo(iis, paddr)
|
||||
if ii == nil {
|
||||
continue
|
||||
}
|
||||
if ii.Flags&syscall.IFF_UP != 0 {
|
||||
var flags Flags
|
||||
if paddr.Flags&windows.IfOperStatusUp != 0 {
|
||||
flags |= FlagUp
|
||||
}
|
||||
if ii.Flags&syscall.IFF_LOOPBACK != 0 {
|
||||
if paddr.IfType&windows.IF_TYPE_SOFTWARE_LOOPBACK != 0 {
|
||||
flags |= FlagLoopback
|
||||
}
|
||||
if ii.Flags&syscall.IFF_BROADCAST != 0 {
|
||||
@ -111,17 +127,17 @@ func interfaceTable(ifindex int) ([]Interface, error) {
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user