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