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