github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/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 package net 6 7 import ( 8 "context" 9 "internal/bytealg" 10 "runtime" 11 "sync" 12 ) 13 14 // BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the 15 // "tcp" and "udp" networks does not listen for both IPv4 and IPv6 16 // connections. This is due to the fact that IPv4 traffic will not be 17 // routed to an IPv6 socket - two separate sockets are required if 18 // both address families are to be supported. 19 // See inet6(4) for details. 20 21 type ipStackCapabilities struct { 22 sync.Once // guards following 23 ipv4Enabled bool 24 ipv6Enabled bool 25 ipv4MappedIPv6Enabled bool 26 } 27 28 var ipStackCaps ipStackCapabilities 29 30 // supportsIPv4 reports whether the platform supports IPv4 networking 31 // functionality. 32 func supportsIPv4() bool { 33 ipStackCaps.Once.Do(ipStackCaps.probe) 34 return ipStackCaps.ipv4Enabled 35 } 36 37 // supportsIPv6 reports whether the platform supports IPv6 networking 38 // functionality. 39 func supportsIPv6() bool { 40 ipStackCaps.Once.Do(ipStackCaps.probe) 41 return ipStackCaps.ipv6Enabled 42 } 43 44 // supportsIPv4map reports whether the platform supports mapping an 45 // IPv4 address inside an IPv6 address at transport layer 46 // protocols. See RFC 4291, RFC 4038 and RFC 3493. 47 func supportsIPv4map() bool { 48 // Some operating systems provide no support for mapping IPv4 49 // addresses to IPv6, and a runtime check is unnecessary. 50 switch runtime.GOOS { 51 case "dragonfly", "openbsd": 52 return false 53 } 54 55 ipStackCaps.Once.Do(ipStackCaps.probe) 56 return ipStackCaps.ipv4MappedIPv6Enabled 57 } 58 59 // An addrList represents a list of network endpoint addresses. 60 type addrList []Addr 61 62 // isIPv4 reports whether addr contains an IPv4 address. 63 func isIPv4(addr Addr) bool { 64 switch addr := addr.(type) { 65 case *TCPAddr: 66 return addr.IP.To4() != nil 67 case *UDPAddr: 68 return addr.IP.To4() != nil 69 case *IPAddr: 70 return addr.IP.To4() != nil 71 } 72 return false 73 } 74 75 // isNotIPv4 reports whether addr does not contain an IPv4 address. 76 func isNotIPv4(addr Addr) bool { return !isIPv4(addr) } 77 78 // forResolve returns the most appropriate address in address for 79 // a call to ResolveTCPAddr, ResolveUDPAddr, or ResolveIPAddr. 80 // IPv4 is preferred, unless addr contains an IPv6 literal. 81 func (addrs addrList) forResolve(network, addr string) Addr { 82 var want6 bool 83 switch network { 84 case "ip": 85 // IPv6 literal (addr does NOT contain a port) 86 want6 = count(addr, ':') > 0 87 case "tcp", "udp": 88 // IPv6 literal. (addr contains a port, so look for '[') 89 want6 = count(addr, '[') > 0 90 } 91 if want6 { 92 return addrs.first(isNotIPv4) 93 } 94 return addrs.first(isIPv4) 95 } 96 97 // first returns the first address which satisfies strategy, or if 98 // none do, then the first address of any kind. 99 func (addrs addrList) first(strategy func(Addr) bool) Addr { 100 for _, addr := range addrs { 101 if strategy(addr) { 102 return addr 103 } 104 } 105 return addrs[0] 106 } 107 108 // partition divides an address list into two categories, using a 109 // strategy function to assign a boolean label to each address. 110 // The first address, and any with a matching label, are returned as 111 // primaries, while addresses with the opposite label are returned 112 // as fallbacks. For non-empty inputs, primaries is guaranteed to be 113 // non-empty. 114 func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) { 115 var primaryLabel bool 116 for i, addr := range addrs { 117 label := strategy(addr) 118 if i == 0 || label == primaryLabel { 119 primaryLabel = label 120 primaries = append(primaries, addr) 121 } else { 122 fallbacks = append(fallbacks, addr) 123 } 124 } 125 return 126 } 127 128 // filterAddrList applies a filter to a list of IP addresses, 129 // yielding a list of Addr objects. Known filters are nil, ipv4only, 130 // and ipv6only. It returns every address when the filter is nil. 131 // The result contains at least one address when error is nil. 132 func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) { 133 var addrs addrList 134 for _, ip := range ips { 135 if filter == nil || filter(ip) { 136 addrs = append(addrs, inetaddr(ip)) 137 } 138 } 139 if len(addrs) == 0 { 140 return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr} 141 } 142 return addrs, nil 143 } 144 145 // ipv4only reports whether addr is an IPv4 address. 146 func ipv4only(addr IPAddr) bool { 147 return addr.IP.To4() != nil 148 } 149 150 // ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address. 151 func ipv6only(addr IPAddr) bool { 152 return len(addr.IP) == IPv6len && addr.IP.To4() == nil 153 } 154 155 // SplitHostPort splits a network address of the form "host:port", 156 // "host%zone:port", "[host]:port" or "[host%zone]:port" into host or 157 // host%zone and port. 158 // 159 // A literal IPv6 address in hostport must be enclosed in square 160 // brackets, as in "[::1]:80", "[::1%lo0]:80". 161 // 162 // See func Dial for a description of the hostport parameter, and host 163 // and port results. 164 func SplitHostPort(hostport string) (host, port string, err error) { 165 const ( 166 missingPort = "missing port in address" 167 tooManyColons = "too many colons in address" 168 ) 169 addrErr := func(addr, why string) (host, port string, err error) { 170 return "", "", &AddrError{Err: why, Addr: addr} 171 } 172 j, k := 0, 0 173 174 // The port starts after the last colon. 175 i := last(hostport, ':') 176 if i < 0 { 177 return addrErr(hostport, missingPort) 178 } 179 180 if hostport[0] == '[' { 181 // Expect the first ']' just before the last ':'. 182 end := bytealg.IndexByteString(hostport, ']') 183 if end < 0 { 184 return addrErr(hostport, "missing ']' in address") 185 } 186 switch end + 1 { 187 case len(hostport): 188 // There can't be a ':' behind the ']' now. 189 return addrErr(hostport, missingPort) 190 case i: 191 // The expected result. 192 default: 193 // Either ']' isn't followed by a colon, or it is 194 // followed by a colon that is not the last one. 195 if hostport[end+1] == ':' { 196 return addrErr(hostport, tooManyColons) 197 } 198 return addrErr(hostport, missingPort) 199 } 200 host = hostport[1:end] 201 j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions 202 } else { 203 host = hostport[:i] 204 if bytealg.IndexByteString(host, ':') >= 0 { 205 return addrErr(hostport, tooManyColons) 206 } 207 } 208 if bytealg.IndexByteString(hostport[j:], '[') >= 0 { 209 return addrErr(hostport, "unexpected '[' in address") 210 } 211 if bytealg.IndexByteString(hostport[k:], ']') >= 0 { 212 return addrErr(hostport, "unexpected ']' in address") 213 } 214 215 port = hostport[i+1:] 216 return host, port, nil 217 } 218 219 func splitHostZone(s string) (host, zone string) { 220 // The IPv6 scoped addressing zone identifier starts after the 221 // last percent sign. 222 if i := last(s, '%'); i > 0 { 223 host, zone = s[:i], s[i+1:] 224 } else { 225 host = s 226 } 227 return 228 } 229 230 // JoinHostPort combines host and port into a network address of the 231 // form "host:port". If host contains a colon, as found in literal 232 // IPv6 addresses, then JoinHostPort returns "[host]:port". 233 // 234 // See func Dial for a description of the host and port parameters. 235 func JoinHostPort(host, port string) string { 236 // We assume that host is a literal IPv6 address if host has 237 // colons. 238 if bytealg.IndexByteString(host, ':') >= 0 { 239 return "[" + host + "]:" + port 240 } 241 return host + ":" + port 242 } 243 244 // internetAddrList resolves addr, which may be a literal IP 245 // address or a DNS name, and returns a list of internet protocol 246 // family addresses. The result contains at least one address when 247 // error is nil. 248 func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) { 249 var ( 250 err error 251 host, port string 252 portnum int 253 ) 254 switch net { 255 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 256 if addr != "" { 257 if host, port, err = SplitHostPort(addr); err != nil { 258 return nil, err 259 } 260 if portnum, err = r.LookupPort(ctx, net, port); err != nil { 261 return nil, err 262 } 263 } 264 case "ip", "ip4", "ip6": 265 if addr != "" { 266 host = addr 267 } 268 default: 269 return nil, UnknownNetworkError(net) 270 } 271 inetaddr := func(ip IPAddr) Addr { 272 switch net { 273 case "tcp", "tcp4", "tcp6": 274 return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} 275 case "udp", "udp4", "udp6": 276 return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} 277 case "ip", "ip4", "ip6": 278 return &IPAddr{IP: ip.IP, Zone: ip.Zone} 279 default: 280 panic("unexpected network: " + net) 281 } 282 } 283 if host == "" { 284 return addrList{inetaddr(IPAddr{})}, nil 285 } 286 287 // Try as a literal IP address, then as a DNS name. 288 ips, err := r.lookupIPAddr(ctx, net, host) 289 if err != nil { 290 return nil, err 291 } 292 // Issue 18806: if the machine has halfway configured 293 // IPv6 such that it can bind on "::" (IPv6unspecified) 294 // but not connect back to that same address, fall 295 // back to dialing 0.0.0.0. 296 if len(ips) == 1 && ips[0].IP.Equal(IPv6unspecified) { 297 ips = append(ips, IPAddr{IP: IPv4zero}) 298 } 299 300 var filter func(IPAddr) bool 301 if net != "" && net[len(net)-1] == '4' { 302 filter = ipv4only 303 } 304 if net != "" && net[len(net)-1] == '6' { 305 filter = ipv6only 306 } 307 return filterAddrList(filter, ips, inetaddr, host) 308 } 309 310 func loopbackIP(net string) IP { 311 if net != "" && net[len(net)-1] == '6' { 312 return IPv6loopback 313 } 314 return IP{127, 0, 0, 1} 315 }