libgo: update to Go 1.11
[official-gcc.git] / libgo / go / net / cgo_unix.go
bloba4be3ba8293d03805ec44bca758bbab9f5f7854c
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.
5 // +build cgo,!netgo
6 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
8 package net
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>
19 import (
20 "context"
21 "syscall"
22 "unsafe"
25 //extern getaddrinfo
26 func libc_getaddrinfo(node *byte, service *byte, hints *syscall.Addrinfo, res **syscall.Addrinfo) int32
28 //extern freeaddrinfo
29 func libc_freeaddrinfo(res *syscall.Addrinfo)
31 //extern gai_strerror
32 func libc_gai_strerror(errcode int) *byte
34 // bytePtrToString takes a NUL-terminated array of bytes and convert
35 // it to a Go string.
36 func bytePtrToString(p *byte) string {
37 a := (*[10000]byte)(unsafe.Pointer(p))
38 i := 0
39 for a[i] != 0 {
40 i++
42 return string(a[:i])
45 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
46 // error number. It's a signed number and a zero value is a non-error
47 // by convention.
48 type addrinfoErrno int
50 func (eai addrinfoErrno) Error() string { return bytePtrToString(libc_gai_strerror(int(eai))) }
51 func (eai addrinfoErrno) Temporary() bool { return eai == syscall.EAI_AGAIN }
52 func (eai addrinfoErrno) Timeout() bool { return false }
54 type portLookupResult struct {
55 port int
56 err error
59 type ipLookupResult struct {
60 addrs []IPAddr
61 cname string
62 err error
65 type reverseLookupResult struct {
66 names []string
67 err error
70 func cgoLookupHost(ctx context.Context, name string) (hosts []string, err error, completed bool) {
71 addrs, err, completed := cgoLookupIP(ctx, name)
72 for _, addr := range addrs {
73 hosts = append(hosts, addr.String())
75 return
78 func cgoLookupPort(ctx context.Context, network, service string) (port int, err error, completed bool) {
79 var hints syscall.Addrinfo
80 switch network {
81 case "": // no hints
82 case "tcp", "tcp4", "tcp6":
83 hints.Ai_socktype = syscall.SOCK_STREAM
84 hints.Ai_protocol = syscall.IPPROTO_TCP
85 case "udp", "udp4", "udp6":
86 hints.Ai_socktype = syscall.SOCK_DGRAM
87 hints.Ai_protocol = syscall.IPPROTO_UDP
88 default:
89 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
91 if len(network) >= 4 {
92 switch network[3] {
93 case '4':
94 hints.Ai_family = syscall.AF_INET
95 case '6':
96 hints.Ai_family = syscall.AF_INET6
99 if ctx.Done() == nil {
100 port, err := cgoLookupServicePort(&hints, network, service)
101 return port, err, true
103 result := make(chan portLookupResult, 1)
104 go cgoPortLookup(result, &hints, network, service)
105 select {
106 case r := <-result:
107 return r.port, r.err, true
108 case <-ctx.Done():
109 // Since there isn't a portable way to cancel the lookup,
110 // we just let it finish and write to the buffered channel.
111 return 0, mapErr(ctx.Err()), false
115 func cgoLookupServicePort(hints *syscall.Addrinfo, network, service string) (port int, err error) {
116 s, err := syscall.BytePtrFromString(service)
117 if err != nil {
118 return 0, err
120 // Lowercase the service name in the memory passed to C.
121 for i := 0; i < len(service); i++ {
122 bp := (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(s)) + uintptr(i)))
123 *bp = lowerASCII(*bp)
125 var res *syscall.Addrinfo
126 syscall.Entersyscall()
127 gerrno := libc_getaddrinfo(nil, s, hints, &res)
128 syscall.Exitsyscall()
129 if gerrno != 0 {
130 switch gerrno {
131 case syscall.EAI_SYSTEM:
132 errno := syscall.GetErrno()
133 if errno == 0 { // see golang.org/issue/6232
134 errno = syscall.EMFILE
136 err = errno
137 default:
138 err = addrinfoErrno(gerrno)
140 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}
142 defer libc_freeaddrinfo(res)
144 for r := res; r != nil; r = r.Ai_next {
145 switch r.Ai_family {
146 case syscall.AF_INET:
147 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
148 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
149 return int(p[0])<<8 | int(p[1]), nil
150 case syscall.AF_INET6:
151 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
152 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
153 return int(p[0])<<8 | int(p[1]), nil
156 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}
159 func cgoPortLookup(result chan<- portLookupResult, hints *syscall.Addrinfo, network, service string) {
160 port, err := cgoLookupServicePort(hints, network, service)
161 result <- portLookupResult{port, err}
164 func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error) {
165 acquireThread()
166 defer releaseThread()
168 var hints syscall.Addrinfo
169 hints.Ai_flags = int32(cgoAddrInfoFlags)
170 hints.Ai_socktype = syscall.SOCK_STREAM
172 h := syscall.StringBytePtr(name)
173 var res *syscall.Addrinfo
174 syscall.Entersyscall()
175 gerrno := libc_getaddrinfo(h, nil, &hints, &res)
176 syscall.Exitsyscall()
177 if gerrno != 0 {
178 switch gerrno {
179 case syscall.EAI_SYSTEM:
180 errno := syscall.GetErrno()
181 if errno == 0 {
182 // err should not be nil, but sometimes getaddrinfo returns
183 // gerrno == C.EAI_SYSTEM with err == nil on Linux.
184 // The report claims that it happens when we have too many
185 // open files, so use syscall.EMFILE (too many open files in system).
186 // Most system calls would return ENFILE (too many open files),
187 // so at the least EMFILE should be easy to recognize if this
188 // comes up again. golang.org/issue/6232.
189 errno = syscall.EMFILE
191 err = errno
192 case syscall.EAI_NONAME:
193 err = errNoSuchHost
194 default:
195 err = addrinfoErrno(gerrno)
197 return nil, "", &DNSError{Err: err.Error(), Name: name}
199 defer libc_freeaddrinfo(res)
201 if res != nil {
202 cname = bytePtrToString((*byte)(unsafe.Pointer(res.Ai_canonname)))
203 if cname == "" {
204 cname = name
206 if len(cname) > 0 && cname[len(cname)-1] != '.' {
207 cname += "."
210 for r := res; r != nil; r = r.Ai_next {
211 // We only asked for SOCK_STREAM, but check anyhow.
212 if r.Ai_socktype != syscall.SOCK_STREAM {
213 continue
215 switch r.Ai_family {
216 case syscall.AF_INET:
217 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
218 addr := IPAddr{IP: copyIP(sa.Addr[:])}
219 addrs = append(addrs, addr)
220 case syscall.AF_INET6:
221 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
222 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneCache.name(int(sa.Scope_id))}
223 addrs = append(addrs, addr)
226 return addrs, cname, nil
229 func cgoIPLookup(result chan<- ipLookupResult, name string) {
230 addrs, cname, err := cgoLookupIPCNAME(name)
231 result <- ipLookupResult{addrs, cname, err}
234 func cgoLookupIP(ctx context.Context, name string) (addrs []IPAddr, err error, completed bool) {
235 if ctx.Done() == nil {
236 addrs, _, err = cgoLookupIPCNAME(name)
237 return addrs, err, true
239 result := make(chan ipLookupResult, 1)
240 go cgoIPLookup(result, name)
241 select {
242 case r := <-result:
243 return r.addrs, r.err, true
244 case <-ctx.Done():
245 return nil, mapErr(ctx.Err()), false
249 func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error, completed bool) {
250 if ctx.Done() == nil {
251 _, cname, err = cgoLookupIPCNAME(name)
252 return cname, err, true
254 result := make(chan ipLookupResult, 1)
255 go cgoIPLookup(result, name)
256 select {
257 case r := <-result:
258 return r.cname, r.err, true
259 case <-ctx.Done():
260 return "", mapErr(ctx.Err()), false
264 // These are roughly enough for the following:
266 // Source Encoding Maximum length of single name entry
267 // Unicast DNS ASCII or <=253 + a NUL terminator
268 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
269 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
270 // the same as unicast DNS ASCII <=253 + a NUL terminator
271 // Local database various depends on implementation
272 const (
273 nameinfoLen = 64
274 maxNameinfoLen = 4096
277 func cgoLookupPTR(ctx context.Context, addr string) (names []string, err error, completed bool) {
278 var zone string
279 ip := parseIPv4(addr)
280 if ip == nil {
281 ip, zone = parseIPv6Zone(addr)
283 if ip == nil {
284 return nil, &DNSError{Err: "invalid address", Name: addr}, true
286 sa, salen := cgoSockaddr(ip, zone)
287 if sa == nil {
288 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
290 if ctx.Done() == nil {
291 names, err := cgoLookupAddrPTR(addr, sa, salen)
292 return names, err, true
294 result := make(chan reverseLookupResult, 1)
295 go cgoReverseLookup(result, addr, sa, salen)
296 select {
297 case r := <-result:
298 return r.names, r.err, true
299 case <-ctx.Done():
300 return nil, mapErr(ctx.Err()), false
304 func cgoLookupAddrPTR(addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) (names []string, err error) {
305 acquireThread()
306 defer releaseThread()
308 var gerrno int
309 var b []byte
310 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
311 b = make([]byte, l)
312 gerrno, err = cgoNameinfoPTR(b, sa, salen)
313 if gerrno == 0 || gerrno != syscall.EAI_OVERFLOW {
314 break
317 if gerrno != 0 {
318 switch gerrno {
319 case syscall.EAI_SYSTEM:
320 if err == nil { // see golang.org/issue/6232
321 err = syscall.EMFILE
323 default:
324 err = addrinfoErrno(gerrno)
326 return nil, &DNSError{Err: err.Error(), Name: addr}
328 for i := 0; i < len(b); i++ {
329 if b[i] == 0 {
330 b = b[:i]
331 break
334 return []string{absDomainName(b)}, nil
337 func cgoReverseLookup(result chan<- reverseLookupResult, addr string, sa *syscall.RawSockaddr, salen syscall.Socklen_t) {
338 names, err := cgoLookupAddrPTR(addr, sa, salen)
339 result <- reverseLookupResult{names, err}
342 func cgoSockaddr(ip IP, zone string) (*syscall.RawSockaddr, syscall.Socklen_t) {
343 if ip4 := ip.To4(); ip4 != nil {
344 return cgoSockaddrInet4(ip4), syscall.Socklen_t(syscall.SizeofSockaddrInet4)
346 if ip6 := ip.To16(); ip6 != nil {
347 return cgoSockaddrInet6(ip6, zoneCache.index(zone)), syscall.Socklen_t(syscall.SizeofSockaddrInet6)
349 return nil, 0
352 func copyIP(x IP) IP {
353 if len(x) < 16 {
354 return x.To16()
356 y := make(IP, len(x))
357 copy(y, x)
358 return y