| // 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" |
| ) |
| |
| // supportsVistaIP reports whether the platform implements new IP |
| // stack and ABIs supported on Windows Vista and above. |
| var supportsVistaIP bool |
| |
| func init() { |
| supportsVistaIP = probeWindowsIPStack() |
| } |
| |
| func probeWindowsIPStack() (supportsVistaIP bool) { |
| v, err := syscall.GetVersion() |
| if err != nil { |
| return true // Windows 10 and above will deprecate this API |
| } |
| if byte(v) < 6 { // major version of Windows Vista is 6 |
| return false |
| } |
| return true |
| } |
| |
| // adapterAddresses returns a list of IP adapter and address |
| // structures. The structure contains an IP adapter and flattened |
| // multiple IP addresses including unicast, anycast and multicast |
| // addresses. |
| func adapterAddresses() ([]*windows.IpAdapterAddresses, error) { |
| var b []byte |
| l := uint32(15000) // recommended initial size |
| for { |
| b = make([]byte, l) |
| err := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, windows.GAA_FLAG_INCLUDE_PREFIX, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l) |
| if err == nil { |
| if l == 0 { |
| return nil, nil |
| } |
| break |
| } |
| if err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW { |
| return nil, os.NewSyscallError("getadaptersaddresses", err) |
| } |
| if l <= uint32(len(b)) { |
| return nil, os.NewSyscallError("getadaptersaddresses", err) |
| } |
| } |
| var aas []*windows.IpAdapterAddresses |
| for aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next { |
| aas = append(aas, aa) |
| } |
| return aas, 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) { |
| aas, err := adapterAddresses() |
| if err != nil { |
| return nil, err |
| } |
| var ift []Interface |
| for _, aa := range aas { |
| index := aa.IfIndex |
| if index == 0 { // ipv6IfIndex is a substitute for ifIndex |
| index = aa.Ipv6IfIndex |
| } |
| if ifindex == 0 || ifindex == int(index) { |
| ifi := Interface{ |
| Index: int(index), |
| Name: syscall.UTF16ToString((*(*[10000]uint16)(unsafe.Pointer(aa.FriendlyName)))[:]), |
| } |
| if aa.OperStatus == windows.IfOperStatusUp { |
| ifi.Flags |= FlagUp |
| } |
| // For now we need to infer link-layer service |
| // capabilities from media types. |
| // We will be able to use |
| // MIB_IF_ROW2.AccessType once we drop support |
| // for Windows XP. |
| switch aa.IfType { |
| case windows.IF_TYPE_ETHERNET_CSMACD, windows.IF_TYPE_ISO88025_TOKENRING, windows.IF_TYPE_IEEE80211, windows.IF_TYPE_IEEE1394: |
| ifi.Flags |= FlagBroadcast | FlagMulticast |
| case windows.IF_TYPE_PPP, windows.IF_TYPE_TUNNEL: |
| ifi.Flags |= FlagPointToPoint | FlagMulticast |
| case windows.IF_TYPE_SOFTWARE_LOOPBACK: |
| ifi.Flags |= FlagLoopback | FlagMulticast |
| case windows.IF_TYPE_ATM: |
| ifi.Flags |= FlagBroadcast | FlagPointToPoint | FlagMulticast // assume all services available; LANE, point-to-point and point-to-multipoint |
| } |
| if aa.Mtu == 0xffffffff { |
| ifi.MTU = -1 |
| } else { |
| ifi.MTU = int(aa.Mtu) |
| } |
| if aa.PhysicalAddressLength > 0 { |
| ifi.HardwareAddr = make(HardwareAddr, aa.PhysicalAddressLength) |
| copy(ifi.HardwareAddr, aa.PhysicalAddress[:]) |
| } |
| ift = append(ift, ifi) |
| if ifindex == 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) { |
| aas, err := adapterAddresses() |
| if err != nil { |
| return nil, err |
| } |
| var ifat []Addr |
| for _, aa := range aas { |
| index := aa.IfIndex |
| if index == 0 { // ipv6IfIndex is a substitute for ifIndex |
| index = aa.Ipv6IfIndex |
| } |
| var pfx4, pfx6 []IPNet |
| if !supportsVistaIP { |
| pfx4, pfx6, err = addrPrefixTable(aa) |
| if err != nil { |
| return nil, err |
| } |
| } |
| if ifi == nil || ifi.Index == int(index) { |
| for puni := aa.FirstUnicastAddress; puni != nil; puni = puni.Next { |
| sa, err := puni.Address.Sockaddr.Sockaddr() |
| if err != nil { |
| return nil, os.NewSyscallError("sockaddr", err) |
| } |
| var l int |
| switch sa := sa.(type) { |
| case *syscall.SockaddrInet4: |
| if supportsVistaIP { |
| l = int(puni.OnLinkPrefixLength) |
| } else { |
| l = addrPrefixLen(pfx4, IP(sa.Addr[:])) |
| } |
| ifat = append(ifat, &IPNet{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]), Mask: CIDRMask(l, 8*IPv4len)}) |
| case *syscall.SockaddrInet6: |
| if supportsVistaIP { |
| l = int(puni.OnLinkPrefixLength) |
| } else { |
| l = addrPrefixLen(pfx6, IP(sa.Addr[:])) |
| } |
| ifa := &IPNet{IP: make(IP, IPv6len), Mask: CIDRMask(l, 8*IPv6len)} |
| copy(ifa.IP, sa.Addr[:]) |
| ifat = append(ifat, ifa) |
| } |
| } |
| for pany := aa.FirstAnycastAddress; pany != nil; pany = pany.Next { |
| sa, err := pany.Address.Sockaddr.Sockaddr() |
| if err != nil { |
| return nil, os.NewSyscallError("sockaddr", err) |
| } |
| switch sa := sa.(type) { |
| case *syscall.SockaddrInet4: |
| ifat = append(ifat, &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}) |
| case *syscall.SockaddrInet6: |
| ifa := &IPAddr{IP: make(IP, IPv6len)} |
| copy(ifa.IP, sa.Addr[:]) |
| ifat = append(ifat, ifa) |
| } |
| } |
| } |
| } |
| return ifat, nil |
| } |
| |
| func addrPrefixTable(aa *windows.IpAdapterAddresses) (pfx4, pfx6 []IPNet, err error) { |
| for p := aa.FirstPrefix; p != nil; p = p.Next { |
| sa, err := p.Address.Sockaddr.Sockaddr() |
| if err != nil { |
| return nil, nil, os.NewSyscallError("sockaddr", err) |
| } |
| switch sa := sa.(type) { |
| case *syscall.SockaddrInet4: |
| pfx := IPNet{IP: IP(sa.Addr[:]), Mask: CIDRMask(int(p.PrefixLength), 8*IPv4len)} |
| pfx4 = append(pfx4, pfx) |
| case *syscall.SockaddrInet6: |
| pfx := IPNet{IP: IP(sa.Addr[:]), Mask: CIDRMask(int(p.PrefixLength), 8*IPv6len)} |
| pfx6 = append(pfx6, pfx) |
| } |
| } |
| return |
| } |
| |
| // addrPrefixLen returns an appropriate prefix length in bits for ip |
| // from pfxs. It returns 32 or 128 when no appropriate on-link address |
| // prefix found. |
| // |
| // NOTE: This is pretty naive implementation that contains many |
| // allocations and non-effective linear search, and should not be used |
| // freely. |
| func addrPrefixLen(pfxs []IPNet, ip IP) int { |
| var l int |
| var cand *IPNet |
| for i := range pfxs { |
| if !pfxs[i].Contains(ip) { |
| continue |
| } |
| if cand == nil { |
| l, _ = pfxs[i].Mask.Size() |
| cand = &pfxs[i] |
| continue |
| } |
| m, _ := pfxs[i].Mask.Size() |
| if m > l { |
| l = m |
| cand = &pfxs[i] |
| continue |
| } |
| } |
| if l > 0 { |
| return l |
| } |
| if ip.To4() != nil { |
| return 8 * IPv4len |
| } |
| return 8 * IPv6len |
| } |
| |
| // interfaceMulticastAddrTable returns addresses for a specific |
| // interface. |
| func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { |
| aas, err := adapterAddresses() |
| if err != nil { |
| return nil, err |
| } |
| var ifat []Addr |
| for _, aa := range aas { |
| index := aa.IfIndex |
| if index == 0 { // ipv6IfIndex is a substitute for ifIndex |
| index = aa.Ipv6IfIndex |
| } |
| if ifi == nil || ifi.Index == int(index) { |
| for pmul := aa.FirstMulticastAddress; pmul != nil; pmul = pmul.Next { |
| sa, err := pmul.Address.Sockaddr.Sockaddr() |
| if err != nil { |
| return nil, os.NewSyscallError("sockaddr", err) |
| } |
| switch sa := sa.(type) { |
| case *syscall.SockaddrInet4: |
| ifat = append(ifat, &IPAddr{IP: IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3])}) |
| case *syscall.SockaddrInet6: |
| ifa := &IPAddr{IP: make(IP, IPv6len)} |
| copy(ifa.IP, sa.Addr[:]) |
| ifat = append(ifat, ifa) |
| } |
| } |
| } |
| } |
| return ifat, nil |
| } |