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>
27 func libc_getaddrinfo(node
*byte, service
*byte, hints
*syscall
.Addrinfo
, res
**syscall
.Addrinfo
) int
30 func libc_freeaddrinfo(res
*syscall
.Addrinfo
)
33 func libc_gai_strerror(errcode
int) *byte
35 // bytePtrToString takes a NUL-terminated array of bytes and convert
37 func bytePtrToString(p
*byte) string {
38 a
:= (*[10000]byte)(unsafe
.Pointer(p
))
46 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
47 // error number. It's a signed number and a zero value is a non-error
49 type addrinfoErrno
int
51 func (eai addrinfoErrno
) Error() string { return bytePtrToString(libc_gai_strerror(int(eai
))) }
52 func (eai addrinfoErrno
) Temporary() bool { return eai
== syscall
.EAI_AGAIN
}
53 func (eai addrinfoErrno
) Timeout() bool { return false }
55 type portLookupResult
struct {
60 type ipLookupResult
struct {
66 type reverseLookupResult
struct {
71 func cgoLookupHost(ctx context
.Context
, name
string) (hosts
[]string, err error
, completed
bool) {
72 addrs
, err
, completed
:= cgoLookupIP(ctx
, name
)
73 for _
, addr
:= range addrs
{
74 hosts
= append(hosts
, addr
.String())
79 func cgoLookupPort(ctx context
.Context
, network
, service
string) (port
int, err error
, completed
bool) {
80 var hints syscall
.Addrinfo
83 case "tcp", "tcp4", "tcp6":
84 hints
.Ai_socktype
= syscall
.SOCK_STREAM
85 hints
.Ai_protocol
= syscall
.IPPROTO_TCP
86 case "udp", "udp4", "udp6":
87 hints
.Ai_socktype
= syscall
.SOCK_DGRAM
88 hints
.Ai_protocol
= syscall
.IPPROTO_UDP
90 return 0, &DNSError
{Err
: "unknown network", Name
: network
+ "/" + service
}, true
92 if len(network
) >= 4 {
95 hints
.Ai_family
= syscall
.AF_INET
97 hints
.Ai_family
= syscall
.AF_INET6
100 if ctx
.Done() == nil {
101 port
, err
:= cgoLookupServicePort(&hints
, network
, service
)
102 return port
, err
, true
104 result
:= make(chan portLookupResult
, 1)
105 go cgoPortLookup(result
, &hints
, network
, service
)
108 return r
.port
, r
.err
, true
110 // Since there isn't a portable way to cancel the lookup,
111 // we just let it finish and write to the buffered channel.
112 return 0, mapErr(ctx
.Err()), false
116 func cgoLookupServicePort(hints
*syscall
.Addrinfo
, network
, service
string) (port
int, err error
) {
117 s
, err
:= syscall
.BytePtrFromString(service
)
121 // Lowercase the service name in the memory passed to C.
122 for i
:= 0; i
< len(service
); i
++ {
123 bp
:= (*byte)(unsafe
.Pointer(uintptr(unsafe
.Pointer(s
)) + uintptr(i
)))
124 *bp
= lowerASCII(*bp
)
126 var res
*syscall
.Addrinfo
127 syscall
.Entersyscall()
128 gerrno
:= libc_getaddrinfo(nil, s
, hints
, &res
)
129 syscall
.Exitsyscall()
132 case syscall
.EAI_SYSTEM
:
133 errno
:= syscall
.GetErrno()
134 if errno
== 0 { // see golang.org/issue/6232
135 errno
= syscall
.EMFILE
139 err
= addrinfoErrno(gerrno
)
141 return 0, &DNSError
{Err
: err
.Error(), Name
: network
+ "/" + service
}
143 defer libc_freeaddrinfo(res
)
145 for r
:= res
; r
!= nil; r
= r
.Ai_next
{
147 case syscall
.AF_INET
:
148 sa
:= (*syscall
.RawSockaddrInet4
)(unsafe
.Pointer(r
.Ai_addr
))
149 p
:= (*[2]byte)(unsafe
.Pointer(&sa
.Port
))
150 return int(p
[0])<<8 |
int(p
[1]), nil
151 case syscall
.AF_INET6
:
152 sa
:= (*syscall
.RawSockaddrInet6
)(unsafe
.Pointer(r
.Ai_addr
))
153 p
:= (*[2]byte)(unsafe
.Pointer(&sa
.Port
))
154 return int(p
[0])<<8 |
int(p
[1]), nil
157 return 0, &DNSError
{Err
: "unknown port", Name
: network
+ "/" + service
}
160 func cgoPortLookup(result
chan<- portLookupResult
, hints
*syscall
.Addrinfo
, network
, service
string) {
161 port
, err
:= cgoLookupServicePort(hints
, network
, service
)
162 result
<- portLookupResult
{port
, err
}
165 func cgoLookupIPCNAME(name
string) (addrs
[]IPAddr
, cname
string, err error
) {
167 defer releaseThread()
169 var hints syscall
.Addrinfo
170 hints
.Ai_flags
= int32(cgoAddrInfoFlags
)
171 hints
.Ai_socktype
= syscall
.SOCK_STREAM
173 h
:= syscall
.StringBytePtr(name
)
174 var res
*syscall
.Addrinfo
175 syscall
.Entersyscall()
176 gerrno
:= libc_getaddrinfo(h
, nil, &hints
, &res
)
177 syscall
.Exitsyscall()
180 case syscall
.EAI_SYSTEM
:
181 errno
:= syscall
.GetErrno()
183 // err should not be nil, but sometimes getaddrinfo returns
184 // gerrno == C.EAI_SYSTEM with err == nil on Linux.
185 // The report claims that it happens when we have too many
186 // open files, so use syscall.EMFILE (too many open files in system).
187 // Most system calls would return ENFILE (too many open files),
188 // so at the least EMFILE should be easy to recognize if this
189 // comes up again. golang.org/issue/6232.
190 errno
= syscall
.EMFILE
193 case syscall
.EAI_NONAME
:
196 err
= addrinfoErrno(gerrno
)
198 return nil, "", &DNSError
{Err
: err
.Error(), Name
: name
}
200 defer libc_freeaddrinfo(res
)
203 cname
= bytePtrToString((*byte)(unsafe
.Pointer(res
.Ai_canonname
)))
207 if len(cname
) > 0 && cname
[len(cname
)-1] != '.' {
211 for r
:= res
; r
!= nil; r
= r
.Ai_next
{
212 // We only asked for SOCK_STREAM, but check anyhow.
213 if r
.Ai_socktype
!= syscall
.SOCK_STREAM
{
217 case syscall
.AF_INET
:
218 sa
:= (*syscall
.RawSockaddrInet4
)(unsafe
.Pointer(r
.Ai_addr
))
219 addr
:= IPAddr
{IP
: copyIP(sa
.Addr
[:])}
220 addrs
= append(addrs
, addr
)
221 case syscall
.AF_INET6
:
222 sa
:= (*syscall
.RawSockaddrInet6
)(unsafe
.Pointer(r
.Ai_addr
))
223 addr
:= IPAddr
{IP
: copyIP(sa
.Addr
[:]), Zone
: zoneToString(int(sa
.Scope_id
))}
224 addrs
= append(addrs
, addr
)
227 return addrs
, cname
, nil
230 func cgoIPLookup(result
chan<- ipLookupResult
, name
string) {
231 addrs
, cname
, err
:= cgoLookupIPCNAME(name
)
232 result
<- ipLookupResult
{addrs
, cname
, err
}
235 func cgoLookupIP(ctx context
.Context
, name
string) (addrs
[]IPAddr
, err error
, completed
bool) {
236 if ctx
.Done() == nil {
237 addrs
, _
, err
= cgoLookupIPCNAME(name
)
238 return addrs
, err
, true
240 result
:= make(chan ipLookupResult
, 1)
241 go cgoIPLookup(result
, name
)
244 return r
.addrs
, r
.err
, true
246 return nil, mapErr(ctx
.Err()), false
250 func cgoLookupCNAME(ctx context
.Context
, name
string) (cname
string, err error
, completed
bool) {
251 if ctx
.Done() == nil {
252 _
, cname
, err
= cgoLookupIPCNAME(name
)
253 return cname
, err
, true
255 result
:= make(chan ipLookupResult
, 1)
256 go cgoIPLookup(result
, name
)
259 return r
.cname
, r
.err
, true
261 return "", mapErr(ctx
.Err()), false
265 // These are roughly enough for the following:
267 // Source Encoding Maximum length of single name entry
268 // Unicast DNS ASCII or <=253 + a NUL terminator
269 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
270 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
271 // the same as unicast DNS ASCII <=253 + a NUL terminator
272 // Local database various depends on implementation
275 maxNameinfoLen
= 4096
278 func cgoLookupPTR(ctx context
.Context
, addr
string) (names
[]string, err error
, completed
bool) {
280 ip
:= parseIPv4(addr
)
282 ip
, zone
= parseIPv6(addr
, true)
285 return nil, &DNSError
{Err
: "invalid address", Name
: addr
}, true
287 sa
, salen
:= cgoSockaddr(ip
, zone
)
289 return nil, &DNSError
{Err
: "invalid address " + ip
.String(), Name
: addr
}, true
291 if ctx
.Done() == nil {
292 names
, err
:= cgoLookupAddrPTR(addr
, sa
, salen
)
293 return names
, err
, true
295 result
:= make(chan reverseLookupResult
, 1)
296 go cgoReverseLookup(result
, addr
, sa
, salen
)
299 return r
.names
, r
.err
, true
301 return nil, mapErr(ctx
.Err()), false
305 func cgoLookupAddrPTR(addr
string, sa
*syscall
.RawSockaddr
, salen syscall
.Socklen_t
) (names
[]string, err error
) {
307 defer releaseThread()
311 for l
:= nameinfoLen
; l
<= maxNameinfoLen
; l
*= 2 {
313 gerrno
, err
= cgoNameinfoPTR(b
, sa
, salen
)
314 if gerrno
== 0 || gerrno
!= syscall
.EAI_OVERFLOW
{
320 case syscall
.EAI_SYSTEM
:
321 if err
== nil { // see golang.org/issue/6232
325 err
= addrinfoErrno(gerrno
)
327 return nil, &DNSError
{Err
: err
.Error(), Name
: addr
}
329 for i
:= 0; i
< len(b
); i
++ {
335 return []string{absDomainName(b
)}, nil
338 func cgoReverseLookup(result
chan<- reverseLookupResult
, addr
string, sa
*syscall
.RawSockaddr
, salen syscall
.Socklen_t
) {
339 names
, err
:= cgoLookupAddrPTR(addr
, sa
, salen
)
340 result
<- reverseLookupResult
{names
, err
}
343 func cgoSockaddr(ip IP
, zone
string) (*syscall
.RawSockaddr
, syscall
.Socklen_t
) {
344 if ip4
:= ip
.To4(); ip4
!= nil {
345 return cgoSockaddrInet4(ip4
), syscall
.Socklen_t(syscall
.SizeofSockaddrInet4
)
347 if ip6
:= ip
.To16(); ip6
!= nil {
348 return cgoSockaddrInet6(ip6
, zoneToInt(zone
)), syscall
.Socklen_t(syscall
.SizeofSockaddrInet6
)
353 func copyIP(x IP
) IP
{
357 y
:= make(IP
, len(x
))