github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/net/lookup_windows.go (about) 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. 4 5 package net 6 7 import ( 8 "context" 9 "internal/syscall/windows" 10 "os" 11 "runtime" 12 "syscall" 13 "unsafe" 14 ) 15 16 const _WSAHOST_NOT_FOUND = syscall.Errno(11001) 17 18 func winError(call string, err error) error { 19 switch err { 20 case _WSAHOST_NOT_FOUND: 21 return errNoSuchHost 22 } 23 return os.NewSyscallError(call, err) 24 } 25 26 func getprotobyname(name string) (proto int, err error) { 27 p, err := syscall.GetProtoByName(name) 28 if err != nil { 29 return 0, winError("getprotobyname", err) 30 } 31 return int(p.Proto), nil 32 } 33 34 // lookupProtocol looks up IP protocol name and returns correspondent protocol number. 35 func lookupProtocol(ctx context.Context, name string) (int, error) { 36 // GetProtoByName return value is stored in thread local storage. 37 // Start new os thread before the call to prevent races. 38 type result struct { 39 proto int 40 err error 41 } 42 ch := make(chan result) // unbuffered 43 go func() { 44 acquireThread() 45 defer releaseThread() 46 runtime.LockOSThread() 47 defer runtime.UnlockOSThread() 48 proto, err := getprotobyname(name) 49 select { 50 case ch <- result{proto: proto, err: err}: 51 case <-ctx.Done(): 52 } 53 }() 54 select { 55 case r := <-ch: 56 if r.err != nil { 57 if proto, err := lookupProtocolMap(name); err == nil { 58 return proto, nil 59 } 60 61 dnsError := &DNSError{Err: r.err.Error(), Name: name} 62 if r.err == errNoSuchHost { 63 dnsError.IsNotFound = true 64 } 65 r.err = dnsError 66 } 67 return r.proto, r.err 68 case <-ctx.Done(): 69 return 0, mapErr(ctx.Err()) 70 } 71 } 72 73 func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) { 74 ips, err := r.lookupIP(ctx, "ip", name) 75 if err != nil { 76 return nil, err 77 } 78 addrs := make([]string, 0, len(ips)) 79 for _, ip := range ips { 80 addrs = append(addrs, ip.String()) 81 } 82 return addrs, nil 83 } 84 85 // preferGoOverWindows reports whether the resolver should use the 86 // pure Go implementation rather than making win32 calls to ask the 87 // kernel for its answer. 88 func (r *Resolver) preferGoOverWindows() bool { 89 conf := systemConf() 90 order := conf.hostLookupOrder(r, "") // name is unused 91 return order != hostLookupCgo 92 } 93 94 func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) { 95 if r.preferGoOverWindows() { 96 return r.goLookupIP(ctx, network, name) 97 } 98 // TODO(bradfitz,brainman): use ctx more. See TODO below. 99 100 var family int32 = syscall.AF_UNSPEC 101 switch ipVersion(network) { 102 case '4': 103 family = syscall.AF_INET 104 case '6': 105 family = syscall.AF_INET6 106 } 107 108 getaddr := func() ([]IPAddr, error) { 109 acquireThread() 110 defer releaseThread() 111 hints := syscall.AddrinfoW{ 112 Family: family, 113 Socktype: syscall.SOCK_STREAM, 114 Protocol: syscall.IPPROTO_IP, 115 } 116 var result *syscall.AddrinfoW 117 name16p, err := syscall.UTF16PtrFromString(name) 118 if err != nil { 119 return nil, &DNSError{Name: name, Err: err.Error()} 120 } 121 e := syscall.GetAddrInfoW(name16p, nil, &hints, &result) 122 if e != nil { 123 err := winError("getaddrinfow", e) 124 dnsError := &DNSError{Err: err.Error(), Name: name} 125 if err == errNoSuchHost { 126 dnsError.IsNotFound = true 127 } 128 return nil, dnsError 129 } 130 defer syscall.FreeAddrInfoW(result) 131 addrs := make([]IPAddr, 0, 5) 132 for ; result != nil; result = result.Next { 133 addr := unsafe.Pointer(result.Addr) 134 switch result.Family { 135 case syscall.AF_INET: 136 a := (*syscall.RawSockaddrInet4)(addr).Addr 137 addrs = append(addrs, IPAddr{IP: IPv4(a[0], a[1], a[2], a[3])}) 138 case syscall.AF_INET6: 139 a := (*syscall.RawSockaddrInet6)(addr).Addr 140 zone := zoneCache.name(int((*syscall.RawSockaddrInet6)(addr).Scope_id)) 141 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}) 142 default: 143 return nil, &DNSError{Err: syscall.EWINDOWS.Error(), Name: name} 144 } 145 } 146 return addrs, nil 147 } 148 149 type ret struct { 150 addrs []IPAddr 151 err error 152 } 153 154 var ch chan ret 155 if ctx.Err() == nil { 156 ch = make(chan ret, 1) 157 go func() { 158 addr, err := getaddr() 159 ch <- ret{addrs: addr, err: err} 160 }() 161 } 162 163 select { 164 case r := <-ch: 165 return r.addrs, r.err 166 case <-ctx.Done(): 167 // TODO(bradfitz,brainman): cancel the ongoing 168 // GetAddrInfoW? It would require conditionally using 169 // GetAddrInfoEx with lpOverlapped, which requires 170 // Windows 8 or newer. I guess we'll need oldLookupIP, 171 // newLookupIP, and newerLookUP. 172 // 173 // For now we just let it finish and write to the 174 // buffered channel. 175 return nil, &DNSError{ 176 Name: name, 177 Err: ctx.Err().Error(), 178 IsTimeout: ctx.Err() == context.DeadlineExceeded, 179 } 180 } 181 } 182 183 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) { 184 if r.preferGoOverWindows() { 185 return lookupPortMap(network, service) 186 } 187 188 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 189 acquireThread() 190 defer releaseThread() 191 var stype int32 192 switch network { 193 case "tcp4", "tcp6": 194 stype = syscall.SOCK_STREAM 195 case "udp4", "udp6": 196 stype = syscall.SOCK_DGRAM 197 } 198 hints := syscall.AddrinfoW{ 199 Family: syscall.AF_UNSPEC, 200 Socktype: stype, 201 Protocol: syscall.IPPROTO_IP, 202 } 203 var result *syscall.AddrinfoW 204 e := syscall.GetAddrInfoW(nil, syscall.StringToUTF16Ptr(service), &hints, &result) 205 if e != nil { 206 if port, err := lookupPortMap(network, service); err == nil { 207 return port, nil 208 } 209 err := winError("getaddrinfow", e) 210 dnsError := &DNSError{Err: err.Error(), Name: network + "/" + service} 211 if err == errNoSuchHost { 212 dnsError.IsNotFound = true 213 } 214 return 0, dnsError 215 } 216 defer syscall.FreeAddrInfoW(result) 217 if result == nil { 218 return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service} 219 } 220 addr := unsafe.Pointer(result.Addr) 221 switch result.Family { 222 case syscall.AF_INET: 223 a := (*syscall.RawSockaddrInet4)(addr) 224 return int(syscall.Ntohs(a.Port)), nil 225 case syscall.AF_INET6: 226 a := (*syscall.RawSockaddrInet6)(addr) 227 return int(syscall.Ntohs(a.Port)), nil 228 } 229 return 0, &DNSError{Err: syscall.EINVAL.Error(), Name: network + "/" + service} 230 } 231 232 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) { 233 if r.preferGoOverWindows() { 234 return r.goLookupCNAME(ctx, name) 235 } 236 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 237 acquireThread() 238 defer releaseThread() 239 var rec *syscall.DNSRecord 240 e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil) 241 // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s 242 if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS { 243 // if there are no aliases, the canonical name is the input name 244 return absDomainName(name), nil 245 } 246 if e != nil { 247 return "", &DNSError{Err: winError("dnsquery", e).Error(), Name: name} 248 } 249 defer syscall.DnsRecordListFree(rec, 1) 250 251 resolved := resolveCNAME(syscall.StringToUTF16Ptr(name), rec) 252 cname := windows.UTF16PtrToString(resolved) 253 return absDomainName(cname), nil 254 } 255 256 func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) { 257 if r.preferGoOverWindows() { 258 return r.goLookupSRV(ctx, service, proto, name) 259 } 260 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 261 acquireThread() 262 defer releaseThread() 263 var target string 264 if service == "" && proto == "" { 265 target = name 266 } else { 267 target = "_" + service + "._" + proto + "." + name 268 } 269 var rec *syscall.DNSRecord 270 e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &rec, nil) 271 if e != nil { 272 return "", nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: target} 273 } 274 defer syscall.DnsRecordListFree(rec, 1) 275 276 srvs := make([]*SRV, 0, 10) 277 for _, p := range validRecs(rec, syscall.DNS_TYPE_SRV, target) { 278 v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0])) 279 srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight}) 280 } 281 byPriorityWeight(srvs).sort() 282 return absDomainName(target), srvs, nil 283 } 284 285 func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) { 286 if r.preferGoOverWindows() { 287 return r.goLookupMX(ctx, name) 288 } 289 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 290 acquireThread() 291 defer releaseThread() 292 var rec *syscall.DNSRecord 293 e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil) 294 if e != nil { 295 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} 296 } 297 defer syscall.DnsRecordListFree(rec, 1) 298 299 mxs := make([]*MX, 0, 10) 300 for _, p := range validRecs(rec, syscall.DNS_TYPE_MX, name) { 301 v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0])) 302 mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference}) 303 } 304 byPref(mxs).sort() 305 return mxs, nil 306 } 307 308 func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) { 309 if r.preferGoOverWindows() { 310 return r.goLookupNS(ctx, name) 311 } 312 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 313 acquireThread() 314 defer releaseThread() 315 var rec *syscall.DNSRecord 316 e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil) 317 if e != nil { 318 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} 319 } 320 defer syscall.DnsRecordListFree(rec, 1) 321 322 nss := make([]*NS, 0, 10) 323 for _, p := range validRecs(rec, syscall.DNS_TYPE_NS, name) { 324 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) 325 nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))}) 326 } 327 return nss, nil 328 } 329 330 func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) { 331 if r.preferGoOverWindows() { 332 return r.goLookupTXT(ctx, name) 333 } 334 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 335 acquireThread() 336 defer releaseThread() 337 var rec *syscall.DNSRecord 338 e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil) 339 if e != nil { 340 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: name} 341 } 342 defer syscall.DnsRecordListFree(rec, 1) 343 344 txts := make([]string, 0, 10) 345 for _, p := range validRecs(rec, syscall.DNS_TYPE_TEXT, name) { 346 d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0])) 347 s := "" 348 for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] { 349 s += windows.UTF16PtrToString(v) 350 } 351 txts = append(txts, s) 352 } 353 return txts, nil 354 } 355 356 func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) { 357 if r.preferGoOverWindows() { 358 return r.goLookupPTR(ctx, addr) 359 } 360 361 // TODO(bradfitz): finish ctx plumbing. Nothing currently depends on this. 362 acquireThread() 363 defer releaseThread() 364 arpa, err := reverseaddr(addr) 365 if err != nil { 366 return nil, err 367 } 368 var rec *syscall.DNSRecord 369 e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &rec, nil) 370 if e != nil { 371 return nil, &DNSError{Err: winError("dnsquery", e).Error(), Name: addr} 372 } 373 defer syscall.DnsRecordListFree(rec, 1) 374 375 ptrs := make([]string, 0, 10) 376 for _, p := range validRecs(rec, syscall.DNS_TYPE_PTR, arpa) { 377 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])) 378 ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host))) 379 } 380 return ptrs, nil 381 } 382 383 const dnsSectionMask = 0x0003 384 385 // returns only results applicable to name and resolves CNAME entries 386 func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord { 387 cname := syscall.StringToUTF16Ptr(name) 388 if dnstype != syscall.DNS_TYPE_CNAME { 389 cname = resolveCNAME(cname, r) 390 } 391 rec := make([]*syscall.DNSRecord, 0, 10) 392 for p := r; p != nil; p = p.Next { 393 // in case of a local machine, DNS records are returned with DNSREC_QUESTION flag instead of DNS_ANSWER 394 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer && p.Dw&dnsSectionMask != syscall.DnsSectionQuestion { 395 continue 396 } 397 if p.Type != dnstype { 398 continue 399 } 400 if !syscall.DnsNameCompare(cname, p.Name) { 401 continue 402 } 403 rec = append(rec, p) 404 } 405 return rec 406 } 407 408 // returns the last CNAME in chain 409 func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 { 410 // limit cname resolving to 10 in case of an infinite CNAME loop 411 Cname: 412 for cnameloop := 0; cnameloop < 10; cnameloop++ { 413 for p := r; p != nil; p = p.Next { 414 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer { 415 continue 416 } 417 if p.Type != syscall.DNS_TYPE_CNAME { 418 continue 419 } 420 if !syscall.DnsNameCompare(name, p.Name) { 421 continue 422 } 423 name = (*syscall.DNSPTRData)(unsafe.Pointer(&r.Data[0])).Host 424 continue Cname 425 } 426 break 427 } 428 return name 429 } 430 431 // concurrentThreadsLimit returns the number of threads we permit to 432 // run concurrently doing DNS lookups. 433 func concurrentThreadsLimit() int { 434 return 500 435 }