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