Source file
src/net/cgo_unix.go
1
2
3
4
5
6
7
8
9
10
11
12 package net
13
14 import (
15 "context"
16 "errors"
17 "syscall"
18 "unsafe"
19
20 "golang.org/x/net/dns/dnsmessage"
21 )
22
23
24
25
26 type addrinfoErrno int
27
28 func (eai addrinfoErrno) Error() string { return _C_gai_strerror(_C_int(eai)) }
29 func (eai addrinfoErrno) Temporary() bool { return eai == _C_EAI_AGAIN }
30 func (eai addrinfoErrno) Timeout() bool { return false }
31
32 type portLookupResult struct {
33 port int
34 err error
35 }
36
37 type ipLookupResult struct {
38 addrs []IPAddr
39 cname string
40 err error
41 }
42
43 type reverseLookupResult struct {
44 names []string
45 err error
46 }
47
48 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
49 addrs, err, completed := cgoLookupIP(ctx, "ip", name)
50 for _, addr := range addrs {
51 hosts = append(hosts, addr.String())
52 }
53 return
54 }
55
56 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) {
57 var hints _C_struct_addrinfo
58 switch network {
59 case "":
60 case "tcp", "tcp4", "tcp6":
61 *_C_ai_socktype(&hints) = _C_SOCK_STREAM
62 *_C_ai_protocol(&hints) = _C_IPPROTO_TCP
63 case "udp", "udp4", "udp6":
64 *_C_ai_socktype(&hints) = _C_SOCK_DGRAM
65 *_C_ai_protocol(&hints) = _C_IPPROTO_UDP
66 default:
67 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
68 }
69 switch ipVersion(network) {
70 case '4':
71 *_C_ai_family(&hints) = _C_AF_INET
72 case '6':
73 *_C_ai_family(&hints) = _C_AF_INET6
74 }
75 if ctx.Done() == nil {
76 port, err := cgoLookupServicePort(&hints, network, service)
77 return port, err, true
78 }
79 result := make(chan portLookupResult, 1)
80 go cgoPortLookup(result, &hints, network, service)
81 select {
82 case r := <-result:
83 return r.port, r.err, true
84 case <-ctx.Done():
85
86
87 return 0, mapErr(ctx.Err()), false
88 }
89 }
90
91 func cgoLookupServicePort(hints *_C_struct_addrinfo, network, service string) (port int, err error) {
92 cservice := make([]byte, len(service)+1)
93 copy(cservice, service)
94
95 for i, b := range cservice[:len(service)] {
96 cservice[i] = lowerASCII(b)
97 }
98 var res *_C_struct_addrinfo
99 gerrno, err := _C_getaddrinfo(nil, (*_C_char)(unsafe.Pointer(&cservice[0])), hints, &res)
100 if gerrno != 0 {
101 isTemporary := false
102 switch gerrno {
103 case _C_EAI_SYSTEM:
104 if err == nil {
105 err = syscall.EMFILE
106 }
107 default:
108 err = addrinfoErrno(gerrno)
109 isTemporary = addrinfoErrno(gerrno).Temporary()
110 }
111 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service, IsTemporary: isTemporary}
112 }
113 defer _C_freeaddrinfo(res)
114
115 for r := res; r != nil; r = *_C_ai_next(r) {
116 switch *_C_ai_family(r) {
117 case _C_AF_INET:
118 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(*_C_ai_addr(r)))
119 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
120 return int(p[0])<<8 | int(p[1]), nil
121 case _C_AF_INET6:
122 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(*_C_ai_addr(r)))
123 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
124 return int(p[0])<<8 | int(p[1]), nil
125 }
126 }
127 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}
128 }
129
130 func cgoPortLookup(result chan<- portLookupResult, hints *_C_struct_addrinfo, network, service string) {
131 port, err := cgoLookupServicePort(hints, network, service)
132 result <- portLookupResult{port, err}
133 }
134
135 func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) {
136 acquireThread()
137 defer releaseThread()
138
139 var hints _C_struct_addrinfo
140 *_C_ai_flags(&hints) = cgoAddrInfoFlags
141 *_C_ai_socktype(&hints) = _C_SOCK_STREAM
142 *_C_ai_family(&hints) = _C_AF_UNSPEC
143 switch ipVersion(network) {
144 case '4':
145 *_C_ai_family(&hints) = _C_AF_INET
146 case '6':
147 *_C_ai_family(&hints) = _C_AF_INET6
148 }
149
150 h := make([]byte, len(name)+1)
151 copy(h, name)
152 var res *_C_struct_addrinfo
153 gerrno, err := _C_getaddrinfo((*_C_char)(unsafe.Pointer(&h[0])), nil, &hints, &res)
154 if gerrno != 0 {
155 isErrorNoSuchHost := false
156 isTemporary := false
157 switch gerrno {
158 case _C_EAI_SYSTEM:
159 if err == nil {
160
161
162
163
164
165
166
167 err = syscall.EMFILE
168 }
169 case _C_EAI_NONAME:
170 err = errNoSuchHost
171 isErrorNoSuchHost = true
172 default:
173 err = addrinfoErrno(gerrno)
174 isTemporary = addrinfoErrno(gerrno).Temporary()
175 }
176
177 return nil, "", &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost, IsTemporary: isTemporary}
178 }
179 defer _C_freeaddrinfo(res)
180
181 if res != nil {
182 cname = _C_GoString(*_C_ai_canonname(res))
183 if cname == "" {
184 cname = name
185 }
186 if len(cname) > 0 && cname[len(cname)-1] != '.' {
187 cname += "."
188 }
189 }
190 for r := res; r != nil; r = *_C_ai_next(r) {
191
192 if *_C_ai_socktype(r) != _C_SOCK_STREAM {
193 continue
194 }
195 switch *_C_ai_family(r) {
196 case _C_AF_INET:
197 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(*_C_ai_addr(r)))
198 addr := IPAddr{IP: copyIP(sa.Addr[:])}
199 addrs = append(addrs, addr)
200 case _C_AF_INET6:
201 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(*_C_ai_addr(r)))
202 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))}
203 addrs = append(addrs, addr)
204 }
205 }
206 return addrs, cname, nil
207 }
208
209 func cgoIPLookup(result chan<- ipLookupResult, network, name string) {
210 addrs, cname, err := cgoLookupIPCNAME(network, name)
211 result <- ipLookupResult{addrs, cname, err}
212 }
213
214 func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
215 if ctx.Done() == nil {
216 addrs, _, err = cgoLookupIPCNAME(network, name)
217 return addrs, err, true
218 }
219 result := make(chan ipLookupResult, 1)
220 go cgoIPLookup(result, network, name)
221 select {
222 case r := <-result:
223 return r.addrs, r.err, true
224 case <-ctx.Done():
225 return nil, mapErr(ctx.Err()), false
226 }
227 }
228
229
230
231
232
233
234
235
236
237 const (
238 nameinfoLen = 64
239 maxNameinfoLen = 4096
240 )
241
242 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) {
243 var zone string
244 ip := parseIPv4(addr)
245 if ip == nil {
246 ip, zone = parseIPv6Zone(addr)
247 }
248 if ip == nil {
249 return nil, &DNSError{Err: "invalid address", Name: addr}, true
250 }
251 sa, salen := cgoSockaddr(ip, zone)
252 if sa == nil {
253 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
254 }
255 if ctx.Done() == nil {
256 names, err := cgoLookupAddrPTR(addr, sa, salen)
257 return names, err, true
258 }
259 result := make(chan reverseLookupResult, 1)
260 go cgoReverseLookup(result, addr, sa, salen)
261 select {
262 case r := <-result:
263 return r.names, r.err, true
264 case <-ctx.Done():
265 return nil, mapErr(ctx.Err()), false
266 }
267 }
268
269 func cgoLookupAddrPTR(addr string, sa *_C_struct_sockaddr, salen _C_socklen_t) (names []string, err error) {
270 acquireThread()
271 defer releaseThread()
272
273 var gerrno int
274 var b []byte
275 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
276 b = make([]byte, l)
277 gerrno, err = cgoNameinfoPTR(b, sa, salen)
278 if gerrno == 0 || gerrno != _C_EAI_OVERFLOW {
279 break
280 }
281 }
282 if gerrno != 0 {
283 isTemporary := false
284 switch gerrno {
285 case _C_EAI_SYSTEM:
286 if err == nil {
287 err = syscall.EMFILE
288 }
289 default:
290 err = addrinfoErrno(gerrno)
291 isTemporary = addrinfoErrno(gerrno).Temporary()
292 }
293 return nil, &DNSError{Err: err.Error(), Name: addr, IsTemporary: isTemporary}
294 }
295 for i := 0; i < len(b); i++ {
296 if b[i] == 0 {
297 b = b[:i]
298 break
299 }
300 }
301 return []string{absDomainName(string(b))}, nil
302 }
303
304 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *_C_struct_sockaddr, salen _C_socklen_t) {
305 names, err := cgoLookupAddrPTR(addr, sa, salen)
306 result <- reverseLookupResult{names, err}
307 }
308
309 func cgoSockaddr(ip IP, zone string) (*_C_struct_sockaddr, _C_socklen_t) {
310 if ip4 := ip.To4(); ip4 != nil {
311 return cgoSockaddrInet4(ip4), _C_socklen_t(syscall.SizeofSockaddrInet4)
312 }
313 if ip6 := ip.To16(); ip6 != nil {
314 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), _C_socklen_t(syscall.SizeofSockaddrInet6)
315 }
316 return nil, 0
317 }
318
319 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
320 resources, err := resSearch(ctx, name, int(dnsmessage.TypeCNAME), int(dnsmessage.ClassINET))
321 if err != nil {
322 return
323 }
324 cname, err = parseCNAMEFromResources(resources)
325 if err != nil {
326 return "", err, false
327 }
328 return cname, nil, true
329 }
330
331
332
333 func resSearch(ctx context.Context, hostname string, rtype, class int) ([]dnsmessage.Resource, error) {
334 if ctx.Done() == nil {
335 return cgoResSearch(hostname, rtype, class)
336 }
337
338 type result struct {
339 res []dnsmessage.Resource
340 err error
341 }
342
343 res := make(chan result, 1)
344 go func() {
345 r, err := cgoResSearch(hostname, rtype, class)
346 res <- result{
347 res: r,
348 err: err,
349 }
350 }()
351
352 select {
353 case res := <-res:
354 return res.res, res.err
355 case <-ctx.Done():
356 return nil, mapErr(ctx.Err())
357 }
358 }
359
360 func cgoResSearch(hostname string, rtype, class int) ([]dnsmessage.Resource, error) {
361 acquireThread()
362 defer releaseThread()
363
364 state := (*_C_struct___res_state)(_C_malloc(unsafe.Sizeof(_C_struct___res_state{})))
365 defer _C_free(unsafe.Pointer(state))
366 if err := _C_res_ninit(state); err != nil {
367 return nil, errors.New("res_ninit failure: " + err.Error())
368 }
369 defer _C_res_nclose(state)
370
371
372
373
374
375
376
377
378
379 bufSize := maxDNSPacketSize
380 buf := (*_C_uchar)(_C_malloc(uintptr(bufSize)))
381 defer _C_free(unsafe.Pointer(buf))
382
383 s := _C_CString(hostname)
384 defer _C_FreeCString(s)
385
386 var size int
387 for {
388 size, _ = _C_res_nsearch(state, s, class, rtype, buf, bufSize)
389 if size <= 0 || size > 0xffff {
390 return nil, errors.New("res_nsearch failure")
391 }
392 if size <= bufSize {
393 break
394 }
395
396
397 _C_free(unsafe.Pointer(buf))
398 bufSize = size
399 buf = (*_C_uchar)(_C_malloc(uintptr(bufSize)))
400 }
401
402 var p dnsmessage.Parser
403 if _, err := p.Start(unsafe.Slice((*byte)(unsafe.Pointer(buf)), size)); err != nil {
404 return nil, err
405 }
406 p.SkipAllQuestions()
407 resources, err := p.AllAnswers()
408 if err != nil {
409 return nil, err
410 }
411 return resources, nil
412 }
413
View as plain text