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