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