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