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