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.
6 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
26 func libc_getaddrinfo(node
*byte, service
*byte, hints
*syscall
.Addrinfo
, res
**syscall
.Addrinfo
) int32
29 func libc_freeaddrinfo(res
*syscall
.Addrinfo
)
32 func libc_gai_strerror(errcode
int) *byte
34 // bytePtrToString takes a NUL-terminated array of bytes and convert
36 func bytePtrToString(p
*byte) string {
37 a
:= (*[10000]byte)(unsafe
.Pointer(p
))
45 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
46 // error number. It's a signed number and a zero value is a non-error
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 {
59 type ipLookupResult
struct {
65 type reverseLookupResult
struct {
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())
78 func cgoLookupPort(ctx context
.Context
, network
, service
string) (port
int, err error
, completed
bool) {
79 var hints syscall
.Addrinfo
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
89 return 0, &DNSError
{Err
: "unknown network", Name
: network
+ "/" + service
}, true
91 if len(network
) >= 4 {
94 hints
.Ai_family
= syscall
.AF_INET
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
)
107 return r
.port
, r
.err
, true
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
)
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()
131 case syscall
.EAI_SYSTEM
:
132 errno
:= syscall
.GetErrno()
133 if errno
== 0 { // see golang.org/issue/6232
134 errno
= syscall
.EMFILE
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
{
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
) {
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()
179 case syscall
.EAI_SYSTEM
:
180 errno
:= syscall
.GetErrno()
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
192 case syscall
.EAI_NONAME
:
195 err
= addrinfoErrno(gerrno
)
197 return nil, "", &DNSError
{Err
: err
.Error(), Name
: name
}
199 defer libc_freeaddrinfo(res
)
202 cname
= bytePtrToString((*byte)(unsafe
.Pointer(res
.Ai_canonname
)))
206 if len(cname
) > 0 && cname
[len(cname
)-1] != '.' {
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
{
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
)
243 return r
.addrs
, r
.err
, true
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
)
258 return r
.cname
, r
.err
, true
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
274 maxNameinfoLen
= 4096
277 func cgoLookupPTR(ctx context
.Context
, addr
string) (names
[]string, err error
, completed
bool) {
279 ip
:= parseIPv4(addr
)
281 ip
, zone
= parseIPv6Zone(addr
)
284 return nil, &DNSError
{Err
: "invalid address", Name
: addr
}, true
286 sa
, salen
:= cgoSockaddr(ip
, zone
)
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
)
298 return r
.names
, r
.err
, true
300 return nil, mapErr(ctx
.Err()), false
304 func cgoLookupAddrPTR(addr
string, sa
*syscall
.RawSockaddr
, salen syscall
.Socklen_t
) (names
[]string, err error
) {
306 defer releaseThread()
310 for l
:= nameinfoLen
; l
<= maxNameinfoLen
; l
*= 2 {
312 gerrno
, err
= cgoNameinfoPTR(b
, sa
, salen
)
313 if gerrno
== 0 || gerrno
!= syscall
.EAI_OVERFLOW
{
319 case syscall
.EAI_SYSTEM
:
320 if err
== nil { // see golang.org/issue/6232
324 err
= addrinfoErrno(gerrno
)
326 return nil, &DNSError
{Err
: err
.Error(), Name
: addr
}
328 for i
:= 0; i
< len(b
); i
++ {
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
)
352 func copyIP(x IP
) IP
{
356 y
:= make(IP
, len(x
))