Source file
src/net/lookup_windows.go
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "internal/syscall/windows"
10 "os"
11 "runtime"
12 "syscall"
13 "time"
14 "unsafe"
15 )
16
17
18
19
20 const cgoAvailable = true
21
22 const (
23 _WSAHOST_NOT_FOUND = syscall.Errno(11001)
24 _WSATRY_AGAIN = syscall.Errno(11002)
25 )
26
27 func winError(call string, err error) error {
28 switch err {
29 case _WSAHOST_NOT_FOUND:
30 return errNoSuchHost
31 }
32 return os.NewSyscallError(call, err)
33 }
34
35 func getprotobyname(name string) (proto int, err error) {
36 p, err := syscall.GetProtoByName(name)
37 if err != nil {
38 return 0, winError("getprotobyname", err)
39 }
40 return int(p.Proto), nil
41 }
42
43
44 func lookupProtocol(ctx context.Context, name string) (int, error) {
45
46
47 type result struct {
48 proto int
49 err error
50 }
51 ch := make(chan result)
52 go func() {
53 acquireThread()
54 defer releaseThread()
55 runtime.LockOSThread()
56 defer runtime.UnlockOSThread()
57 proto, err := getprotobyname(name)
58 select {
59 case ch <- result{proto: proto, err: err}:
60 case <-ctx.Done():
61 }
62 }()
63 select {
64 case r := <-ch:
65 if r.err != nil {
66 if proto, err := lookupProtocolMap(name); err == nil {
67 return proto, nil
68 }
69
70 dnsError := &DNSError{Err: r.err.Error(), Name: name}
71 if r.err == errNoSuchHost {
72 dnsError.IsNotFound = true
73 }
74 r.err = dnsError
75 }
76 return r.proto, r.err
77 case <-ctx.Done():
78 return 0, mapErr(ctx.Err())
79 }
80 }
81
82 func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {
83 ips, err := r.lookupIP(ctx, "ip", name)
84 if err != nil {
85 return nil, err
86 }
87 addrs := make([]string, 0, len(ips))
88 for _, ip := range ips {
89 addrs = append(addrs, ip.String())
90 }
91 return addrs, nil
92 }
93
94
95
96
97 func (r *Resolver) preferGoOverWindows() bool {
98 conf := systemConf()
99 order, _ := conf.hostLookupOrder(r, "")
100 return order != hostLookupCgo
101 }
102
103 func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {
104 if r.preferGoOverWindows() {
105 return r.goLookupIP(ctx, network, name)
106 }
107
108
109 var family int32 = syscall.AF_UNSPEC
110 switch ipVersion(network) {
111 case '4':
112 family = syscall.AF_INET
113 case '6':
114 family = syscall.AF_INET6
115 }
116
117 getaddr := func() ([]IPAddr, error) {
118 acquireThread()
119 defer releaseThread()
120 hints := syscall.AddrinfoW{
121 Family: family,
122 Socktype: syscall.SOCK_STREAM,
123 Protocol: syscall.IPPROTO_IP,
124 }
125 var result *syscall.AddrinfoW
126 name16p, err := syscall.UTF16PtrFromString(name)
127 if err != nil {
128 return nil, &DNSError{Name: name, Err: err.Error()}
129 }
130
131 dnsConf := getSystemDNSConfig()
132 start := time.Now()
133
134 var e error
135 for i := 0; i < dnsConf.attempts; i++ {
136 e = syscall.GetAddrInfoW(name16p, nil, &hints, &result)
137 if e == nil || e != _WSATRY_AGAIN || time.Since(start) > dnsConf.timeout {
138 break
139 }
140 }
141 if e != nil {
142 err := winError("getaddrinfow", e)
143 dnsError := &DNSError{Err: err.Error(), Name: name}
144 if err == errNoSuchHost {
145 dnsError.IsNotFound = true
146 }
147 return nil, dnsError
148 }
149 defer syscall.FreeAddrInfoW(result)
150 addrs := make([]IPAddr, 0, 5)
151 for ; result != nil; result = result.Next {
152 addr := unsafe.Pointer(result.Addr)
153 switch result.Family {
154 case syscall.AF_INET:
155 a := (*syscall.RawSockaddrInet4)(addr).Addr
156 addrs = append(addrs, IPAddr{IP: copyIP(a[:])})
157 case syscall.AF_INET6:
158 a := (*syscall.RawSockaddrInet6)(addr).Addr
159 zone := zoneCache.name(int((*syscall.RawSockaddrInet6)(addr).Scope_id))
160 addrs = append(addrs, IPAddr{IP: copyIP(a[:]), Zone: zone})
161 default:
162 return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name}
163 }
164 }
165 return addrs, nil
166 }
167
168 type ret struct {
169 addrs []IPAddr
170 err error
171 }
172
173 var ch chan ret
174 if ctx.Err() == nil {
175 ch = make(chan ret, 1)
176 go func() {
177 addr, err := getaddr()
178 ch <- ret{addrs: addr, err: err}
179 }()
180 }
181
182 select {
183 case r := <-ch:
184 return r.addrs, r.err
185 case <-ctx.Done():
186
187
188
189
190
191
192
193
194 return nil, &DNSError{
195 Name: name,
196 Err: ctx.Err().Error(),
197 IsTimeout: ctx.Err() == context.DeadlineExceeded,
198 }
199 }
200 }
201
202 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
203 if r.preferGoOverWindows() {
204 return lookupPortMap(network, service)
205 }
206
207
208 acquireThread()
209 defer releaseThread()
210 var stype int32
211 switch network {
212 case "tcp4", "tcp6":
213 stype = syscall.SOCK_STREAM
214 case "udp4", "udp6":
215 stype = syscall.SOCK_DGRAM
216 }
217 hints := syscall.AddrinfoW{
218 Family: syscall.AF_UNSPEC,
219 Socktype: stype,
220 Protocol: syscall.IPPROTO_IP,
221 }
222 var result *syscall.AddrinfoW
223 e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result)
224 if e != nil {
225 if port, err := lookupPortMap(network, service); err == nil {
226 return port, nil
227 }
228 err := winError("getaddrinfow", e)
229 dnsError := &DNSError{Err: err.Error(), Name: network + "/" + service}
230 if err == errNoSuchHost {
231 dnsError.IsNotFound = true
232 }
233 return 0, dnsError
234 }
235 defer syscall.FreeAddrInfoW(result)
236 if result == nil {
237 return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
238 }
239 addr := unsafe.Pointer(result.Addr)
240 switch result.Family {
241 case syscall.AF_INET:
242 a := (*syscall.RawSockaddrInet4)(addr)
243 return int(syscall.Ntohs(a.Port)), nil
244 case syscall.AF_INET6:
245 a := (*syscall.RawSockaddrInet6)(addr)
246 return int(syscall.Ntohs(a.Port)), nil
247 }
248 return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service}
249 }
250
251 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
252 if order, conf := systemConf().hostLookupOrder(r, ""); order != hostLookupCgo {
253 return r.goLookupCNAME(ctx, name, order, conf)
254 }
255
256
257 acquireThread()
258 defer releaseThread()
259 var rec *syscall.DNSRecord
260 e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil)
261
262 if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {
263
264 return absDomainName(name), nil
265 }
266 if e != nil {
267 return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
268 }
269 defer syscall.DnsRecordListFree(rec, 1)
270
271 resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), rec)
272 cname := windows.UTF16PtrToString(resolved)
273 return absDomainName(cname), nil
274 }
275
276 func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
277 if r.preferGoOverWindows() {
278 return r.goLookupSRV(ctx, service, proto, name)
279 }
280
281 acquireThread()
282 defer releaseThread()
283 var target string
284 if service == "" && proto == "" {
285 target = name
286 } else {
287 target = "_" + service + "._" + proto + "." + name
288 }
289 var rec *syscall.DNSRecord
290 e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &rec, nil)
291 if e != nil {
292 return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target}
293 }
294 defer syscall.DnsRecordListFree(rec, 1)
295
296 srvs := make([]*SRV, 0, 10)
297 for _, p := range validRecs(rec, syscall.DNS_TYPE_SRV, target) {
298 v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))
299 srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight})
300 }
301 byPriorityWeight(srvs).sort()
302 return absDomainName(target), srvs, nil
303 }
304
305 func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
306 if r.preferGoOverWindows() {
307 return r.goLookupMX(ctx, name)
308 }
309
310 acquireThread()
311 defer releaseThread()
312 var rec *syscall.DNSRecord
313 e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil)
314 if e != nil {
315 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
316 }
317 defer syscall.DnsRecordListFree(rec, 1)
318
319 mxs := make([]*MX, 0, 10)
320 for _, p := range validRecs(rec, syscall.DNS_TYPE_MX, name) {
321 v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))
322 mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference})
323 }
324 byPref(mxs).sort()
325 return mxs, nil
326 }
327
328 func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
329 if r.preferGoOverWindows() {
330 return r.goLookupNS(ctx, name)
331 }
332
333 acquireThread()
334 defer releaseThread()
335 var rec *syscall.DNSRecord
336 e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil)
337 if e != nil {
338 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
339 }
340 defer syscall.DnsRecordListFree(rec, 1)
341
342 nss := make([]*NS, 0, 10)
343 for _, p := range validRecs(rec, syscall.DNS_TYPE_NS, name) {
344 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
345 nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))})
346 }
347 return nss, nil
348 }
349
350 func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
351 if r.preferGoOverWindows() {
352 return r.goLookupTXT(ctx, name)
353 }
354
355 acquireThread()
356 defer releaseThread()
357 var rec *syscall.DNSRecord
358 e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil)
359 if e != nil {
360 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name}
361 }
362 defer syscall.DnsRecordListFree(rec, 1)
363
364 txts := make([]string, 0, 10)
365 for _, p := range validRecs(rec, syscall.DNS_TYPE_TEXT, name) {
366 d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))
367 s := ""
368 for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] {
369 s += windows.UTF16PtrToString(v)
370 }
371 txts = append(txts, s)
372 }
373 return txts, nil
374 }
375
376 func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
377 if order, conf := systemConf().hostLookupOrder(r, ""); order != hostLookupCgo {
378 return r.goLookupPTR(ctx, addr, order, conf)
379 }
380
381
382 acquireThread()
383 defer releaseThread()
384 arpa, err := reverseaddr(addr)
385 if err != nil {
386 return nil, err
387 }
388 var rec *syscall.DNSRecord
389 e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &rec, nil)
390 if e != nil {
391 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr}
392 }
393 defer syscall.DnsRecordListFree(rec, 1)
394
395 ptrs := make([]string, 0, 10)
396 for _, p := range validRecs(rec, syscall.DNS_TYPE_PTR, arpa) {
397 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))
398 ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host)))
399 }
400 return ptrs, nil
401 }
402
403 const dnsSectionMask = 0x0003
404
405
406 func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {
407 cname := syscall.StringToUTF16Ptr(name)
408 if dnstype != syscall.DNS_TYPE_CNAME {
409 cname = resolveCNAME(cname, r)
410 }
411 rec := make([]*syscall.DNSRecord, 0, 10)
412 for p := r; p != nil; p = p.Next {
413
414 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer && p.Dw&dnsSectionMask != syscall.DnsSectionQuestion {
415 continue
416 }
417 if p.Type != dnstype {
418 continue
419 }
420 if !syscall.DnsNameCompare(cname, p.Name) {
421 continue
422 }
423 rec = append(rec, p)
424 }
425 return rec
426 }
427
428
429 func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {
430
431 Cname:
432 for cnameloop := 0; cnameloop < 10; cnameloop++ {
433 for p := r; p != nil; p = p.Next {
434 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {
435 continue
436 }
437 if p.Type != syscall.DNS_TYPE_CNAME {
438 continue
439 }
440 if !syscall.DnsNameCompare(name, p.Name) {
441 continue
442 }
443 name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host
444 continue Cname
445 }
446 break
447 }
448 return name
449 }
450
451
452
453 func concurrentThreadsLimit() int {
454 return 500
455 }
456
View as plain text