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 //go:build cgo && !netgo && (aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris)
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
17 // If nothing else defined EAI_OVERFLOW, make sure it has a value.
19 #define EAI_OVERFLOW -12
30 func libc_getaddrinfo(node
*byte, service
*byte, hints
*syscall
.Addrinfo
, res
**syscall
.Addrinfo
) int32
33 func libc_freeaddrinfo(res
*syscall
.Addrinfo
)
36 func libc_gai_strerror(errcode
int) *byte
38 // bytePtrToString takes a NUL-terminated array of bytes and convert
40 func bytePtrToString(p
*byte) string {
41 a
:= (*[10000]byte)(unsafe
.Pointer(p
))
49 // An addrinfoErrno represents a getaddrinfo, getnameinfo-specific
50 // error number. It's a signed number and a zero value is a non-error
52 type addrinfoErrno
int
54 func (eai addrinfoErrno
) Error() string { return bytePtrToString(libc_gai_strerror(int(eai
))) }
55 func (eai addrinfoErrno
) Temporary() bool { return eai
== syscall
.EAI_AGAIN
}
56 func (eai addrinfoErrno
) Timeout() bool { return false }
58 type portLookupResult
struct {
63 type ipLookupResult
struct {
69 type reverseLookupResult
struct {
74 func cgoLookupHost(ctx context
.Context
, name
string) (hosts
[]string, err error
, completed
bool) {
75 addrs
, err
, completed
:= cgoLookupIP(ctx
, "ip", name
)
76 for _
, addr
:= range addrs
{
77 hosts
= append(hosts
, addr
.String())
82 func cgoLookupPort(ctx context
.Context
, network
, service
string) (port
int, err error
, completed
bool) {
83 var hints syscall
.Addrinfo
86 case "tcp", "tcp4", "tcp6":
87 hints
.Ai_socktype
= syscall
.SOCK_STREAM
88 hints
.Ai_protocol
= syscall
.IPPROTO_TCP
89 case "udp", "udp4", "udp6":
90 hints
.Ai_socktype
= syscall
.SOCK_DGRAM
91 hints
.Ai_protocol
= syscall
.IPPROTO_UDP
93 return 0, &DNSError
{Err
: "unknown network", Name
: network
+ "/" + service
}, true
95 switch ipVersion(network
) {
97 hints
.Ai_family
= syscall
.AF_INET
99 hints
.Ai_family
= syscall
.AF_INET6
101 if ctx
.Done() == nil {
102 port
, err
:= cgoLookupServicePort(&hints
, network
, service
)
103 return port
, err
, true
105 result
:= make(chan portLookupResult
, 1)
106 go cgoPortLookup(result
, &hints
, network
, service
)
109 return r
.port
, r
.err
, true
111 // Since there isn't a portable way to cancel the lookup,
112 // we just let it finish and write to the buffered channel.
113 return 0, mapErr(ctx
.Err()), false
117 func cgoLookupServicePort(hints
*syscall
.Addrinfo
, network
, service
string) (port
int, err error
) {
118 s
, err
:= syscall
.BytePtrFromString(service
)
122 // Lowercase the service name in the memory passed to C.
123 for i
:= 0; i
< len(service
); i
++ {
124 bp
:= (*byte)(unsafe
.Pointer(uintptr(unsafe
.Pointer(s
)) + uintptr(i
)))
125 *bp
= lowerASCII(*bp
)
127 var res
*syscall
.Addrinfo
128 syscall
.Entersyscall()
129 gerrno
:= libc_getaddrinfo(nil, s
, hints
, &res
)
130 syscall
.Exitsyscall()
134 case syscall
.EAI_SYSTEM
:
135 errno
:= syscall
.GetErrno()
136 if errno
== 0 { // see golang.org/issue/6232
137 errno
= syscall
.EMFILE
141 err
= addrinfoErrno(gerrno
)
142 isTemporary
= addrinfoErrno(gerrno
).Temporary()
144 return 0, &DNSError
{Err
: err
.Error(), Name
: network
+ "/" + service
, IsTemporary
: isTemporary
}
146 defer libc_freeaddrinfo(res
)
148 for r
:= res
; r
!= nil; r
= r
.Ai_next
{
150 case syscall
.AF_INET
:
151 sa
:= (*syscall
.RawSockaddrInet4
)(unsafe
.Pointer(r
.Ai_addr
))
152 p
:= (*[2]byte)(unsafe
.Pointer(&sa
.Port
))
153 return int(p
[0])<<8 |
int(p
[1]), nil
154 case syscall
.AF_INET6
:
155 sa
:= (*syscall
.RawSockaddrInet6
)(unsafe
.Pointer(r
.Ai_addr
))
156 p
:= (*[2]byte)(unsafe
.Pointer(&sa
.Port
))
157 return int(p
[0])<<8 |
int(p
[1]), nil
160 return 0, &DNSError
{Err
: "unknown port", Name
: network
+ "/" + service
}
163 func cgoPortLookup(result
chan<- portLookupResult
, hints
*syscall
.Addrinfo
, network
, service
string) {
164 port
, err
:= cgoLookupServicePort(hints
, network
, service
)
165 result
<- portLookupResult
{port
, err
}
168 func cgoLookupIPCNAME(network
, name
string) (addrs
[]IPAddr
, cname
string, err error
) {
170 defer releaseThread()
172 var hints syscall
.Addrinfo
173 hints
.Ai_flags
= int32(cgoAddrInfoFlags
)
174 hints
.Ai_socktype
= syscall
.SOCK_STREAM
175 hints
.Ai_family
= syscall
.AF_UNSPEC
176 switch ipVersion(network
) {
178 hints
.Ai_family
= syscall
.AF_INET
180 hints
.Ai_family
= syscall
.AF_INET6
183 h
:= syscall
.StringBytePtr(name
)
184 var res
*syscall
.Addrinfo
185 syscall
.Entersyscall()
186 gerrno
:= libc_getaddrinfo(h
, nil, &hints
, &res
)
187 syscall
.Exitsyscall()
189 isErrorNoSuchHost
:= false
192 case syscall
.EAI_SYSTEM
:
193 errno
:= syscall
.GetErrno()
195 // err should not be nil, but sometimes getaddrinfo returns
196 // gerrno == C.EAI_SYSTEM with err == nil on Linux.
197 // The report claims that it happens when we have too many
198 // open files, so use syscall.EMFILE (too many open files in system).
199 // Most system calls would return ENFILE (too many open files),
200 // so at the least EMFILE should be easy to recognize if this
201 // comes up again. golang.org/issue/6232.
202 errno
= syscall
.EMFILE
205 case syscall
.EAI_NONAME
:
207 isErrorNoSuchHost
= true
209 err
= addrinfoErrno(gerrno
)
210 isTemporary
= addrinfoErrno(gerrno
).Temporary()
213 return nil, "", &DNSError
{Err
: err
.Error(), Name
: name
, IsNotFound
: isErrorNoSuchHost
, IsTemporary
: isTemporary
}
215 defer libc_freeaddrinfo(res
)
218 cname
= bytePtrToString((*byte)(unsafe
.Pointer(res
.Ai_canonname
)))
222 if len(cname
) > 0 && cname
[len(cname
)-1] != '.' {
226 for r
:= res
; r
!= nil; r
= r
.Ai_next
{
227 // We only asked for SOCK_STREAM, but check anyhow.
228 if r
.Ai_socktype
!= syscall
.SOCK_STREAM
{
232 case syscall
.AF_INET
:
233 sa
:= (*syscall
.RawSockaddrInet4
)(unsafe
.Pointer(r
.Ai_addr
))
234 addr
:= IPAddr
{IP
: copyIP(sa
.Addr
[:])}
235 addrs
= append(addrs
, addr
)
236 case syscall
.AF_INET6
:
237 sa
:= (*syscall
.RawSockaddrInet6
)(unsafe
.Pointer(r
.Ai_addr
))
238 addr
:= IPAddr
{IP
: copyIP(sa
.Addr
[:]), Zone
: zoneCache
.name(int(sa
.Scope_id
))}
239 addrs
= append(addrs
, addr
)
242 return addrs
, cname
, nil
245 func cgoIPLookup(result
chan<- ipLookupResult
, network
, name
string) {
246 addrs
, cname
, err
:= cgoLookupIPCNAME(network
, name
)
247 result
<- ipLookupResult
{addrs
, cname
, err
}
250 func cgoLookupIP(ctx context
.Context
, network
, name
string) (addrs
[]IPAddr
, err error
, completed
bool) {
251 if ctx
.Done() == nil {
252 addrs
, _
, err
= cgoLookupIPCNAME(network
, name
)
253 return addrs
, err
, true
255 result
:= make(chan ipLookupResult
, 1)
256 go cgoIPLookup(result
, network
, name
)
259 return r
.addrs
, r
.err
, true
261 return nil, mapErr(ctx
.Err()), false
265 func cgoLookupCNAME(ctx context
.Context
, name
string) (cname
string, err error
, completed
bool) {
266 if ctx
.Done() == nil {
267 _
, cname
, err
= cgoLookupIPCNAME("ip", name
)
268 return cname
, err
, true
270 result
:= make(chan ipLookupResult
, 1)
271 go cgoIPLookup(result
, "ip", name
)
274 return r
.cname
, r
.err
, true
276 return "", mapErr(ctx
.Err()), false
280 // These are roughly enough for the following:
282 // Source Encoding Maximum length of single name entry
283 // Unicast DNS ASCII or <=253 + a NUL terminator
284 // Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
285 // Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
286 // the same as unicast DNS ASCII <=253 + a NUL terminator
287 // Local database various depends on implementation
290 maxNameinfoLen
= 4096
293 func cgoLookupPTR(ctx context
.Context
, addr
string) (names
[]string, err error
, completed
bool) {
295 ip
:= parseIPv4(addr
)
297 ip
, zone
= parseIPv6Zone(addr
)
300 return nil, &DNSError
{Err
: "invalid address", Name
: addr
}, true
302 sa
, salen
:= cgoSockaddr(ip
, zone
)
304 return nil, &DNSError
{Err
: "invalid address " + ip
.String(), Name
: addr
}, true
306 if ctx
.Done() == nil {
307 names
, err
:= cgoLookupAddrPTR(addr
, sa
, salen
)
308 return names
, err
, true
310 result
:= make(chan reverseLookupResult
, 1)
311 go cgoReverseLookup(result
, addr
, sa
, salen
)
314 return r
.names
, r
.err
, true
316 return nil, mapErr(ctx
.Err()), false
320 func cgoLookupAddrPTR(addr
string, sa
*syscall
.RawSockaddr
, salen syscall
.Socklen_t
) (names
[]string, err error
) {
322 defer releaseThread()
326 for l
:= nameinfoLen
; l
<= maxNameinfoLen
; l
*= 2 {
328 gerrno
, err
= cgoNameinfoPTR(b
, sa
, salen
)
329 if gerrno
== 0 || gerrno
!= syscall
.EAI_OVERFLOW
{
336 case syscall
.EAI_SYSTEM
:
337 if err
== nil { // see golang.org/issue/6232
341 err
= addrinfoErrno(gerrno
)
342 isTemporary
= addrinfoErrno(gerrno
).Temporary()
344 return nil, &DNSError
{Err
: err
.Error(), Name
: addr
, IsTemporary
: isTemporary
}
346 for i
:= 0; i
< len(b
); i
++ {
352 return []string{absDomainName(string(b
))}, nil
355 func cgoReverseLookup(result
chan<- reverseLookupResult
, addr
string, sa
*syscall
.RawSockaddr
, salen syscall
.Socklen_t
) {
356 names
, err
:= cgoLookupAddrPTR(addr
, sa
, salen
)
357 result
<- reverseLookupResult
{names
, err
}
360 func cgoSockaddr(ip IP
, zone
string) (*syscall
.RawSockaddr
, syscall
.Socklen_t
) {
361 if ip4
:= ip
.To4(); ip4
!= nil {
362 return cgoSockaddrInet4(ip4
), syscall
.Socklen_t(syscall
.SizeofSockaddrInet4
)
364 if ip6
:= ip
.To16(); ip6
!= nil {
365 return cgoSockaddrInet6(ip6
, zoneCache
.index(zone
)), syscall
.Socklen_t(syscall
.SizeofSockaddrInet6
)
370 func copyIP(x IP
) IP
{
374 y
:= make(IP
, len(x
))