github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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 returns true if the 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 // first returns the first address which satisfies strategy, or if 67 // none do, then the first address of any kind. 68 func (addrs addrList) first(strategy func(Addr) bool) Addr { 69 for _, addr := range addrs { 70 if strategy(addr) { 71 return addr 72 } 73 } 74 return addrs[0] 75 } 76 77 // partition divides an address list into two categories, using a 78 // strategy function to assign a boolean label to each address. 79 // The first address, and any with a matching label, are returned as 80 // primaries, while addresses with the opposite label are returned 81 // as fallbacks. For non-empty inputs, primaries is guaranteed to be 82 // non-empty. 83 func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) { 84 var primaryLabel bool 85 for i, addr := range addrs { 86 label := strategy(addr) 87 if i == 0 || label == primaryLabel { 88 primaryLabel = label 89 primaries = append(primaries, addr) 90 } else { 91 fallbacks = append(fallbacks, addr) 92 } 93 } 94 return 95 } 96 97 // filterAddrList applies a filter to a list of IP addresses, 98 // yielding a list of Addr objects. Known filters are nil, ipv4only, 99 // and ipv6only. It returns every address when the filter is nil. 100 // The result contains at least one address when error is nil. 101 func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) { 102 var addrs addrList 103 for _, ip := range ips { 104 if filter == nil || filter(ip) { 105 addrs = append(addrs, inetaddr(ip)) 106 } 107 } 108 if len(addrs) == 0 { 109 return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr} 110 } 111 return addrs, nil 112 } 113 114 // ipv4only reports whether addr is an IPv4 address. 115 func ipv4only(addr IPAddr) bool { 116 return addr.IP.To4() != nil 117 } 118 119 // ipv6only reports whether addr is an IPv6 address except IPv4-mapped IPv6 address. 120 func ipv6only(addr IPAddr) bool { 121 return len(addr.IP) == IPv6len && addr.IP.To4() == nil 122 } 123 124 // SplitHostPort splits a network address of the form "host:port", 125 // "host%zone:port", "[host]:port" or "[host%zone]:port" into host or 126 // host%zone and port. 127 // 128 // A literal IPv6 address in hostport must be enclosed in square 129 // brackets, as in "[::1]:80", "[::1%lo0]:80". 130 func SplitHostPort(hostport string) (host, port string, err error) { 131 const ( 132 missingPort = "missing port in address" 133 tooManyColons = "too many colons in address" 134 ) 135 addrErr := func(addr, why string) (host, port string, err error) { 136 return "", "", &AddrError{Err: why, Addr: addr} 137 } 138 j, k := 0, 0 139 140 // The port starts after the last colon. 141 i := last(hostport, ':') 142 if i < 0 { 143 return addrErr(hostport, missingPort) 144 } 145 146 if hostport[0] == '[' { 147 // Expect the first ']' just before the last ':'. 148 end := byteIndex(hostport, ']') 149 if end < 0 { 150 return addrErr(hostport, "missing ']' in address") 151 } 152 switch end + 1 { 153 case len(hostport): 154 // There can't be a ':' behind the ']' now. 155 return addrErr(hostport, missingPort) 156 case i: 157 // The expected result. 158 default: 159 // Either ']' isn't followed by a colon, or it is 160 // followed by a colon that is not the last one. 161 if hostport[end+1] == ':' { 162 return addrErr(hostport, tooManyColons) 163 } 164 return addrErr(hostport, missingPort) 165 } 166 host = hostport[1:end] 167 j, k = 1, end+1 // there can't be a '[' resp. ']' before these positions 168 } else { 169 host = hostport[:i] 170 if byteIndex(host, ':') >= 0 { 171 return addrErr(hostport, tooManyColons) 172 } 173 } 174 if byteIndex(hostport[j:], '[') >= 0 { 175 return addrErr(hostport, "unexpected '[' in address") 176 } 177 if byteIndex(hostport[k:], ']') >= 0 { 178 return addrErr(hostport, "unexpected ']' in address") 179 } 180 181 port = hostport[i+1:] 182 return host, port, nil 183 } 184 185 func splitHostZone(s string) (host, zone string) { 186 // The IPv6 scoped addressing zone identifier starts after the 187 // last percent sign. 188 if i := last(s, '%'); i > 0 { 189 host, zone = s[:i], s[i+1:] 190 } else { 191 host = s 192 } 193 return 194 } 195 196 // JoinHostPort combines host and port into a network address of the 197 // form "host:port" or "host%zone:port", if host is a literal IPv6 198 // address, "[host]:port" or [host%zone]:port. 199 func JoinHostPort(host, port string) string { 200 // We assume that host is a literal IPv6 address if host has 201 // colons. 202 if byteIndex(host, ':') >= 0 { 203 return "[" + host + "]:" + port 204 } 205 return host + ":" + port 206 } 207 208 // internetAddrList resolves addr, which may be a literal IP 209 // address or a DNS name, and returns a list of internet protocol 210 // family addresses. The result contains at least one address when 211 // error is nil. 212 func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) { 213 var ( 214 err error 215 host, port string 216 portnum int 217 ) 218 switch net { 219 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6": 220 if addr != "" { 221 if host, port, err = SplitHostPort(addr); err != nil { 222 return nil, err 223 } 224 if portnum, err = r.LookupPort(ctx, net, port); err != nil { 225 return nil, err 226 } 227 } 228 case "ip", "ip4", "ip6": 229 if addr != "" { 230 host = addr 231 } 232 default: 233 return nil, UnknownNetworkError(net) 234 } 235 inetaddr := func(ip IPAddr) Addr { 236 switch net { 237 case "tcp", "tcp4", "tcp6": 238 return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} 239 case "udp", "udp4", "udp6": 240 return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone} 241 case "ip", "ip4", "ip6": 242 return &IPAddr{IP: ip.IP, Zone: ip.Zone} 243 default: 244 panic("unexpected network: " + net) 245 } 246 } 247 if host == "" { 248 return addrList{inetaddr(IPAddr{})}, nil 249 } 250 251 // Try as a literal IP address, then as a DNS name. 252 var ips []IPAddr 253 if ip := parseIPv4(host); ip != nil { 254 ips = []IPAddr{{IP: ip}} 255 } else if ip, zone := parseIPv6(host, true); ip != nil { 256 ips = []IPAddr{{IP: ip, Zone: zone}} 257 } else { 258 // Try as a DNS name. 259 ips, err = r.LookupIPAddr(ctx, host) 260 if err != nil { 261 return nil, err 262 } 263 } 264 265 var filter func(IPAddr) bool 266 if net != "" && net[len(net)-1] == '4' { 267 filter = ipv4only 268 } 269 if net != "" && net[len(net)-1] == '6' { 270 filter = ipv6only 271 } 272 return filterAddrList(filter, ips, inetaddr, host) 273 } 274 275 func loopbackIP(net string) IP { 276 if net != "" && net[len(net)-1] == '6' { 277 return IPv6loopback 278 } 279 return IP{127, 0, 0, 1} 280 }