| // Copyright 2015 The Vanadium 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 netstate implements utilities for retrieving and filtering network |
| // interface state. |
| // |
| // There are routines to obtain the available set of of network addresess, for |
| // determining changes to those addresses, and for selecting from amongst them |
| // according to some set of policies. Polices are implemented by applying |
| // simple predicates (functions with names of the form Is<condition>) to filter |
| // or find the first matching address from a list of addresses. The intent is |
| // to make it easy to create policies that do things like 'find the first IPv4 |
| // unicast address that is globally routable, failing that use a private IPv4 |
| // address, and failing that, an IPv6 address'. |
| // |
| // A simple usage would be: |
| // |
| // state, _ := netstate.GetAccessibleIPs() |
| // ipv4 := state.Filter(netstate.IsPublicUnicastIPv4) |
| // // ipv4 will contain all of the public IPv4 addresses, if any. |
| // |
| // The example policy described above would be implemented using a |
| // series of calls to Filter with appropriate predicates. |
| // |
| // In some cases, it may be necessary to take IP routing information |
| // into account and hence the interface hosting the address. The interface |
| // hosting each address is provided via NetworkInterface. GetAllAddresses and |
| // GetAccessibleIPs return instances of Address that provide access to the |
| // network address and the network interface that hosts it. |
| // |
| // GetAll and GetAccessibleIPs cache the state of the network interfaces |
| // and routing information. This cache is invalidated by the Invalidate |
| // function which must be called whenever the network changes state (e.g. |
| // in response to dhcp changes). |
| // |
| // Although most commercial networking hardware supports IPv6, some consumer |
| // devices and more importantly many ISPs do not, so routines are provided |
| // to allow developers to easily distinguish between the two and to use |
| // whichever is appropriate for their product/situation. |
| // |
| // The term 'accessible' is used to refer to any non-loopback IP address. |
| // The term 'public' is used to refer to any globally routable IP address. |
| // |
| // All IPv6 addresses are intended to be 'public', but any starting with |
| // fc00::/7 (RFC4193) are reserved for private use, but the go |
| // net libraries do not appear to recognise this. Similarly fe80::/10 |
| // (RFC 4291) are reserved for 'site-local' usage, but again this is not |
| // implemented in the go libraries. Any developer who needs to distinguish |
| // these cases will need to write their own routines to test for them. |
| // |
| // When using the go net package it is important to remember that IPv6 |
| // addresses subsume IPv4 and hence in many cases the same internal |
| // representation is used for both, thus testing for the length of the IP |
| // address is unreliable. The reliable test is to use the net.To4() which |
| // will return a non-nil result if can be used as an IPv4 one. Any address |
| // can be used as an IPv6 and hence the only reliable way to test for an IPv6 |
| // address that is not an IPv4 one also is for the To4 call to return nil for |
| // it. |
| package netstate |
| |
| import ( |
| "errors" |
| "fmt" |
| "net" |
| "strings" |
| "sync" |
| |
| "v.io/x/lib/netconfig" |
| ) |
| |
| var ( |
| ErrUnsupportedProtocol = errors.New("unsupported protocol") |
| ErrFailedToParseIPAddr = errors.New("failed to parse IP address") |
| ErrUnspecifiedIPAddr = errors.New("unspecified (i.e. zero) IP address") |
| ErrFailedToFindInterface = errors.New("failed to find a network interface") |
| ) |
| |
| type netAddr struct { |
| network string |
| addr string |
| } |
| |
| func (hp *netAddr) Network() string { |
| return hp.network |
| } |
| |
| func (hp *netAddr) String() string { |
| return hp.addr |
| } |
| |
| func NewNetAddr(network, protocol string) net.Addr { |
| return &netAddr{network, protocol} |
| } |
| |
| // address represents a network address and the network interface that |
| // hosts it. It implements the Address interface. |
| type address struct { |
| addr net.Addr |
| ifc NetworkInterface |
| } |
| |
| // Implements Address |
| func (a *address) Network() string { |
| return a.addr.Network() |
| } |
| |
| // Implements Address |
| func (a *address) String() string { |
| return a.addr.String() |
| } |
| |
| func (a *address) DebugString() string { |
| return fmt.Sprintf("[%s:%s %s:%d]", a.addr.Network(), a.addr.String(), a.ifc.Name(), a.ifc.Index()) |
| } |
| |
| // Implements Address |
| func (a *address) Interface() NetworkInterface { |
| return a.ifc |
| } |
| |
| // ipifc represents a network interface and associated routing information for |
| // IP networks. |
| type ipifc struct { |
| // local copies of net.Interface data |
| name string |
| index, mtu int |
| hardwareAddr net.HardwareAddr |
| flags net.Flags |
| |
| // copy of addresses so that we don't have to call into |
| // the net package to get them again |
| addrs []net.Addr |
| // The IPRoutes of the network interface this address is hosted on, |
| // nil if this information is not available. |
| ipRoutes IPRouteList |
| } |
| |
| // return a comma separated string of network addresses |
| func addrsToStr(addrs []net.Addr) string { |
| r := "" |
| for _, a := range addrs { |
| r += a.String() + " " |
| } |
| return strings.TrimRight(r, " ") |
| } |
| |
| // Implements NetworkInterface |
| func (ifc ipifc) String() string { |
| r := fmt.Sprintf("(%s:%d %s flags[%s], mtu[%d], hw:[%s])", ifc.name, ifc.index, addrsToStr(ifc.addrs), ifc.flags, ifc.mtu, ifc.hardwareAddr) |
| if len(ifc.ipRoutes) > 0 { |
| r += " [" |
| for _, rt := range ifc.ipRoutes { |
| src := "" |
| if rt.PreferredSource != nil { |
| src = ", src: " + rt.PreferredSource.String() |
| } |
| r += fmt.Sprintf("{%d: net: %s, gw: %s%s}, ", rt.IfcIndex, rt.Net, rt.Gateway, src) |
| } |
| r = strings.TrimSuffix(r, ", ") |
| r += "]" |
| } |
| return r |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) Addrs() []net.Addr { |
| return a.addrs |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) Index() int { |
| return a.index |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) Name() string { |
| return a.name |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) MTU() int { |
| return a.mtu |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) HardwareAddr() net.HardwareAddr { |
| return a.hardwareAddr |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) Flags() net.Flags { |
| return a.flags |
| } |
| |
| // Implements NetworkInterface |
| func (a ipifc) Networks() []net.Addr { |
| nets := []net.Addr{} |
| for _, r := range a.ipRoutes { |
| nets = append(nets, &r.Net) |
| } |
| return nets |
| } |
| |
| // Implements IPNetworkInterface |
| func (a ipifc) IPRoutes() IPRouteList { |
| routes := make(IPRouteList, len(a.ipRoutes)) |
| copy(routes, a.ipRoutes) |
| return routes |
| } |
| |
| // Network interface represents a network interface. |
| type NetworkInterface interface { |
| // Addrs returns the addresses hosted by this interface. |
| Addrs() []net.Addr |
| // Index returns the index of this interface. |
| Index() int |
| // Name returns the name of this interface, e.g., "en0", "lo0", "eth0.100" |
| Name() string |
| // MTU returns the maximum transmission unit |
| MTU() int |
| // HardwareAddr returns the hardware address in IEEE MAC-48, EUI-48 and EUI-64 form |
| HardwareAddr() net.HardwareAddr |
| // Flags returns the flags for the interface e.g., FlagUp, FlagLoopback, FlagMulticast |
| Flags() net.Flags |
| // Networks returns the set of networks accessible over this interface. |
| Networks() []net.Addr |
| // String returns a string representation of the interface. |
| String() string |
| } |
| |
| // IPNetworkInterface represents a network interface supporting IP protocols. |
| type IPNetworkInterface interface { |
| NetworkInterface |
| IPRoutes() IPRouteList |
| } |
| |
| // Address represents a network address and the interface that hosts it. |
| type Address interface { |
| // Address returns the network address this instance represents. |
| net.Addr |
| Interface() NetworkInterface |
| DebugString() string |
| } |
| |
| // AddrList is a slice of Addresses. |
| type AddrList []Address |
| |
| func (al AddrList) String() string { |
| r := "" |
| for _, v := range al { |
| r += fmt.Sprintf("(%s) ", v) |
| } |
| return strings.TrimRight(r, " ") |
| } |
| |
| // RouteTable represents the set of currently available network interfaces |
| // and the routes on each such interface. It is index by the index number |
| // of each interface. |
| type RouteTable map[int]IPRouteList |
| |
| type netstateCache struct { |
| mu sync.RWMutex |
| current bool |
| interfaces []NetworkInterface |
| routes RouteTable |
| valid chan struct{} |
| } |
| |
| func (cache *netstateCache) invalidate() { |
| cache.mu.Lock() |
| defer cache.mu.Unlock() |
| if !cache.current { |
| return |
| } |
| cache.current = false |
| close(cache.valid) |
| } |
| |
| func (cache *netstateCache) refresh() error { |
| cache.mu.Lock() |
| defer cache.mu.Unlock() |
| |
| if cache.current { |
| return nil |
| |
| } |
| |
| interfaces, err := net.Interfaces() |
| if err != nil { |
| return err |
| } |
| routes := netconfig.GetIPRoutes(false) |
| |
| cache.interfaces = make([]NetworkInterface, len(interfaces)) |
| for i, ifc := range interfaces { |
| addrs, err := ifc.Addrs() |
| if err != nil { |
| return err |
| } |
| cache.interfaces[i] = &ipifc{ |
| name: ifc.Name, |
| index: ifc.Index, |
| mtu: ifc.MTU, |
| flags: ifc.Flags, |
| hardwareAddr: ifc.HardwareAddr, |
| addrs: addrs, |
| } |
| } |
| |
| cache.routes = make(RouteTable) |
| for _, r := range routes { |
| cache.routes[r.IfcIndex] = append(cache.routes[r.IfcIndex], r) |
| } |
| cache.current = true |
| cache.valid = make(chan struct{}) |
| return nil |
| } |
| |
| func (cache *netstateCache) getNetState() ([]NetworkInterface, RouteTable, <-chan struct{}, error) { |
| if err := cache.refresh(); err != nil { |
| return nil, nil, nil, err |
| } |
| cache.mu.RLock() |
| defer cache.mu.RUnlock() |
| |
| ifcs := make([]NetworkInterface, len(cache.interfaces)) |
| copy(ifcs, cache.interfaces) |
| |
| rt := make(RouteTable) |
| for k, v := range cache.routes { |
| rt[k] = make(IPRouteList, len(v)) |
| copy(rt[k], v) |
| } |
| return ifcs, rt, cache.valid, nil |
| } |
| |
| // Allow this to be overwritten by tests. |
| var internalCache *netstateCache |
| |
| func init() { |
| internalCache = &netstateCache{valid: make(chan struct{})} |
| } |
| |
| // InvalidateCache invalidates any cached network state. |
| func InvalidateCache() { |
| internalCache.invalidate() |
| } |
| |
| // GetAllAddresses gets all of the available addresses on the device, including |
| // loopback addresses, non-IP protocols etc. The IP interface addresses |
| // returned are in CIDR form. |
| // GetAllAddressses caches the state of network interfaces and route tables to avoid |
| // expensive system calls (for routing information), the cache is invalidated |
| // by the Invalidate function which should be called whenever the network state |
| // may have changed (e.g. following a dhcp change). |
| // The returned chan is closed when the returned AddrList has become stale. |
| func GetAllAddresses() (AddrList, <-chan struct{}, error) { |
| interfaces, routeTable, valid, err := internalCache.getNetState() |
| if err != nil { |
| return nil, nil, err |
| } |
| var all AddrList |
| for _, ifc := range interfaces { |
| for _, a := range ifc.Addrs() { |
| na := &address{ |
| addr: a, |
| ifc: fillInterfaceInfo(ifc, routeTable[ifc.Index()]), |
| } |
| all = append(all, na) |
| } |
| |
| } |
| return all, valid, nil |
| } |
| |
| // InterfaceList represents a list of network interfaces. |
| type InterfaceList []NetworkInterface |
| |
| func fillInterfaceInfo(ifc NetworkInterface, rl IPRouteList) ipifc { |
| n := ipifc{ |
| name: ifc.Name(), |
| index: ifc.Index(), |
| mtu: ifc.MTU(), |
| flags: ifc.Flags(), |
| hardwareAddr: ifc.HardwareAddr(), |
| addrs: ifc.Addrs(), |
| } |
| if rl != nil { |
| n.ipRoutes = rl |
| } |
| return n |
| } |
| |
| // GetAllInterfaces returns a list of all of the network interfaces on this |
| // device. It uses the same cache as GetAllAddresses. |
| func GetAllInterfaces() (InterfaceList, error) { |
| interfaces, routeTable, _, err := internalCache.getNetState() |
| r := []NetworkInterface{} |
| for _, ifc := range interfaces { |
| ipifc := fillInterfaceInfo(ifc, routeTable[ifc.Index()]) |
| r = append(r, &ipifc) |
| } |
| return r, err |
| } |
| |
| func (ifcl InterfaceList) String() string { |
| r := "" |
| for _, ifc := range ifcl { |
| r += fmt.Sprintf("%s, ", ifc) |
| } |
| return strings.TrimRight(r, ", ") |
| } |
| |
| // GetAccessibleIPs returns all of the accessible IP addresses on the device |
| // - i.e. excluding loopback and unspecified addresses. |
| // The IP addresses returned will be host addresses. |
| func GetAccessibleIPs() (AddrList, error) { |
| all, _, err := GetAllAddresses() |
| if err != nil { |
| return nil, err |
| } |
| |
| convertAccessible := func(a Address) Address { |
| ah := WithIPHost(a) |
| if !IsAccessibleIP(ah) { |
| return nil |
| } |
| return WithIPHost(ah) |
| } |
| |
| return all.Map(convertAccessible), nil |
| } |
| |
| // AsNetAddrs returns al as a slice of net.Addrs by changing the type |
| // of the slice that contains them and not by copying them. |
| func (al AddrList) AsNetAddrs() []net.Addr { |
| r := make([]net.Addr, len(al)) |
| for i, a := range al { |
| r[i] = a |
| } |
| return r |
| } |
| |
| // ConvertToAddresses attempts to convert a slice of net.Addr's into |
| // an AddrList. It does so as follows: |
| // - using type assertion if the net.Addr instance is also an instance |
| // of Address. |
| // - using AddressFromAddr. |
| // - filling in just the address portion of Address without any interface |
| // information. |
| func ConvertToAddresses(addrs []net.Addr) AddrList { |
| r := []Address{} |
| for _, addr := range addrs { |
| if a, ok := addr.(Address); ok { |
| r = append(r, a) |
| continue |
| } |
| if a, err := AddressFromAddr(addr); err == nil { |
| r = append(r, a) |
| continue |
| } else { |
| r = append(r, &address{addr: addr}) |
| } |
| } |
| return r |
| } |
| |
| // AddressPredicate defines the function signature for predicate functions |
| // to be used with AddrList |
| type AddressPredicate func(a Address) bool |
| |
| // Filter returns all of the addresses for which the predicate |
| // function is true. |
| func (al AddrList) Filter(predicate AddressPredicate) AddrList { |
| r := AddrList{} |
| for _, a := range al { |
| if predicate(a) { |
| r = append(r, a) |
| } |
| } |
| return r |
| } |
| |
| type Mapper func(a Address) Address |
| |
| // Map will apply the Mapper function to all of the items in its receiver |
| // and return a new AddrList containing all of the non-nil results from |
| // said calls. |
| func (al AddrList) Map(mapper Mapper) AddrList { |
| var ral AddrList |
| for _, a := range al { |
| if na := mapper(a); na != nil { |
| ral = append(ral, na) |
| } |
| } |
| return ral |
| } |
| |
| // WithIPHost returns an instance of Address with the network |
| // address component being an instance with a net.Addr that contains an |
| // IP host address (as opposed to a network CIDR for example). |
| func WithIPHost(a Address) Address { |
| aifc := &address{} |
| aifc.addr = AsIPAddr(a) |
| aifc.ifc = ipifcFromNetIfc(a.Interface().(IPNetworkInterface)) |
| return aifc |
| } |
| |
| // WithIPHostAndPort returns an instance of Address with the network |
| // address component being an instance of net.Addr that contains an |
| // IP host and port in : notation. |
| func WithIPHostAndPort(a Address, port string) Address { |
| aifc, ok := a.(*address) |
| if !ok { |
| return nil |
| } |
| aifc.addr = AsIPAddr(aifc.addr) |
| hostAndPort := a.String() |
| if len(port) > 0 { |
| hostAndPort = net.JoinHostPort(hostAndPort, port) |
| } |
| aifc.addr = &netAddr{a.Network(), hostAndPort} |
| return aifc |
| } |
| |
| func ipifcFromNetIfc(ifc IPNetworkInterface) ipifc { |
| return ipifc{ |
| name: ifc.Name(), |
| index: ifc.Index(), |
| mtu: ifc.MTU(), |
| flags: ifc.Flags(), |
| hardwareAddr: ifc.HardwareAddr(), |
| addrs: ifc.Addrs(), |
| ipRoutes: ifc.IPRoutes(), |
| } |
| } |
| |
| // AddressFromAddr creates an instance of Address given the suppied |
| // net.Addr. It will search through the available network interfaces |
| // to find the interface that hosts this address. |
| // It currently supports only IP protocols. |
| func AddressFromAddr(addr net.Addr) (Address, error) { |
| if !IsIPProtocol(addr.Network()) { |
| return nil, ErrUnsupportedProtocol |
| } |
| ip := net.ParseIP(addr.String()) |
| if ip == nil { |
| return nil, ErrFailedToParseIPAddr |
| } |
| return AddressFromIP(ip) |
| } |
| |
| // AddressFromIP creates an instance of Address given the supplied |
| // IP address. It will search through the available network interfaces |
| // to find the interface that hosts this IP address. |
| func AddressFromIP(ip net.IP) (Address, error) { |
| if ip.IsUnspecified() { |
| return nil, ErrUnspecifiedIPAddr |
| } |
| ifcs, _, _, err := internalCache.getNetState() |
| if err != nil { |
| return nil, err |
| } |
| for _, ifc := range ifcs { |
| for _, ifaddr := range ifc.Addrs() { |
| if !IsIPProtocol(ifaddr.Network()) { |
| continue |
| } |
| cip := AsIP(ifaddr) |
| if ip.Equal(cip) { |
| addr := &address{addr: &net.IPAddr{IP: ip}} |
| addr.ifc = ipifcFromNetIfc(ifc.(IPNetworkInterface)) |
| return addr, nil |
| } |
| } |
| } |
| return nil, ErrFailedToFindInterface |
| } |
| |
| // IsIPProtocol returns true if its parameter is one of the allowed |
| // network/protocol values for IP. It considers the vanadium specific |
| // websockect protocols (wsh, wsh4, wsh6) as being IP. |
| func IsIPProtocol(n string) bool { |
| // Removed the training IP version number. |
| n = strings.TrimRightFunc(n, func(r rune) bool { return r == '4' || r == '6' }) |
| switch n { |
| case "ip+net", "ip", "tcp", "udp", "ws", "wsh": |
| return true |
| default: |
| return false |
| } |
| } |
| |
| // AsIPAddr returns its argument as a net.IPAddr if that's possible. If |
| // the address is an IP in host:port notation it will use the host portion |
| // only. |
| func AsIPAddr(a net.Addr) *net.IPAddr { |
| switch v := a.(type) { |
| case *net.IPAddr: |
| return v |
| case *net.IPNet: |
| return &net.IPAddr{IP: v.IP} |
| case *address: |
| return AsIPAddr(v.addr) |
| } |
| if IsIPProtocol(a.Network()) { |
| if r := net.ParseIP(a.String()); r != nil { |
| return &net.IPAddr{IP: r} |
| } |
| if r, _, _ := net.ParseCIDR(a.String()); r != nil { |
| return &net.IPAddr{IP: r} |
| } |
| host, _, _ := net.SplitHostPort(a.String()) |
| if r := net.ParseIP(host); r != nil { |
| return &net.IPAddr{IP: r} |
| } |
| } |
| return nil |
| } |
| |
| // AsIP returns its argument as a net.IP if that's possible. |
| func AsIP(a net.Addr) net.IP { |
| ipAddr := AsIPAddr(a) |
| if ipAddr == nil { |
| return nil |
| } |
| return ipAddr.IP |
| } |
| |
| // IsUnspecified returns true if its argument is an unspecified IP address |
| func IsUnspecifiedIP(a Address) bool { |
| if ip := AsIP(a); ip != nil { |
| return ip.IsUnspecified() |
| } |
| return false |
| } |
| |
| // IsLoopback returns true if its argument is a loopback IP address |
| func IsLoopbackIP(a Address) bool { |
| if ip := AsIP(a); ip != nil && !ip.IsUnspecified() { |
| return ip.IsLoopback() |
| } |
| return false |
| } |
| |
| // IsAccessible returns true if its argument is an accessible (non-loopback) |
| // IP address. |
| func IsAccessibleIP(a Address) bool { |
| if ip := AsIP(a); ip != nil && !ip.IsUnspecified() { |
| return !ip.IsLoopback() |
| } |
| return false |
| } |
| |
| // IsUnicastIP returns true if its argument is a unicast IP address. |
| func IsUnicastIP(a Address) bool { |
| if ip := AsIP(a); ip != nil && !ip.IsUnspecified() { |
| // ipv4 or v6 |
| return !(ip.IsMulticast() || ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast()) |
| } |
| return false |
| } |
| |
| // IsUnicastIPv4 returns true if its argument is a unicast IP4 address |
| func IsUnicastIPv4(a Address) bool { |
| if ip := AsIP(a); ip != nil && ip.To4() != nil { |
| return !ip.IsUnspecified() && !ip.IsMulticast() |
| } |
| return false |
| } |
| |
| // IsPublicUnicastIPv4 returns true if its argument is a globally routable, |
| // public IPv4 unicast address. |
| func IsPublicUnicastIPv4(a Address) bool { |
| if ip := AsIP(a); ip != nil && !ip.IsUnspecified() { |
| if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) { |
| return !ip.IsMulticast() |
| } |
| } |
| return false |
| } |
| |
| // IsUnicastIPv6 returns true if its argument is a unicast IPv6 address |
| func IsUnicastIPv6(a Address) bool { |
| if ip := AsIP(a); ip != nil && ip.To4() == nil { |
| return !ip.IsUnspecified() && !(ip.IsLinkLocalMulticast() || ip.IsInterfaceLocalMulticast()) |
| } |
| return false |
| } |
| |
| // IsUnicastIPv6 returns true if its argument is a globally routable IP6 |
| // address |
| func IsPublicUnicastIPv6(a Address) bool { |
| if ip := AsIP(a); ip != nil && ip.To4() == nil { |
| if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // IsPublicUnicastIP returns true if its argument is a global routable IPv4 |
| // or 6 address. |
| func IsPublicUnicastIP(a Address) bool { |
| if ip := AsIP(a); ip != nil { |
| if t := ip.To4(); t != nil && IsGloballyRoutableIP(t) { |
| return true |
| } |
| if t := ip.To16(); t != nil && IsGloballyRoutableIP(t) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func diffAB(a, b AddrList) AddrList { |
| diff := AddrList{} |
| for _, av := range a { |
| found := false |
| for _, bv := range b { |
| if av.Network() == bv.Network() && |
| av.String() == bv.String() { |
| found = true |
| break |
| } |
| } |
| if !found { |
| diff = append(diff, av) |
| } |
| } |
| return diff |
| } |
| |
| // FindAdded returns the set addresses that are present in b, but not |
| // in a - i.e. have been added. |
| func FindAdded(a, b AddrList) AddrList { |
| return diffAB(b, a) |
| } |
| |
| // FindRemoved returns the set of addresses that are present in a, but not |
| // in b - i.e. have been removed. |
| func FindRemoved(a, b AddrList) AddrList { |
| return diffAB(a, b) |
| } |
| |
| // SameMachine returns true if the provided addr is on the host |
| // executing this function. |
| func SameMachine(addr net.Addr) (bool, error) { |
| addrs, _, err := GetAllAddresses() |
| if err != nil { |
| return false, err |
| } |
| ips := make(map[string]struct{}) |
| for _, a := range addrs { |
| ip, _, err := net.ParseCIDR(a.String()) |
| if err != nil { |
| return false, err |
| } |
| ips[ip.String()] = struct{}{} |
| } |
| |
| client, _, err := net.SplitHostPort(addr.String()) |
| if err != nil { |
| return false, err |
| } |
| _, islocal := ips[client] |
| return islocal, nil |
| } |