Source file
src/net/cgo_unix.go
Documentation: net
1
2
3
4
5
6
7
8
9
10 package net
11
12
25 import "C"
26
27 import (
28 "context"
29 "syscall"
30 "unsafe"
31 )
32
33
34
35
36 type addrinfoErrno int
37
38 func (eai addrinfoErrno) Error() string { return C.GoString(C.gai_strerror(C.int(eai))) }
39 func (eai addrinfoErrno) Temporary() bool { return eai == C.EAI_AGAIN }
40 func (eai addrinfoErrno) Timeout() bool { return false }
41
42 type portLookupResult struct {
43 port int
44 err error
45 }
46
47 type ipLookupResult struct {
48 addrs []IPAddr
49 cname string
50 err error
51 }
52
53 type reverseLookupResult struct {
54 names []string
55 err error
56 }
57
58 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
59 addrs, err, completed := cgoLookupIP(ctx, "ip", name)
60 for _, addr := range addrs {
61 hosts = append(hosts, addr.String())
62 }
63 return
64 }
65
66 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) {
67 var hints C.struct_addrinfo
68 switch network {
69 case "":
70 case "tcp", "tcp4", "tcp6":
71 hints.ai_socktype = C.SOCK_STREAM
72 hints.ai_protocol = C.IPPROTO_TCP
73 case "udp", "udp4", "udp6":
74 hints.ai_socktype = C.SOCK_DGRAM
75 hints.ai_protocol = C.IPPROTO_UDP
76 default:
77 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
78 }
79 switch ipVersion(network) {
80 case '4':
81 hints.ai_family = C.AF_INET
82 case '6':
83 hints.ai_family = C.AF_INET6
84 }
85 if ctx.Done() == nil {
86 port, err := cgoLookupServicePort(&hints, network, service)
87 return port, err, true
88 }
89 result := make(chan portLookupResult, 1)
90 go cgoPortLookup(result, &hints, network, service)
91 select {
92 case r := <-result:
93 return r.port, r.err, true
94 case <-ctx.Done():
95
96
97 return 0, mapErr(ctx.Err()), false
98 }
99 }
100
101 func cgoLookupServicePort(hints *C.struct_addrinfo, network, service string) (port int, err error) {
102 cservice := make([]byte, len(service)+1)
103 copy(cservice, service)
104
105 for i, b := range cservice[:len(service)] {
106 cservice[i] = lowerASCII(b)
107 }
108 var res *C.struct_addrinfo
109 gerrno, err := C.getaddrinfo(nil, (*C.char)(unsafe.Pointer(&cservice[0])), hints, &res)
110 if gerrno != 0 {
111 isTemporary := false
112 switch gerrno {
113 case C.EAI_SYSTEM:
114 if err == nil {
115 err = syscall.EMFILE
116 }
117 default:
118 err = addrinfoErrno(gerrno)
119 isTemporary = addrinfoErrno(gerrno).Temporary()
120 }
121 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service, IsTemporary: isTemporary}
122 }
123 defer C.freeaddrinfo(res)
124
125 for r := res; r != nil; r = r.ai_next {
126 switch r.ai_family {
127 case C.AF_INET:
128 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
129 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
130 return int(p[0])<<8 | int(p[1]), nil
131 case C.AF_INET6:
132 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
133 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
134 return int(p[0])<<8 | int(p[1]), nil
135 }
136 }
137 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}
138 }
139
140 func cgoPortLookup(result chan<- portLookupResult, hints *C.struct_addrinfo, network, service string) {
141 port, err := cgoLookupServicePort(hints, network, service)
142 result <- portLookupResult{port, err}
143 }
144
145 func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) {
146 acquireThread()
147 defer releaseThread()
148
149 var hints C.struct_addrinfo
150 hints.ai_flags = cgoAddrInfoFlags
151 hints.ai_socktype = C.SOCK_STREAM
152 hints.ai_family = C.AF_UNSPEC
153 switch ipVersion(network) {
154 case '4':
155 hints.ai_family = C.AF_INET
156 case '6':
157 hints.ai_family = C.AF_INET6
158 }
159
160 h := make([]byte, len(name)+1)
161 copy(h, name)
162 var res *C.struct_addrinfo
163 gerrno, err := C.getaddrinfo((*C.char)(unsafe.Pointer(&h[0])), nil, &hints, &res)
164 if gerrno != 0 {
165 isErrorNoSuchHost := false
166 isTemporary := false
167 switch gerrno {
168 case C.EAI_SYSTEM:
169 if err == nil {
170
171
172
173
174
175
176
177 err = syscall.EMFILE
178 }
179 case C.EAI_NONAME:
180 err = errNoSuchHost
181 isErrorNoSuchHost = true
182 default:
183 err = addrinfoErrno(gerrno)
184 isTemporary = addrinfoErrno(gerrno).Temporary()
185 }
186
187 return nil, "", &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost, IsTemporary: isTemporary}
188 }
189 defer C.freeaddrinfo(res)
190
191 if res != nil {
192 cname = C.GoString(res.ai_canonname)
193 if cname == "" {
194 cname = name
195 }
196 if len(cname) > 0 && cname[len(cname)-1] != '.' {
197 cname += "."
198 }
199 }
200 for r := res; r != nil; r = r.ai_next {
201
202 if r.ai_socktype != C.SOCK_STREAM {
203 continue
204 }
205 switch r.ai_family {
206 case C.AF_INET:
207 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.ai_addr))
208 addr := IPAddr{IP: copyIP(sa.Addr[:])}
209 addrs = append(addrs, addr)
210 case C.AF_INET6:
211 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.ai_addr))
212 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))}
213 addrs = append(addrs, addr)
214 }
215 }
216 return addrs, cname, nil
217 }
218
219 func cgoIPLookup(result chan<- ipLookupResult, network, name string) {
220 addrs, cname, err := cgoLookupIPCNAME(network, name)
221 result <- ipLookupResult{addrs, cname, err}
222 }
223
224 func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
225 if ctx.Done() == nil {
226 addrs, _, err = cgoLookupIPCNAME(network, name)
227 return addrs, err, true
228 }
229 result := make(chan ipLookupResult, 1)
230 go cgoIPLookup(result, network, name)
231 select {
232 case r := <-result:
233 return r.addrs, r.err, true
234 case <-ctx.Done():
235 return nil, mapErr(ctx.Err()), false
236 }
237 }
238
239 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
240 if ctx.Done() == nil {
241 _, cname, err = cgoLookupIPCNAME("ip", name)
242 return cname, err, true
243 }
244 result := make(chan ipLookupResult, 1)
245 go cgoIPLookup(result, "ip", name)
246 select {
247 case r := <-result:
248 return r.cname, r.err, true
249 case <-ctx.Done():
250 return "", mapErr(ctx.Err()), false
251 }
252 }
253
254
255
256
257
258
259
260
261
262 const (
263 nameinfoLen = 64
264 maxNameinfoLen = 4096
265 )
266
267 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) {
268 var zone string
269 ip := parseIPv4(addr)
270 if ip == nil {
271 ip, zone = parseIPv6Zone(addr)
272 }
273 if ip == nil {
274 return nil, &DNSError{Err: "invalid address", Name: addr}, true
275 }
276 sa, salen := cgoSockaddr(ip, zone)
277 if sa == nil {
278 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
279 }
280 if ctx.Done() == nil {
281 names, err := cgoLookupAddrPTR(addr, sa, salen)
282 return names, err, true
283 }
284 result := make(chan reverseLookupResult, 1)
285 go cgoReverseLookup(result, addr, sa, salen)
286 select {
287 case r := <-result:
288 return r.names, r.err, true
289 case <-ctx.Done():
290 return nil, mapErr(ctx.Err()), false
291 }
292 }
293
294 func cgoLookupAddrPTR(addr string, sa *C.struct_sockaddr, salen C.socklen_t) (names []string, err error) {
295 acquireThread()
296 defer releaseThread()
297
298 var gerrno int
299 var b []byte
300 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
301 b = make([]byte, l)
302 gerrno, err = cgoNameinfoPTR(b, sa, salen)
303 if gerrno == 0 || gerrno != C.EAI_OVERFLOW {
304 break
305 }
306 }
307 if gerrno != 0 {
308 isTemporary := false
309 switch gerrno {
310 case C.EAI_SYSTEM:
311 if err == nil {
312 err = syscall.EMFILE
313 }
314 default:
315 err = addrinfoErrno(gerrno)
316 isTemporary = addrinfoErrno(gerrno).Temporary()
317 }
318 return nil, &DNSError{Err: err.Error(), Name: addr, IsTemporary: isTemporary}
319 }
320 for i := 0; i < len(b); i++ {
321 if b[i] == 0 {
322 b = b[:i]
323 break
324 }
325 }
326 return []string{absDomainName(b)}, nil
327 }
328
329 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *C.struct_sockaddr, salen C.socklen_t) {
330 names, err := cgoLookupAddrPTR(addr, sa, salen)
331 result <- reverseLookupResult{names, err}
332 }
333
334 func cgoSockaddr(ip IP, zone string) (*C.struct_sockaddr, C.socklen_t) {
335 if ip4 := ip.To4(); ip4 != nil {
336 return cgoSockaddrInet4(ip4), C.socklen_t(syscall.SizeofSockaddrInet4)
337 }
338 if ip6 := ip.To16(); ip6 != nil {
339 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), C.socklen_t(syscall.SizeofSockaddrInet6)
340 }
341 return nil, 0
342 }
343
344 func copyIP(x IP) IP {
345 if len(x) < 16 {
346 return x.To16()
347 }
348 y := make(IP, len(x))
349 copy(y, x)
350 return y
351 }
352
View as plain text