Source file
src/net/ipsock.go
Documentation: net
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "internal/bytealg"
10 "runtime"
11 "sync"
12 )
13
14
15
16
17
18
19
20
21 type ipStackCapabilities struct {
22 sync.Once
23 ipv4Enabled bool
24 ipv6Enabled bool
25 ipv4MappedIPv6Enabled bool
26 }
27
28 var ipStackCaps ipStackCapabilities
29
30
31
32 func supportsIPv4() bool {
33 ipStackCaps.Once.Do(ipStackCaps.probe)
34 return ipStackCaps.ipv4Enabled
35 }
36
37
38
39 func supportsIPv6() bool {
40 ipStackCaps.Once.Do(ipStackCaps.probe)
41 return ipStackCaps.ipv6Enabled
42 }
43
44
45
46
47 func supportsIPv4map() bool {
48
49
50 switch runtime.GOOS {
51 case "dragonfly", "openbsd":
52 return false
53 }
54
55 ipStackCaps.Once.Do(ipStackCaps.probe)
56 return ipStackCaps.ipv4MappedIPv6Enabled
57 }
58
59
60 type addrList []Addr
61
62
63 func isIPv4(addr Addr) bool {
64 switch addr := addr.(type) {
65 case *TCPAddr:
66 return addr.IP.To4() != nil
67 case *UDPAddr:
68 return addr.IP.To4() != nil
69 case *IPAddr:
70 return addr.IP.To4() != nil
71 }
72 return false
73 }
74
75
76 func isNotIPv4(addr Addr) bool { return !isIPv4(addr) }
77
78
79
80
81 func (addrs addrList) forResolve(network, addr string) Addr {
82 var want6 bool
83 switch network {
84 case "ip":
85
86 want6 = count(addr, ':') > 0
87 case "tcp", "udp":
88
89 want6 = count(addr, '[') > 0
90 }
91 if want6 {
92 return addrs.first(isNotIPv4)
93 }
94 return addrs.first(isIPv4)
95 }
96
97
98
99 func (addrs addrList) first(strategy func(Addr) bool) Addr {
100 for _, addr := range addrs {
101 if strategy(addr) {
102 return addr
103 }
104 }
105 return addrs[0]
106 }
107
108
109
110
111
112
113
114 func (addrs addrList) partition(strategy func(Addr) bool) (primaries, fallbacks addrList) {
115 var primaryLabel bool
116 for i, addr := range addrs {
117 label := strategy(addr)
118 if i == 0 || label == primaryLabel {
119 primaryLabel = label
120 primaries = append(primaries, addr)
121 } else {
122 fallbacks = append(fallbacks, addr)
123 }
124 }
125 return
126 }
127
128
129
130
131
132 func filterAddrList(filter func(IPAddr) bool, ips []IPAddr, inetaddr func(IPAddr) Addr, originalAddr string) (addrList, error) {
133 var addrs addrList
134 for _, ip := range ips {
135 if filter == nil || filter(ip) {
136 addrs = append(addrs, inetaddr(ip))
137 }
138 }
139 if len(addrs) == 0 {
140 return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: originalAddr}
141 }
142 return addrs, nil
143 }
144
145
146 func ipv4only(addr IPAddr) bool {
147 return addr.IP.To4() != nil
148 }
149
150
151 func ipv6only(addr IPAddr) bool {
152 return len(addr.IP) == IPv6len && addr.IP.To4() == nil
153 }
154
155
156
157
158
159
160
161
162
163
164 func SplitHostPort(hostport string) (host, port string, err error) {
165 const (
166 missingPort = "missing port in address"
167 tooManyColons = "too many colons in address"
168 )
169 addrErr := func(addr, why string) (host, port string, err error) {
170 return "", "", &AddrError{Err: why, Addr: addr}
171 }
172 j, k := 0, 0
173
174
175 i := last(hostport, ':')
176 if i < 0 {
177 return addrErr(hostport, missingPort)
178 }
179
180 if hostport[0] == '[' {
181
182 end := bytealg.IndexByteString(hostport, ']')
183 if end < 0 {
184 return addrErr(hostport, "missing ']' in address")
185 }
186 switch end + 1 {
187 case len(hostport):
188
189 return addrErr(hostport, missingPort)
190 case i:
191
192 default:
193
194
195 if hostport[end+1] == ':' {
196 return addrErr(hostport, tooManyColons)
197 }
198 return addrErr(hostport, missingPort)
199 }
200 host = hostport[1:end]
201 j, k = 1, end+1
202 } else {
203 host = hostport[:i]
204 if bytealg.IndexByteString(host, ':') >= 0 {
205 return addrErr(hostport, tooManyColons)
206 }
207 }
208 if bytealg.IndexByteString(hostport[j:], '[') >= 0 {
209 return addrErr(hostport, "unexpected '[' in address")
210 }
211 if bytealg.IndexByteString(hostport[k:], ']') >= 0 {
212 return addrErr(hostport, "unexpected ']' in address")
213 }
214
215 port = hostport[i+1:]
216 return host, port, nil
217 }
218
219 func splitHostZone(s string) (host, zone string) {
220
221
222 if i := last(s, '%'); i > 0 {
223 host, zone = s[:i], s[i+1:]
224 } else {
225 host = s
226 }
227 return
228 }
229
230
231
232
233
234
235 func JoinHostPort(host, port string) string {
236
237
238 if bytealg.IndexByteString(host, ':') >= 0 {
239 return "[" + host + "]:" + port
240 }
241 return host + ":" + port
242 }
243
244
245
246
247
248 func (r *Resolver) internetAddrList(ctx context.Context, net, addr string) (addrList, error) {
249 var (
250 err error
251 host, port string
252 portnum int
253 )
254 switch net {
255 case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
256 if addr != "" {
257 if host, port, err = SplitHostPort(addr); err != nil {
258 return nil, err
259 }
260 if portnum, err = r.LookupPort(ctx, net, port); err != nil {
261 return nil, err
262 }
263 }
264 case "ip", "ip4", "ip6":
265 if addr != "" {
266 host = addr
267 }
268 default:
269 return nil, UnknownNetworkError(net)
270 }
271 inetaddr := func(ip IPAddr) Addr {
272 switch net {
273 case "tcp", "tcp4", "tcp6":
274 return &TCPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
275 case "udp", "udp4", "udp6":
276 return &UDPAddr{IP: ip.IP, Port: portnum, Zone: ip.Zone}
277 case "ip", "ip4", "ip6":
278 return &IPAddr{IP: ip.IP, Zone: ip.Zone}
279 default:
280 panic("unexpected network: " + net)
281 }
282 }
283 if host == "" {
284 return addrList{inetaddr(IPAddr{})}, nil
285 }
286
287
288 ips, err := r.lookupIPAddr(ctx, net, host)
289 if err != nil {
290 return nil, err
291 }
292
293
294
295
296 if len(ips) == 1 && ips[0].IP.Equal(IPv6unspecified) {
297 ips = append(ips, IPAddr{IP: IPv4zero})
298 }
299
300 var filter func(IPAddr) bool
301 if net != "" && net[len(net)-1] == '4' {
302 filter = ipv4only
303 }
304 if net != "" && net[len(net)-1] == '6' {
305 filter = ipv6only
306 }
307 return filterAddrList(filter, ips, inetaddr, host)
308 }
309
310 func loopbackIP(net string) IP {
311 if net != "" && net[len(net)-1] == '6' {
312 return IPv6loopback
313 }
314 return IP{127, 0, 0, 1}
315 }
316
View as plain text