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 }