go-micro.dev/v5@v5.12.0/util/addr/addr.go (about)

     1  // addr provides functions to retrieve local IP addresses from device interfaces.
     2  package addr
     3  
     4  import (
     5  	"net"
     6  
     7  	"github.com/pkg/errors"
     8  )
     9  
    10  var (
    11  	// ErrIPNotFound no IP address found, and explicit IP not provided.
    12  	ErrIPNotFound = errors.New("no IP address found, and explicit IP not provided")
    13  )
    14  
    15  // IsLocal checks whether an IP belongs to one of the device's interfaces.
    16  func IsLocal(addr string) bool {
    17  	// Extract the host
    18  	host, _, err := net.SplitHostPort(addr)
    19  	if err == nil {
    20  		addr = host
    21  	}
    22  
    23  	if addr == "localhost" {
    24  		return true
    25  	}
    26  
    27  	// Check against all local ips
    28  	for _, ip := range IPs() {
    29  		if addr == ip {
    30  			return true
    31  		}
    32  	}
    33  
    34  	return false
    35  }
    36  
    37  // Extract returns a valid IP address. If the address provided is a valid
    38  // address, it will be returned directly. Otherwise, the available interfaces
    39  // will be iterated over to find an IP address, preferably private.
    40  func Extract(addr string) (string, error) {
    41  	// if addr is already specified then it's directly returned
    42  	if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") {
    43  		return addr, nil
    44  	}
    45  
    46  	var (
    47  		addrs   []net.Addr
    48  		loAddrs []net.Addr
    49  	)
    50  
    51  	ifaces, err := net.Interfaces()
    52  	if err != nil {
    53  		return "", errors.Wrap(err, "failed to get interfaces")
    54  	}
    55  
    56  	for _, iface := range ifaces {
    57  		ifaceAddrs, err := iface.Addrs()
    58  		if err != nil {
    59  			// ignore error, interface can disappear from system
    60  			continue
    61  		}
    62  
    63  		if iface.Flags&net.FlagLoopback != 0 {
    64  			loAddrs = append(loAddrs, ifaceAddrs...)
    65  			continue
    66  		}
    67  
    68  		addrs = append(addrs, ifaceAddrs...)
    69  	}
    70  
    71  	// Add loopback addresses to the end of the list
    72  	addrs = append(addrs, loAddrs...)
    73  
    74  	// Try to find private IP in list, public IP otherwise
    75  	ip, err := findIP(addrs)
    76  	if err != nil {
    77  		return "", err
    78  	}
    79  
    80  	return ip.String(), nil
    81  }
    82  
    83  // IPs returns all available interface IP addresses.
    84  func IPs() []string {
    85  	ifaces, err := net.Interfaces()
    86  	if err != nil {
    87  		return nil
    88  	}
    89  
    90  	var ipAddrs []string
    91  
    92  	for _, i := range ifaces {
    93  		addrs, err := i.Addrs()
    94  		if err != nil {
    95  			continue
    96  		}
    97  
    98  		for _, addr := range addrs {
    99  			var ip net.IP
   100  			switch v := addr.(type) {
   101  			case *net.IPNet:
   102  				ip = v.IP
   103  			case *net.IPAddr:
   104  				ip = v.IP
   105  			}
   106  
   107  			if ip == nil {
   108  				continue
   109  			}
   110  
   111  			ipAddrs = append(ipAddrs, ip.String())
   112  		}
   113  	}
   114  
   115  	return ipAddrs
   116  }
   117  
   118  // findIP will return the first private IP available in the list.
   119  // If no private IP is available it will return the first public IP, if present.
   120  // If no public IP is available, it will return the first loopback IP, if present.
   121  func findIP(addresses []net.Addr) (net.IP, error) {
   122  	var publicIP net.IP
   123  	var localIP net.IP
   124  
   125  	for _, rawAddr := range addresses {
   126  		var ip net.IP
   127  		switch addr := rawAddr.(type) {
   128  		case *net.IPAddr:
   129  			ip = addr.IP
   130  		case *net.IPNet:
   131  			ip = addr.IP
   132  		default:
   133  			continue
   134  		}
   135  
   136  		if ip.IsLoopback() {
   137  			if localIP == nil {
   138  				localIP = ip
   139  			}
   140  			continue
   141  		}
   142  
   143  		if !ip.IsPrivate() {
   144  			if publicIP == nil {
   145  				publicIP = ip
   146  			}
   147  			continue
   148  		}
   149  
   150  		// Return private IP if available
   151  		return ip, nil
   152  	}
   153  
   154  	// Return public or virtual IP
   155  	if len(publicIP) > 0 {
   156  		return publicIP, nil
   157  	}
   158  
   159  	// Return local IP
   160  	if len(localIP) > 0 {
   161  		return localIP, nil
   162  	}
   163  
   164  	return nil, ErrIPNotFound
   165  }