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