111
|
1 // Copyright 2011 The Go Authors. All rights reserved.
|
|
2 // Use of this source code is governed by a BSD-style
|
|
3 // license that can be found in the LICENSE file.
|
|
4
|
|
5 // +build cgo,!netgo
|
145
|
6 // +build aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris
|
111
|
7
|
|
8 package net
|
|
9
|
|
10 /*
|
|
11 #include <sys/types.h>
|
|
12 #include <sys/socket.h>
|
|
13 #include <netinet/in.h>
|
|
14 #include <netdb.h>
|
|
15 #include <unistd.h>
|
|
16 #include <string.h>
|
145
|
17
|
|
18 // If nothing else defined EAI_OVERFLOW, make sure it has a value.
|
|
19 #ifndef EAI_OVERFLOW
|
|
20 #define EAI_OVERFLOW -12
|
|
21 #endif
|
111
|
22 */
|
|
23
|
|
24 import (
|
|
25 "context"
|
|
26 "syscall"
|
|
27 "unsafe"
|
|
28 )
|
|
29
|
|
30 //extern getaddrinfo
|
131
|
31 func libc_getaddrinfo(node *byte, service *byte, hints *syscall.Addrinfo, res **syscall.Addrinfo) int32
|
111
|
32
|
|
33 //extern freeaddrinfo
|
|
34 func libc_freeaddrinfo(res *syscall.Addrinfo)
|
|
35
|
|
36 //extern gai_strerror
|
|
37 func libc_gai_strerror(errcode int) *byte
|
|
38
|
|
39 // bytePtrToString takes a NUL-terminated array of bytes and convert
|
|
40 // it to a Go string.
|
|
41 func bytePtrToString(p *byte) string {
|
|
42 a := (*[10000]byte)(unsafe.Pointer(p))
|
|
43 i := 0
|
|
44 for a[i] != 0 {
|
|
45 i++
|
|
46 }
|
|
47 return string(a[:i])
|
|
48 }
|
|
49
|
|
50 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
|
|
51 // error number. It's a signed number and a zero value is a non-error
|
|
52 // by convention.
|
|
53 type addrinfoErrno int
|
|
54
|
|
55 func (eai addrinfoErrno) Error() string { return bytePtrToString(libc_gai_strerror(int(eai))) }
|
|
56 func (eai addrinfoErrno) Temporary() bool { return eai == syscall.EAI_AGAIN }
|
|
57 func (eai addrinfoErrno) Timeout() bool { return false }
|
|
58
|
|
59 type portLookupResult struct {
|
|
60 port int
|
|
61 err error
|
|
62 }
|
|
63
|
|
64 type ipLookupResult struct {
|
|
65 addrs []IPAddr
|
|
66 cname string
|
|
67 err error
|
|
68 }
|
|
69
|
|
70 type reverseLookupResult struct {
|
|
71 names []string
|
|
72 err error
|
|
73 }
|
|
74
|
|
75 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
|
145
|
76 addrs, err, completed := cgoLookupIP(ctx, "ip", name)
|
111
|
77 for _, addr := range addrs {
|
|
78 hosts = append(hosts, addr.String())
|
|
79 }
|
|
80 return
|
|
81 }
|
|
82
|
|
83 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) {
|
|
84 var hints syscall.Addrinfo
|
|
85 switch network {
|
|
86 case "": // no hints
|
|
87 case "tcp", "tcp4", "tcp6":
|
|
88 hints.Ai_socktype = syscall.SOCK_STREAM
|
|
89 hints.Ai_protocol = syscall.IPPROTO_TCP
|
|
90 case "udp", "udp4", "udp6":
|
|
91 hints.Ai_socktype = syscall.SOCK_DGRAM
|
|
92 hints.Ai_protocol = syscall.IPPROTO_UDP
|
|
93 default:
|
|
94 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
|
|
95 }
|
145
|
96 switch ipVersion(network) {
|
|
97 case '4':
|
|
98 hints.Ai_family = syscall.AF_INET
|
|
99 case '6':
|
|
100 hints.Ai_family = syscall.AF_INET6
|
111
|
101 }
|
|
102 if ctx.Done() == nil {
|
|
103 port, err := cgoLookupServicePort(&hints, network, service)
|
|
104 return port, err, true
|
|
105 }
|
|
106 result := make(chan portLookupResult, 1)
|
|
107 go cgoPortLookup(result, &hints, network, service)
|
|
108 select {
|
|
109 case r := <-result:
|
|
110 return r.port, r.err, true
|
|
111 case <-ctx.Done():
|
|
112 // Since there isn't a portable way to cancel the lookup,
|
|
113 // we just let it finish and write to the buffered channel.
|
|
114 return 0, mapErr(ctx.Err()), false
|
|
115 }
|
|
116 }
|
|
117
|
|
118 func cgoLookupServicePort(hints *syscall.Addrinfo, network, service string) (port int, err error) {
|
|
119 s, err := syscall.BytePtrFromString(service)
|
|
120 if err != nil {
|
|
121 return 0, err
|
|
122 }
|
|
123 // Lowercase the service name in the memory passed to C.
|
|
124 for i := 0; i < len(service); i++ {
|
|
125 bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i)))
|
|
126 *bp = lowerASCII(*bp)
|
|
127 }
|
|
128 var res *syscall.Addrinfo
|
|
129 syscall.Entersyscall()
|
|
130 gerrno := libc_getaddrinfo(nil, s, hints, &res)
|
|
131 syscall.Exitsyscall()
|
|
132 if gerrno != 0 {
|
145
|
133 isTemporary := false
|
111
|
134 switch gerrno {
|
|
135 case syscall.EAI_SYSTEM:
|
|
136 errno := syscall.GetErrno()
|
|
137 if errno == 0 { // see golang.org/issue/6232
|
|
138 errno = syscall.EMFILE
|
|
139 }
|
|
140 err = errno
|
|
141 default:
|
|
142 err = addrinfoErrno(gerrno)
|
145
|
143 isTemporary = addrinfoErrno(gerrno).Temporary()
|
111
|
144 }
|
145
|
145 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service, IsTemporary: isTemporary}
|
111
|
146 }
|
|
147 defer libc_freeaddrinfo(res)
|
|
148
|
|
149 for r := res; r != nil; r = r.Ai_next {
|
|
150 switch r.Ai_family {
|
|
151 case syscall.AF_INET:
|
|
152 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
|
|
153 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
|
|
154 return int(p[0])<<8 | int(p[1]), nil
|
|
155 case syscall.AF_INET6:
|
|
156 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
|
|
157 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
|
|
158 return int(p[0])<<8 | int(p[1]), nil
|
|
159 }
|
|
160 }
|
|
161 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}
|
|
162 }
|
|
163
|
|
164 func cgoPortLookup(result chan<- portLookupResult, hints *syscall.Addrinfo, network, service string) {
|
|
165 port, err := cgoLookupServicePort(hints, network, service)
|
|
166 result <- portLookupResult{port, err}
|
|
167 }
|
|
168
|
145
|
169 func cgoLookupIPCNAME(network, name string) (addrs []IPAddr, cname string, err error) {
|
111
|
170 acquireThread()
|
|
171 defer releaseThread()
|
|
172
|
|
173 var hints syscall.Addrinfo
|
|
174 hints.Ai_flags = int32(cgoAddrInfoFlags)
|
|
175 hints.Ai_socktype = syscall.SOCK_STREAM
|
145
|
176 hints.Ai_family = syscall.AF_UNSPEC
|
|
177 switch ipVersion(network) {
|
|
178 case '4':
|
|
179 hints.Ai_family = syscall.AF_INET
|
|
180 case '6':
|
|
181 hints.Ai_family = syscall.AF_INET6
|
|
182 }
|
111
|
183
|
|
184 h := syscall.StringBytePtr(name)
|
|
185 var res *syscall.Addrinfo
|
|
186 syscall.Entersyscall()
|
|
187 gerrno := libc_getaddrinfo(h, nil, &hints, &res)
|
|
188 syscall.Exitsyscall()
|
|
189 if gerrno != 0 {
|
145
|
190 isErrorNoSuchHost := false
|
|
191 isTemporary := false
|
111
|
192 switch gerrno {
|
|
193 case syscall.EAI_SYSTEM:
|
|
194 errno := syscall.GetErrno()
|
|
195 if errno == 0 {
|
|
196 // err should not be nil, but sometimes getaddrinfo returns
|
|
197 // gerrno == C.EAI_SYSTEM with err == nil on Linux.
|
|
198 // The report claims that it happens when we have too many
|
|
199 // open files, so use syscall.EMFILE (too many open files in system).
|
|
200 // Most system calls would return ENFILE (too many open files),
|
|
201 // so at the least EMFILE should be easy to recognize if this
|
|
202 // comes up again. golang.org/issue/6232.
|
|
203 errno = syscall.EMFILE
|
|
204 }
|
|
205 err = errno
|
|
206 case syscall.EAI_NONAME:
|
|
207 err = errNoSuchHost
|
145
|
208 isErrorNoSuchHost = true
|
111
|
209 default:
|
|
210 err = addrinfoErrno(gerrno)
|
145
|
211 isTemporary = addrinfoErrno(gerrno).Temporary()
|
111
|
212 }
|
145
|
213
|
|
214 return nil, "", &DNSError{Err: err.Error(), Name: name, IsNotFound: isErrorNoSuchHost, IsTemporary: isTemporary}
|
111
|
215 }
|
|
216 defer libc_freeaddrinfo(res)
|
|
217
|
|
218 if res != nil {
|
|
219 cname = bytePtrToString((*byte)(unsafe.Pointer(res.Ai_canonname)))
|
|
220 if cname == "" {
|
|
221 cname = name
|
|
222 }
|
|
223 if len(cname) > 0 && cname[len(cname)-1] != '.' {
|
|
224 cname += "."
|
|
225 }
|
|
226 }
|
|
227 for r := res; r != nil; r = r.Ai_next {
|
|
228 // We only asked for SOCK_STREAM, but check anyhow.
|
|
229 if r.Ai_socktype != syscall.SOCK_STREAM {
|
|
230 continue
|
|
231 }
|
|
232 switch r.Ai_family {
|
|
233 case syscall.AF_INET:
|
|
234 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
|
|
235 addr := IPAddr{IP: copyIP(sa.Addr[:])}
|
|
236 addrs = append(addrs, addr)
|
|
237 case syscall.AF_INET6:
|
|
238 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
|
|
239 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))}
|
|
240 addrs = append(addrs, addr)
|
|
241 }
|
|
242 }
|
|
243 return addrs, cname, nil
|
|
244 }
|
|
245
|
145
|
246 func cgoIPLookup(result chan<- ipLookupResult, network, name string) {
|
|
247 addrs, cname, err := cgoLookupIPCNAME(network, name)
|
111
|
248 result <- ipLookupResult{addrs, cname, err}
|
|
249 }
|
|
250
|
145
|
251 func cgoLookupIP(ctx context.Context, network, name string) (addrs []IPAddr, err error, completed bool) {
|
111
|
252 if ctx.Done() == nil {
|
145
|
253 addrs, _, err = cgoLookupIPCNAME(network, name)
|
111
|
254 return addrs, err, true
|
|
255 }
|
|
256 result := make(chan ipLookupResult, 1)
|
145
|
257 go cgoIPLookup(result, network, name)
|
111
|
258 select {
|
|
259 case r := <-result:
|
|
260 return r.addrs, r.err, true
|
|
261 case <-ctx.Done():
|
|
262 return nil, mapErr(ctx.Err()), false
|
|
263 }
|
|
264 }
|
|
265
|
|
266 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
|
|
267 if ctx.Done() == nil {
|
145
|
268 _, cname, err = cgoLookupIPCNAME("ip", name)
|
111
|
269 return cname, err, true
|
|
270 }
|
|
271 result := make(chan ipLookupResult, 1)
|
145
|
272 go cgoIPLookup(result, "ip", name)
|
111
|
273 select {
|
|
274 case r := <-result:
|
|
275 return r.cname, r.err, true
|
|
276 case <-ctx.Done():
|
|
277 return "", mapErr(ctx.Err()), false
|
|
278 }
|
|
279 }
|
|
280
|
|
281 // These are roughly enough for the following:
|
|
282 //
|
|
283 // Source Encoding Maximum length of single name entry
|
|
284 // Unicast DNS ASCII or <=253 + a NUL terminator
|
|
285 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
|
|
286 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
|
|
287 // the same as unicast DNS ASCII <=253 + a NUL terminator
|
|
288 // Local database various depends on implementation
|
|
289 const (
|
|
290 nameinfoLen = 64
|
|
291 maxNameinfoLen = 4096
|
|
292 )
|
|
293
|
|
294 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) {
|
|
295 var zone string
|
|
296 ip := parseIPv4(addr)
|
|
297 if ip == nil {
|
131
|
298 ip, zone = parseIPv6Zone(addr)
|
111
|
299 }
|
|
300 if ip == nil {
|
|
301 return nil, &DNSError{Err: "invalid address", Name: addr}, true
|
|
302 }
|
|
303 sa, salen := cgoSockaddr(ip, zone)
|
|
304 if sa == nil {
|
|
305 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
|
|
306 }
|
|
307 if ctx.Done() == nil {
|
|
308 names, err := cgoLookupAddrPTR(addr, sa, salen)
|
|
309 return names, err, true
|
|
310 }
|
|
311 result := make(chan reverseLookupResult, 1)
|
|
312 go cgoReverseLookup(result, addr, sa, salen)
|
|
313 select {
|
|
314 case r := <-result:
|
|
315 return r.names, r.err, true
|
|
316 case <-ctx.Done():
|
|
317 return nil, mapErr(ctx.Err()), false
|
|
318 }
|
|
319 }
|
|
320
|
|
321 func cgoLookupAddrPTR(addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) (names []string, err error) {
|
|
322 acquireThread()
|
|
323 defer releaseThread()
|
|
324
|
|
325 var gerrno int
|
|
326 var b []byte
|
|
327 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
|
|
328 b = make([]byte, l)
|
|
329 gerrno, err = cgoNameinfoPTR(b, sa, salen)
|
|
330 if gerrno == 0 || gerrno != syscall.EAI_OVERFLOW {
|
|
331 break
|
|
332 }
|
|
333 }
|
|
334 if gerrno != 0 {
|
145
|
335 isTemporary := false
|
111
|
336 switch gerrno {
|
|
337 case syscall.EAI_SYSTEM:
|
|
338 if err == nil { // see golang.org/issue/6232
|
|
339 err = syscall.EMFILE
|
|
340 }
|
|
341 default:
|
|
342 err = addrinfoErrno(gerrno)
|
145
|
343 isTemporary = addrinfoErrno(gerrno).Temporary()
|
111
|
344 }
|
145
|
345 return nil, &DNSError{Err: err.Error(), Name: addr, IsTemporary: isTemporary}
|
111
|
346 }
|
|
347 for i := 0; i < len(b); i++ {
|
|
348 if b[i] == 0 {
|
|
349 b = b[:i]
|
|
350 break
|
|
351 }
|
|
352 }
|
|
353 return []string{absDomainName(b)}, nil
|
|
354 }
|
|
355
|
|
356 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) {
|
|
357 names, err := cgoLookupAddrPTR(addr, sa, salen)
|
|
358 result <- reverseLookupResult{names, err}
|
|
359 }
|
|
360
|
|
361 func cgoSockaddr(ip IP, zone string) (*syscall.RawSockaddr, syscall.Socklen_t) {
|
|
362 if ip4 := ip.To4(); ip4 != nil {
|
|
363 return cgoSockaddrInet4(ip4), syscall.Socklen_t(syscall.SizeofSockaddrInet4)
|
|
364 }
|
|
365 if ip6 := ip.To16(); ip6 != nil {
|
|
366 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), syscall.Socklen_t(syscall.SizeofSockaddrInet6)
|
|
367 }
|
|
368 return nil, 0
|
|
369 }
|
|
370
|
|
371 func copyIP(x IP) IP {
|
|
372 if len(x) < 16 {
|
|
373 return x.To16()
|
|
374 }
|
|
375 y := make(IP, len(x))
|
|
376 copy(y, x)
|
|
377 return y
|
|
378 }
|