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