Update ChangeLog and version files for release
[official-gcc.git] / libgo / go / net / cgo_unix.go
blobf634323ed8bcc704953c3f37fa1516771fb9792a
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 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 <stdlib.h>
16 #include <unistd.h>
17 #include <string.h>
20 import (
21 "syscall"
22 "unsafe"
25 //extern getaddrinfo
26 func libc_getaddrinfo(node *byte, service *byte, hints *syscall.Addrinfo, res **syscall.Addrinfo) int
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 func cgoLookupHost(name string) (hosts []string, err error, completed bool) {
55 addrs, err, completed := cgoLookupIP(name)
56 for _, addr := range addrs {
57 hosts = append(hosts, addr.String())
59 return
62 func cgoLookupPort(network, service string) (port int, err error, completed bool) {
63 acquireThread()
64 defer releaseThread()
66 var hints syscall.Addrinfo
67 switch network {
68 case "": // no hints
69 case "tcp", "tcp4", "tcp6":
70 hints.Ai_socktype = syscall.SOCK_STREAM
71 hints.Ai_protocol = syscall.IPPROTO_TCP
72 case "udp", "udp4", "udp6":
73 hints.Ai_socktype = syscall.SOCK_DGRAM
74 hints.Ai_protocol = syscall.IPPROTO_UDP
75 default:
76 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}, true
78 if len(network) >= 4 {
79 switch network[3] {
80 case '4':
81 hints.Ai_family = syscall.AF_INET
82 case '6':
83 hints.Ai_family = syscall.AF_INET6
87 s := syscall.StringBytePtr(service)
88 var res *syscall.Addrinfo
89 syscall.Entersyscall()
90 gerrno := libc_getaddrinfo(nil, s, &hints, &res)
91 syscall.Exitsyscall()
92 if gerrno != 0 {
93 switch gerrno {
94 case syscall.EAI_SYSTEM:
95 errno := syscall.GetErrno()
96 if errno == 0 { // see golang.org/issue/6232
97 errno = syscall.EMFILE
99 err = errno
100 default:
101 err = addrinfoErrno(gerrno)
103 return 0, &DNSError{Err: err.Error(), Name: network + "/" + service}, true
105 defer libc_freeaddrinfo(res)
107 for r := res; r != nil; r = r.Ai_next {
108 switch r.Ai_family {
109 case syscall.AF_INET:
110 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
111 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
112 return int(p[0])<<8 | int(p[1]), nil, true
113 case syscall.AF_INET6:
114 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
115 p := (*[2]byte)(unsafe.Pointer(&sa.Port))
116 return int(p[0])<<8 | int(p[1]), nil, true
119 return 0, &DNSError{Err: "unknown port", Name: network + "/" + service}, true
122 func cgoLookupIPCNAME(name string) (addrs []IPAddr, cname string, err error, completed bool) {
123 acquireThread()
124 defer releaseThread()
126 var hints syscall.Addrinfo
127 hints.Ai_flags = int32(cgoAddrInfoFlags)
128 hints.Ai_socktype = syscall.SOCK_STREAM
130 h := syscall.StringBytePtr(name)
131 var res *syscall.Addrinfo
132 syscall.Entersyscall()
133 gerrno := libc_getaddrinfo(h, nil, &hints, &res)
134 syscall.Exitsyscall()
135 if gerrno != 0 {
136 switch gerrno {
137 case syscall.EAI_SYSTEM:
138 errno := syscall.GetErrno()
139 if errno == 0 {
140 // err should not be nil, but sometimes getaddrinfo returns
141 // gerrno == C.EAI_SYSTEM with err == nil on Linux.
142 // The report claims that it happens when we have too many
143 // open files, so use syscall.EMFILE (too many open files in system).
144 // Most system calls would return ENFILE (too many open files),
145 // so at the least EMFILE should be easy to recognize if this
146 // comes up again. golang.org/issue/6232.
147 errno = syscall.EMFILE
149 err = errno
150 case syscall.EAI_NONAME:
151 err = errNoSuchHost
152 default:
153 err = addrinfoErrno(gerrno)
155 return nil, "", &DNSError{Err: err.Error(), Name: name}, true
157 defer libc_freeaddrinfo(res)
159 if res != nil {
160 cname = bytePtrToString((*byte)(unsafe.Pointer(res.Ai_canonname)))
161 if cname == "" {
162 cname = name
164 if len(cname) > 0 && cname[len(cname)-1] != '.' {
165 cname += "."
168 for r := res; r != nil; r = r.Ai_next {
169 // We only asked for SOCK_STREAM, but check anyhow.
170 if r.Ai_socktype != syscall.SOCK_STREAM {
171 continue
173 switch r.Ai_family {
174 case syscall.AF_INET:
175 sa := (*syscall.RawSockaddrInet4)(unsafe.Pointer(r.Ai_addr))
176 addr := IPAddr{IP: copyIP(sa.Addr[:])}
177 addrs = append(addrs, addr)
178 case syscall.AF_INET6:
179 sa := (*syscall.RawSockaddrInet6)(unsafe.Pointer(r.Ai_addr))
180 addr := IPAddr{IP: copyIP(sa.Addr[:]), Zone: zoneToString(int(sa.Scope_id))}
181 addrs = append(addrs, addr)
184 return addrs, cname, nil, true
187 func cgoLookupIP(name string) (addrs []IPAddr, err error, completed bool) {
188 addrs, _, err, completed = cgoLookupIPCNAME(name)
189 return
192 func cgoLookupCNAME(name string) (cname string, err error, completed bool) {
193 _, cname, err, completed = cgoLookupIPCNAME(name)
194 return
197 // These are roughly enough for the following:
199 // Source Encoding Maximum length of single name entry
200 // Unicast DNS ASCII or <=253 + a NUL terminator
201 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
202 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
203 // the same as unicast DNS ASCII <=253 + a NUL terminator
204 // Local database various depends on implementation
205 const (
206 nameinfoLen = 64
207 maxNameinfoLen = 4096
210 func cgoLookupPTR(addr string) ([]string, error, bool) {
211 acquireThread()
212 defer releaseThread()
214 var zone string
215 ip := parseIPv4(addr)
216 if ip == nil {
217 ip, zone = parseIPv6(addr, true)
219 if ip == nil {
220 return nil, &DNSError{Err: "invalid address", Name: addr}, true
222 sa, salen := cgoSockaddr(ip, zone)
223 if sa == nil {
224 return nil, &DNSError{Err: "invalid address " + ip.String(), Name: addr}, true
226 var err error
227 var b []byte
228 var gerrno int
229 for l := nameinfoLen; l <= maxNameinfoLen; l *= 2 {
230 b = make([]byte, l)
231 gerrno, err = cgoNameinfoPTR(b, sa, salen)
232 if gerrno == 0 || gerrno != syscall.EAI_OVERFLOW {
233 break
236 if gerrno != 0 {
237 switch gerrno {
238 case syscall.EAI_SYSTEM:
239 if err == nil { // see golang.org/issue/6232
240 err = syscall.EMFILE
242 default:
243 err = addrinfoErrno(gerrno)
245 return nil, &DNSError{Err: err.Error(), Name: addr}, true
248 for i := 0; i < len(b); i++ {
249 if b[i] == 0 {
250 b = b[:i]
251 break
254 return []string{absDomainName(b)}, nil, true
257 func cgoSockaddr(ip IP, zone string) (*syscall.RawSockaddr, syscall.Socklen_t) {
258 if ip4 := ip.To4(); ip4 != nil {
259 return cgoSockaddrInet4(ip4), syscall.Socklen_t(syscall.SizeofSockaddrInet4)
261 if ip6 := ip.To16(); ip6 != nil {
262 return cgoSockaddrInet6(ip6, zoneToInt(zone)), syscall.Socklen_t(syscall.SizeofSockaddrInet6)
264 return nil, 0
267 func copyIP(x IP) IP {
268 if len(x) < 16 {
269 return x.To16()
271 y := make(IP, len(x))
272 copy(y, x)
273 return y