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