// Copyright 2011 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 net import ( "internal/syscall/windows" "os" "syscall" "unsafe" ) 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 &addrs[0], nil } func getInterfaceInfos() ([]syscall.InterfaceInfo, error) { s, err := sysSocket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP) if err != nil { return nil, err } defer closeFunc(s) iia := [20]syscall.InterfaceInfo{} ret := uint32(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) } 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) { paddr, err := getAdapters() if err != nil { return nil, err } iis, err := getInterfaceInfos() if err != nil { return nil, err } var ift []Interface 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 if paddr.Flags&windows.IfOperStatusUp != 0 { flags |= FlagUp } 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 } ifi := Interface{ Index: int(index), 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 } // If the ifi is nil, interfaceAddrTable returns addresses for all // network interfaces. Otherwise it returns addresses for a specific // interface. func interfaceAddrTable(ifi *Interface) ([]Addr, error) { 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) { 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) { 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) case *syscall.SockaddrInet6: ifa := &IPAddr{IP: make(IP, IPv6len)} copy(ifa.IP, sav.Addr[:]) ifat = append(ifat, ifa) } } } } } return ifat, nil }