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