github.com/roboticscm/goman@v0.0.0-20210203095141-87c07b4a0a55/src/net/ipsock.go (about) 1 // Copyright 2009 The Go Authors. 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 // Internet protocol family sockets 6 7 package net 8 9 import ( 10 "errors" 11 "time" 12 ) 13 14 var ( 15 // supportsIPv4 reports whether the platform supports IPv4 16 // networking functionality. 17 supportsIPv4 bool 18 19 // supportsIPv6 reports whether the platform supports IPv6 20 // networking functionality. 21 supportsIPv6 bool 22 23 // supportsIPv4map reports whether the platform supports 24 // mapping an IPv4 address inside an IPv6 address at transport 25 // layer protocols. See RFC 4291, RFC 4038 and RFC 3493. 26 supportsIPv4map bool 27 ) 28 29 func init() { 30 sysInit() 31 supportsIPv4 = probeIPv4Stack() 32 supportsIPv6, supportsIPv4map = probeIPv6Stack() 33 } 34 35 // A netaddr represents a network endpoint address or a list of 36 // network endpoint addresses. 37 type netaddr interface { 38 // toAddr returns the address represented in Addr interface. 39 // It returns a nil interface when the address is nil. 40 toAddr() Addr 41 } 42 43 // An addrList represents a list of network endpoint addresses. 44 type addrList []netaddr 45 46 func (al addrList) toAddr() Addr { 47 switch len(al) { 48 case 0: 49 return nil 50 case 1: 51 return al[0].toAddr() 52 default: 53 // For now, we'll roughly pick first one without 54 // considering dealing with any preferences such as 55 // DNS TTL, transport path quality, network routing 56 // information. 57 return al[0].toAddr() 58 } 59 } 60 61 var errNoSuitableAddress = errors.New("no suitable address found") 62 63 // firstFavoriteAddr returns an address or a list of addresses that 64 // implement the netaddr interface. Known filters are nil, ipv4only 65 // and ipv6only. It returns any address when filter is nil. The result 66 // contains at least one address when error is nil. 67 func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { 68 if filter != nil { 69 return firstSupportedAddr(filter, ips, inetaddr) 70 } 71 var ( 72 ipv4, ipv6, swap bool 73 list addrList 74 ) 75 for _, ip := range ips { 76 // We'll take any IP address, but since the dialing 77 // code does not yet try multiple addresses 78 // effectively, prefer to use an IPv4 address if 79 // possible. This is especially relevant if localhost 80 // resolves to [ipv6-localhost, ipv4-localhost]. Too 81 // much code assumes localhost == ipv4-localhost. 82 if ip4 := ipv4only(ip); ip4 != nil && !ipv4 { 83 list = append(list, inetaddr(ip4)) 84 ipv4 = true 85 if ipv6 { 86 swap = true 87 } 88 } else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 { 89 list = append(list, inetaddr(ip6)) 90 ipv6 = true 91 } 92 if ipv4 && ipv6 { 93 if swap { 94 list[0], list[1] = list[1], list[0] 95 } 96 break 97 } 98 } 99 switch len(list) { 100 case 0: 101 return nil, errNoSuitableAddress 102 case 1: 103 return list[0], nil 104 default: 105 return list, nil 106 } 107 } 108 109 func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { 110 for _, ip := range ips { 111 if ip := filter(ip); ip != nil { 112 return inetaddr(ip), nil 113 } 114 } 115 return nil, errNoSuitableAddress 116 } 117 118 // ipv4only returns IPv4 addresses that we can use with the kernel's 119 // IPv4 addressing modes. If ip is an IPv4 address, ipv4only returns ip. 120 // Otherwise it returns nil. 121 func ipv4only(ip IP) IP { 122 if supportsIPv4 && ip.To4() != nil { 123 return ip 124 } 125 return nil 126 } 127 128 // ipv6only returns IPv6 addresses that we can use with the kernel's 129 // IPv6 addressing modes. It returns IPv4-mapped IPv6 addresses as 130 // nils and returns other IPv6 address types as IPv6 addresses. 131 func ipv6only(ip IP) IP { 132 if supportsIPv6 && len(ip) == IPv6len && ip.To4() == nil { 133 return ip 134 } 135 return nil 136 } 137 138 // SplitHostPort splits a network address of the form "host:port", 139 // "[host]:port" or "[ipv6-host%zone]:port" into host or 140 // ipv6-host%zone and port. A literal address or host name for IPv6 141 // must be enclosed in square brackets, as in "[::1]:80", 142 // "[ipv6-host]:http" or "[ipv6-host%zone]:80". 143 func SplitHostPort(hostport string) (host, port string, err error) { 144 j, k := 0, 0 145 146 // The port starts after the last colon. 147 i := last(hostport, ':') 148 if i < 0 { 149 goto missingPort 150 } 151 152 if hostport[0] == '[' { 153 // Expect the first ']' just before the last ':'. 154 end := byteIndex(hostport, ']') 155 if end < 0 { 156 err = &AddrError{"missing ']' in address", hostport} 157 return 158 } 159 switch end + 1 { 160 case len(hostport): 161 // There can't be a ':' behind the ']' now. 162 goto missingPort 163 case i: 164 // The expected result. 165 default: 166 // Either ']' isn't followed by a colon, or it is 167 // followed by a colon that is not the last one. 168 if hostport[end+1] == ':' { 169 goto tooManyColons 170 } 171 goto missingPort 172 } 173 host = hostport[1:end] 174 j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions 175 } else { 176 host = hostport[:i] 177 if byteIndex(host, ':') >= 0 { 178 goto tooManyColons 179 } 180 if byteIndex(host, '%') >= 0 { 181 goto missingBrackets 182 } 183 } 184 if byteIndex(hostport[j:], '[') >= 0 { 185 err = &AddrError{"unexpected '[' in address", hostport} 186 return 187 } 188 if byteIndex(hostport[k:], ']') >= 0 { 189 err = &AddrError{"unexpected ']' in address", hostport} 190 return 191 } 192 193 port = hostport[i+1:] 194 return 195 196 missingPort: 197 err = &AddrError{"missing port in address", hostport} 198 return 199 200 tooManyColons: 201 err = &AddrError{"too many colons in address", hostport} 202 return 203 204 missingBrackets: 205 err = &AddrError{"missing brackets in address", hostport} 206 return 207 } 208 209 func splitHostZone(s string) (host, zone string) { 210 // The IPv6 scoped addressing zone identifier starts after the 211 // last percent sign. 212 if i := last(s, '%'); i > 0 { 213 host, zone = s[:i], s[i+1:] 214 } else { 215 host = s 216 } 217 return 218 } 219 220 // JoinHostPort combines host and port into a network address of the 221 // form "host:port" or, if host contains a colon or a percent sign, 222 // "[host]:port". 223 func JoinHostPort(host, port string) string { 224 // If host has colons or a percent sign, have to bracket it. 225 if byteIndex(host, ':') >= 0 || byteIndex(host, '%') >= 0 { 226 return "[" + host + "]:" + port 227 } 228 return host + ":" + port 229 } 230 231 // resolveInternetAddr resolves addr that is either a literal IP 232 // address or a DNS name and returns an internet protocol family 233 // address. It returns a list that contains a pair of different 234 // address family addresses when addr is a DNS name and the name has 235 // multiple address family records. The result contains at least one 236 // address when error is nil. 237 func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) { 238 var ( 239 err error 240 host, port, zone string 241 portnum int 242 ) 243 switch net { 244 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 245 if addr != "" { 246 if host, port, err = SplitHostPort(addr); err != nil { 247 return nil, err 248 } 249 if portnum, err = parsePort(net, port); err != nil { 250 return nil, err 251 } 252 } 253 case "ip", "ip4", "ip6": 254 if addr != "" { 255 host = addr 256 } 257 default: 258 return nil, UnknownNetworkError(net) 259 } 260 inetaddr := func(ip IP) netaddr { 261 switch net { 262 case "tcp", "tcp4", "tcp6": 263 return &TCPAddr{IP: ip, Port: portnum, Zone: zone} 264 case "udp", "udp4", "udp6": 265 return &UDPAddr{IP: ip, Port: portnum, Zone: zone} 266 case "ip", "ip4", "ip6": 267 return &IPAddr{IP: ip, Zone: zone} 268 default: 269 panic("unexpected network: " + net) 270 } 271 } 272 if host == "" { 273 return inetaddr(nil), nil 274 } 275 // Try as a literal IP address. 276 var ip IP 277 if ip = parseIPv4(host); ip != nil { 278 return inetaddr(ip), nil 279 } 280 if ip, zone = parseIPv6(host, true); ip != nil { 281 return inetaddr(ip), nil 282 } 283 // Try as a DNS name. 284 host, zone = splitHostZone(host) 285 ips, err := lookupIPDeadline(host, deadline) 286 if err != nil { 287 return nil, err 288 } 289 var filter func(IP) IP 290 if net != "" && net[len(net)-1] == '4' { 291 filter = ipv4only 292 } 293 if net != "" && net[len(net)-1] == '6' || zone != "" { 294 filter = ipv6only 295 } 296 return firstFavoriteAddr(filter, ips, inetaddr) 297 } 298 299 func zoneToString(zone int) string { 300 if zone == 0 { 301 return "" 302 } 303 if ifi, err := InterfaceByIndex(zone); err == nil { 304 return ifi.Name 305 } 306 return itod(uint(zone)) 307 } 308 309 func zoneToInt(zone string) int { 310 if zone == "" { 311 return 0 312 } 313 if ifi, err := InterfaceByName(zone); err == nil { 314 return ifi.Index 315 } 316 n, _, _ := dtoi(zone, 0) 317 return n 318 }