1 // Copyright 2009 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.
15 const _WSAHOST_NOT_FOUND
= syscall
.Errno(11001)
17 func winError(call
string, err error
) error
{
19 case _WSAHOST_NOT_FOUND
:
22 return os
.NewSyscallError(call
, err
)
25 func getprotobyname(name
string) (proto
int, err error
) {
26 p
, err
:= syscall
.GetProtoByName(name
)
28 return 0, winError("getprotobyname", err
)
30 return int(p
.Proto
), nil
33 // lookupProtocol looks up IP protocol name and returns correspondent protocol number.
34 func lookupProtocol(ctx context
.Context
, name
string) (int, error
) {
35 // GetProtoByName return value is stored in thread local storage.
36 // Start new os thread before the call to prevent races.
41 ch
:= make(chan result
) // unbuffered
45 runtime
.LockOSThread()
46 defer runtime
.UnlockOSThread()
47 proto
, err
:= getprotobyname(name
)
49 case ch
<- result
{proto
: proto
, err
: err
}:
56 if proto
, err
:= lookupProtocolMap(name
); err
== nil {
59 r
.err
= &DNSError
{Err
: r
.err
.Error(), Name
: name
}
63 return 0, mapErr(ctx
.Err())
67 func (r
*Resolver
) lookupHost(ctx context
.Context
, name
string) ([]string, error
) {
68 ips
, err
:= r
.lookupIP(ctx
, name
)
72 addrs
:= make([]string, 0, len(ips
))
73 for _
, ip
:= range ips
{
74 addrs
= append(addrs
, ip
.String())
79 func (r
*Resolver
) lookupIP(ctx context
.Context
, name
string) ([]IPAddr
, error
) {
80 // TODO(bradfitz,brainman): use ctx more. See TODO below.
86 ch
:= make(chan ret
, 1)
90 hints
:= syscall
.AddrinfoW
{
91 Family
: syscall
.AF_UNSPEC
,
92 Socktype
: syscall
.SOCK_STREAM
,
93 Protocol
: syscall
.IPPROTO_IP
,
95 var result
*syscall
.AddrinfoW
96 e
:= syscall
.GetAddrInfoW(syscall
.StringToUTF16Ptr(name
), nil, &hints
, &result
)
98 ch
<- ret
{err
: &DNSError
{Err
: winError("getaddrinfow", e
).Error(), Name
: name
}}
100 defer syscall
.FreeAddrInfoW(result
)
101 addrs
:= make([]IPAddr
, 0, 5)
102 for ; result
!= nil; result
= result
.Next
{
103 addr
:= unsafe
.Pointer(result
.Addr
)
104 switch result
.Family
{
105 case syscall
.AF_INET
:
106 a
:= (*syscall
.RawSockaddrInet4
)(addr
).Addr
107 addrs
= append(addrs
, IPAddr
{IP
: IPv4(a
[0], a
[1], a
[2], a
[3])})
108 case syscall
.AF_INET6
:
109 a
:= (*syscall
.RawSockaddrInet6
)(addr
).Addr
110 zone
:= zoneCache
.name(int((*syscall
.RawSockaddrInet6
)(addr
).Scope_id
))
111 addrs
= append(addrs
, IPAddr
{IP
: IP
{a
[0], a
[1], a
[2], a
[3], a
[4], a
[5], a
[6], a
[7], a
[8], a
[9], a
[10], a
[11], a
[12], a
[13], a
[14], a
[15]}, Zone
: zone
})
113 ch
<- ret
{err
: &DNSError
{Err
: syscall
.EWINDOWS
.Error(), Name
: name
}}
116 ch
<- ret
{addrs
: addrs
}
120 return r
.addrs
, r
.err
122 // TODO(bradfitz,brainman): cancel the ongoing
123 // GetAddrInfoW? It would require conditionally using
124 // GetAddrInfoEx with lpOverlapped, which requires
125 // Windows 8 or newer. I guess we'll need oldLookupIP,
126 // newLookupIP, and newerLookUP.
128 // For now we just let it finish and write to the
130 return nil, &DNSError
{
132 Err
: ctx
.Err().Error(),
133 IsTimeout
: ctx
.Err() == context
.DeadlineExceeded
,
138 func (r
*Resolver
) lookupPort(ctx context
.Context
, network
, service
string) (int, error
) {
140 return lookupPortMap(network
, service
)
143 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
145 defer releaseThread()
149 stype
= syscall
.SOCK_STREAM
151 stype
= syscall
.SOCK_DGRAM
153 hints
:= syscall
.AddrinfoW
{
154 Family
: syscall
.AF_UNSPEC
,
156 Protocol
: syscall
.IPPROTO_IP
,
158 var result
*syscall
.AddrinfoW
159 e
:= syscall
.GetAddrInfoW(nil, syscall
.StringToUTF16Ptr(service
), &hints
, &result
)
161 if port
, err
:= lookupPortMap(network
, service
); err
== nil {
164 return 0, &DNSError
{Err
: winError("getaddrinfow", e
).Error(), Name
: network
+ "/" + service
}
166 defer syscall
.FreeAddrInfoW(result
)
168 return 0, &DNSError
{Err
: syscall
.EINVAL
.Error(), Name
: network
+ "/" + service
}
170 addr
:= unsafe
.Pointer(result
.Addr
)
171 switch result
.Family
{
172 case syscall
.AF_INET
:
173 a
:= (*syscall
.RawSockaddrInet4
)(addr
)
174 return int(syscall
.Ntohs(a
.Port
)), nil
175 case syscall
.AF_INET6
:
176 a
:= (*syscall
.RawSockaddrInet6
)(addr
)
177 return int(syscall
.Ntohs(a
.Port
)), nil
179 return 0, &DNSError
{Err
: syscall
.EINVAL
.Error(), Name
: network
+ "/" + service
}
182 func (*Resolver
) lookupCNAME(ctx context
.Context
, name
string) (string, error
) {
183 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
185 defer releaseThread()
186 var r
*syscall
.DNSRecord
187 e
:= syscall
.DnsQuery(name
, syscall
.DNS_TYPE_CNAME
, 0, nil, &r
, nil)
188 // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s
189 if errno
, ok
:= e
.(syscall
.Errno
); ok
&& errno
== syscall
.DNS_INFO_NO_RECORDS
{
190 // if there are no aliases, the canonical name is the input name
191 return absDomainName([]byte(name
)), nil
194 return "", &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: name
}
196 defer syscall
.DnsRecordListFree(r
, 1)
198 resolved
:= resolveCNAME(syscall
.StringToUTF16Ptr(name
), r
)
199 cname
:= syscall
.UTF16ToString((*[256]uint16)(unsafe
.Pointer(resolved
))[:])
200 return absDomainName([]byte(cname
)), nil
203 func (*Resolver
) lookupSRV(ctx context
.Context
, service
, proto
, name
string) (string, []*SRV
, error
) {
204 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
206 defer releaseThread()
208 if service
== "" && proto
== "" {
211 target
= "_" + service
+ "._" + proto
+ "." + name
213 var r
*syscall
.DNSRecord
214 e
:= syscall
.DnsQuery(target
, syscall
.DNS_TYPE_SRV
, 0, nil, &r
, nil)
216 return "", nil, &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: target
}
218 defer syscall
.DnsRecordListFree(r
, 1)
220 srvs
:= make([]*SRV
, 0, 10)
221 for _
, p
:= range validRecs(r
, syscall
.DNS_TYPE_SRV
, target
) {
222 v
:= (*syscall
.DNSSRVData
)(unsafe
.Pointer(&p
.Data
[0]))
223 srvs
= append(srvs
, &SRV
{absDomainName([]byte(syscall
.UTF16ToString((*[256]uint16)(unsafe
.Pointer(v
.Target
))[:]))), v
.Port
, v
.Priority
, v
.Weight
})
225 byPriorityWeight(srvs
).sort()
226 return absDomainName([]byte(target
)), srvs
, nil
229 func (*Resolver
) lookupMX(ctx context
.Context
, name
string) ([]*MX
, error
) {
230 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
232 defer releaseThread()
233 var r
*syscall
.DNSRecord
234 e
:= syscall
.DnsQuery(name
, syscall
.DNS_TYPE_MX
, 0, nil, &r
, nil)
236 return nil, &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: name
}
238 defer syscall
.DnsRecordListFree(r
, 1)
240 mxs
:= make([]*MX
, 0, 10)
241 for _
, p
:= range validRecs(r
, syscall
.DNS_TYPE_MX
, name
) {
242 v
:= (*syscall
.DNSMXData
)(unsafe
.Pointer(&p
.Data
[0]))
243 mxs
= append(mxs
, &MX
{absDomainName([]byte(syscall
.UTF16ToString((*[256]uint16)(unsafe
.Pointer(v
.NameExchange
))[:]))), v
.Preference
})
249 func (*Resolver
) lookupNS(ctx context
.Context
, name
string) ([]*NS
, error
) {
250 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
252 defer releaseThread()
253 var r
*syscall
.DNSRecord
254 e
:= syscall
.DnsQuery(name
, syscall
.DNS_TYPE_NS
, 0, nil, &r
, nil)
256 return nil, &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: name
}
258 defer syscall
.DnsRecordListFree(r
, 1)
260 nss
:= make([]*NS
, 0, 10)
261 for _
, p
:= range validRecs(r
, syscall
.DNS_TYPE_NS
, name
) {
262 v
:= (*syscall
.DNSPTRData
)(unsafe
.Pointer(&p
.Data
[0]))
263 nss
= append(nss
, &NS
{absDomainName([]byte(syscall
.UTF16ToString((*[256]uint16)(unsafe
.Pointer(v
.Host
))[:])))})
268 func (*Resolver
) lookupTXT(ctx context
.Context
, name
string) ([]string, error
) {
269 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
271 defer releaseThread()
272 var r
*syscall
.DNSRecord
273 e
:= syscall
.DnsQuery(name
, syscall
.DNS_TYPE_TEXT
, 0, nil, &r
, nil)
275 return nil, &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: name
}
277 defer syscall
.DnsRecordListFree(r
, 1)
279 txts
:= make([]string, 0, 10)
280 for _
, p
:= range validRecs(r
, syscall
.DNS_TYPE_TEXT
, name
) {
281 d
:= (*syscall
.DNSTXTData
)(unsafe
.Pointer(&p
.Data
[0]))
282 for _
, v
:= range (*[1 << 10]*uint16)(unsafe
.Pointer(&(d
.StringArray
[0])))[:d
.StringCount
] {
283 s
:= syscall
.UTF16ToString((*[1 << 20]uint16)(unsafe
.Pointer(v
))[:])
284 txts
= append(txts
, s
)
290 func (*Resolver
) lookupAddr(ctx context
.Context
, addr
string) ([]string, error
) {
291 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this.
293 defer releaseThread()
294 arpa
, err
:= reverseaddr(addr
)
298 var r
*syscall
.DNSRecord
299 e
:= syscall
.DnsQuery(arpa
, syscall
.DNS_TYPE_PTR
, 0, nil, &r
, nil)
301 return nil, &DNSError
{Err
: winError("dnsquery", e
).Error(), Name
: addr
}
303 defer syscall
.DnsRecordListFree(r
, 1)
305 ptrs
:= make([]string, 0, 10)
306 for _
, p
:= range validRecs(r
, syscall
.DNS_TYPE_PTR
, arpa
) {
307 v
:= (*syscall
.DNSPTRData
)(unsafe
.Pointer(&p
.Data
[0]))
308 ptrs
= append(ptrs
, absDomainName([]byte(syscall
.UTF16ToString((*[256]uint16)(unsafe
.Pointer(v
.Host
))[:]))))
313 const dnsSectionMask
= 0x0003
315 // returns only results applicable to name and resolves CNAME entries
316 func validRecs(r
*syscall
.DNSRecord
, dnstype
uint16, name
string) []*syscall
.DNSRecord
{
317 cname
:= syscall
.StringToUTF16Ptr(name
)
318 if dnstype
!= syscall
.DNS_TYPE_CNAME
{
319 cname
= resolveCNAME(cname
, r
)
321 rec
:= make([]*syscall
.DNSRecord
, 0, 10)
322 for p
:= r
; p
!= nil; p
= p
.Next
{
323 if p
.Dw
&dnsSectionMask
!= syscall
.DnsSectionAnswer
{
326 if p
.Type
!= dnstype
{
329 if !syscall
.DnsNameCompare(cname
, p
.Name
) {
337 // returns the last CNAME in chain
338 func resolveCNAME(name
*uint16, r
*syscall
.DNSRecord
) *uint16 {
339 // limit cname resolving to 10 in case of a infinite CNAME loop
341 for cnameloop
:= 0; cnameloop
< 10; cnameloop
++ {
342 for p
:= r
; p
!= nil; p
= p
.Next
{
343 if p
.Dw
&dnsSectionMask
!= syscall
.DnsSectionAnswer
{
346 if p
.Type
!= syscall
.DNS_TYPE_CNAME
{
349 if !syscall
.DnsNameCompare(name
, p
.Name
) {
352 name
= (*syscall
.DNSPTRData
)(unsafe
.Pointer(&r
.Data
[0])).Host