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 }