github.com/searKing/golang/go@v1.2.117/net/local_addr.go (about)

     1  // Copyright 2021 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package net
     6  
     7  import (
     8  	"errors"
     9  	"net"
    10  	"time"
    11  
    12  	strings_ "github.com/searKing/golang/go/strings"
    13  	"golang.org/x/net/nettest"
    14  )
    15  
    16  // This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go
    17  
    18  // ScoreAddr scores how likely the given addr is to be a remote address and returns the
    19  // IP to use when listening. Any address which receives a negative score should not be used.
    20  // Scores are calculated as:
    21  // -1 for any unknown IP addreseses.
    22  // +300 for IPv4 addresses
    23  // +100 for non-local addresses, extra +100 for "up" interfaces.
    24  // +100 for routable addresses
    25  // -50 for local mac addr.
    26  func ScoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) {
    27  	var ip net.IP
    28  	if netAddr, ok := addr.(*net.IPNet); ok {
    29  		ip = netAddr.IP
    30  	} else if netIP, ok := addr.(*net.IPAddr); ok {
    31  		ip = netIP.IP
    32  	} else {
    33  		return -1, nil
    34  	}
    35  
    36  	var score int
    37  	if ip.To4() != nil {
    38  		score += 300
    39  	}
    40  	if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() {
    41  		score += 100
    42  		if iface.Flags&net.FlagUp != 0 {
    43  			score += 100
    44  		}
    45  	}
    46  	_, routable := isRoutableIP("ip", ip)
    47  	if routable {
    48  		score -= 25
    49  	}
    50  	if isLocalMacAddr(iface.HardwareAddr) {
    51  		score -= 50
    52  	}
    53  	return score, ip
    54  }
    55  
    56  // filter is a interface filter which returns false if the interface is _not_ to listen on
    57  func listenAddr(interfaces []net.Interface, filter func(iface net.Interface) bool) (net.HardwareAddr, net.IP, error) {
    58  	if filter == nil {
    59  		filter = func(iface net.Interface) bool { return true }
    60  	}
    61  	bestScore := -1
    62  	var bestIP net.IP
    63  	var bestMac net.HardwareAddr
    64  	// Select the highest scoring IP as the best IP.
    65  	for _, iface := range interfaces {
    66  		if !filter(iface) {
    67  			continue
    68  		}
    69  		addrs, err := iface.Addrs()
    70  		if err != nil {
    71  			// Skip this interface if there is an error.
    72  			continue
    73  		}
    74  
    75  		for _, addr := range addrs {
    76  			score, ip := ScoreAddr(iface, addr)
    77  			if score > bestScore {
    78  				bestScore = score
    79  				bestIP = ip
    80  				bestMac = iface.HardwareAddr
    81  			}
    82  		}
    83  	}
    84  
    85  	if bestScore == -1 {
    86  		return nil, nil, errors.New("no addresses to listen on")
    87  	}
    88  
    89  	return bestMac, bestIP, nil
    90  }
    91  
    92  // ExpectInterfaceNameFilter
    93  // If you want to listen specified interfaces (and the loopback) give the name of the interface (eg eth0) here.
    94  func ExpectInterfaceNameFilter(names ...string) func(iface net.Interface) bool {
    95  	return func(iface net.Interface) bool {
    96  		if len(names) == 0 {
    97  			return true
    98  		}
    99  		return strings_.SliceContainsAny(names, iface.Name)
   100  	}
   101  }
   102  
   103  // ExceptInterfaceNameFilter
   104  // you can specify which interface _not_ to listen on
   105  func ExceptInterfaceNameFilter(names ...string) func(iface net.Interface) bool {
   106  	return func(iface net.Interface) bool {
   107  		if len(names) == 0 {
   108  			return true
   109  		}
   110  		return !strings_.SliceContainsAny(names, iface.Name)
   111  	}
   112  }
   113  
   114  // RoutedInterfaceNameFilter returns a network interface that can route IP
   115  // traffic and satisfies flags.
   116  //
   117  // The provided network must be "ip", "ip4" or "ip6".
   118  func RoutedInterfaceNameFilter() func(iface net.Interface) bool {
   119  	return func(iface net.Interface) bool {
   120  		rifs, err := nettest.RoutedInterface("ip", net.FlagUp|net.FlagBroadcast)
   121  		if err != nil {
   122  			return true
   123  		}
   124  
   125  		return rifs.Name == iface.Name
   126  	}
   127  }
   128  
   129  // ListenIP returns the IP to bind to in Listen. It tries to find an IP that can be used
   130  // by other machines to reach this machine.
   131  // filters is interface filters any return false if the interface is _not_ to listen on
   132  func ListenIP(filters ...func(iface net.Interface) bool) (net.IP, error) {
   133  	_, ip, err := ListenAddr(filters...)
   134  	return ip, err
   135  }
   136  
   137  // ListenMac returns the Mac to bind to in Listen. It tries to find an Mac that can be used
   138  // by other machines to reach this machine.
   139  // filters is interface filters any return false if the interface is _not_ to listen on
   140  func ListenMac(filters ...func(iface net.Interface) bool) (net.HardwareAddr, error) {
   141  	mac, _, err := ListenAddr(filters...)
   142  	return mac, err
   143  }
   144  
   145  // ListenAddr returns the Mac and IP to bind to in Listen. It tries to find an Mac and IP that can be used
   146  // by other machines to reach this machine.
   147  // filters is interface filters any return false if the interface is _not_ to listen on
   148  func ListenAddr(filters ...func(iface net.Interface) bool) (net.HardwareAddr, net.IP, error) {
   149  	interfaces, err := net.Interfaces()
   150  	if err != nil {
   151  		return nil, nil, err
   152  	}
   153  	return listenAddr(interfaces, func(iface net.Interface) bool {
   154  		for _, filter := range filters {
   155  			if filter != nil && !filter(iface) {
   156  				return false
   157  			}
   158  		}
   159  		return true
   160  	})
   161  }
   162  
   163  // DialIP returns the local IP to in Dial.
   164  func DialIP(network, address string, timeout time.Duration) (net.IP, error) {
   165  	conn, err := net.DialTimeout(network, address, timeout)
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  	a := conn.LocalAddr()
   170  	ipAddr, err := net.ResolveIPAddr(a.Network(), a.String())
   171  	defer conn.Close()
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	return ipAddr.IP, nil
   176  }
   177  
   178  // ServeIP returns the IP to bind to in Listen. It tries to find an IP that can be used
   179  // by other machines to reach this machine.
   180  // Order is by DialIP and ListenIP
   181  func ServeIP(networks, addresses []string, timeout time.Duration) (net.IP, error) {
   182  	for _, network := range networks {
   183  		for _, address := range addresses {
   184  			ip, err := DialIP(network, address, timeout)
   185  			if err != nil {
   186  				continue
   187  			}
   188  			return ip, nil
   189  		}
   190  	}
   191  	return ListenIP()
   192  }
   193  
   194  // If the first octet's second least-significant-bit is set, then it's local.
   195  // https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
   196  func isLocalMacAddr(addr net.HardwareAddr) bool {
   197  	if len(addr) == 0 {
   198  		return false
   199  	}
   200  	return addr[0]&2 == 2
   201  }
   202  
   203  func isRoutableIP(network string, ip net.IP) (net.IP, bool) {
   204  	if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
   205  		return nil, false
   206  	}
   207  	switch network {
   208  	case "ip4":
   209  		if ip := ip.To4(); ip != nil {
   210  			return ip, true
   211  		}
   212  	case "ip6":
   213  		if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
   214  			return nil, false
   215  		}
   216  		if ip := ip.To16(); ip != nil && ip.To4() == nil {
   217  			return ip, true
   218  		}
   219  	default:
   220  		if ip := ip.To4(); ip != nil {
   221  			return ip, true
   222  		}
   223  		if ip := ip.To16(); ip != nil {
   224  			return ip, true
   225  		}
   226  	}
   227  	return nil, false
   228  }