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