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