1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package net67import (8 "context"9 "internal/syscall/windows"10 "os"11 "runtime"12 "syscall"13 "time"14 "unsafe"15)1617// cgoAvailable set to true to indicate that the cgo resolver18// is available on Windows. Note that on Windows the cgo resolver19// does not actually use cgo.20const cgoAvailable = true2122const (23 _DNS_ERROR_RCODE_NAME_ERROR = syscall.Errno(9003)24 _DNS_INFO_NO_RECORDS = syscall.Errno(9501)2526 _WSAHOST_NOT_FOUND = syscall.Errno(11001)27 _WSATRY_AGAIN = syscall.Errno(11002)28 _WSATYPE_NOT_FOUND = syscall.Errno(10109)29)3031func 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 errNoSuchHost35 }36 return os.NewSyscallError(call, err)37}3839func 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), nil45}4647// lookupProtocol looks up IP protocol name and returns correspondent protocol number.48func lookupProtocol(ctx context.Context, name string) (int, error) {49 // GetProtoByName return value is stored in thread local storage.50 // Start new os thread before the call to prevent races.51 type result struct {52 proto int53 err error54 }55 ch := make(chan result, 1) // buffer so that next goroutine never blocks56 go func() {57 if err := acquireThread(ctx); err != nil {58 ch <- result{err: mapErr(err)}59 return60 }61 defer releaseThread()62 runtime.LockOSThread()63 defer runtime.UnlockOSThread()64 proto, err := getprotobyname(name)65 ch <- result{proto: proto, err: err}66 }()67 select {68 case r := <-ch:69 if r.err != nil {70 if proto, err := lookupProtocolMap(name); err == nil {71 return proto, nil72 }73 r.err = newDNSError(r.err, name, "")74 }75 return r.proto, r.err76 case <-ctx.Done():77 return 0, newDNSError(mapErr(ctx.Err()), name, "")78 }79}8081func (r *Resolver) lookupHost(ctx context.Context, name string) ([]string, error) {82 ips, err := r.lookupIP(ctx, "ip", name)83 if err != nil {84 return nil, err85 }86 addrs := make([]string, 0, len(ips))87 for _, ip := range ips {88 addrs = append(addrs, ip.String())89 }90 return addrs, nil91}9293func (r *Resolver) lookupIP(ctx context.Context, network, name string) ([]IPAddr, error) {94 if order, conf := systemConf().hostLookupOrder(r, name); order != hostLookupCgo {95 return r.goLookupIP(ctx, network, name, order, conf)96 }9798 // TODO(bradfitz,brainman): use ctx more. See TODO below.99100 var family int32 = syscall.AF_UNSPEC101 switch ipVersion(network) {102 case '4':103 family = syscall.AF_INET104 case '6':105 family = syscall.AF_INET6106 }107108 getaddr := func() ([]IPAddr, error) {109 if err := acquireThread(ctx); err != nil {110 return nil, newDNSError(mapErr(err), name, "")111 }112 defer releaseThread()113 hints := syscall.AddrinfoW{114 Family: family,115 Socktype: syscall.SOCK_STREAM,116 Protocol: syscall.IPPROTO_IP,117 }118 var result *syscall.AddrinfoW119 name16p, err := syscall.UTF16PtrFromString(name)120 if err != nil {121 return nil, newDNSError(err, name, "")122 }123124 dnsConf := getSystemDNSConfig()125 start := time.Now()126127 var e error128 for i := 0; i < dnsConf.attempts; i++ {129 e = syscall.GetAddrInfoW(name16p, nil, &hints, &result)130 if e == nil || e != _WSATRY_AGAIN || time.Since(start) > dnsConf.timeout {131 break132 }133 }134 if e != nil {135 return nil, newDNSError(winError("getaddrinfow", e), name, "")136 }137 defer syscall.FreeAddrInfoW(result)138 addrs := make([]IPAddr, 0, 5)139 for ; result != nil; result = result.Next {140 addr := unsafe.Pointer(result.Addr)141 switch result.Family {142 case syscall.AF_INET:143 a := (*syscall.RawSockaddrInet4)(addr).Addr144 addrs = append(addrs, IPAddr{IP: copyIP(a[:])})145 case syscall.AF_INET6:146 a := (*syscall.RawSockaddrInet6)(addr).Addr147 zone := zoneCache.name(int((*syscall.RawSockaddrInet6)(addr).Scope_id))148 addrs = append(addrs, IPAddr{IP: copyIP(a[:]), Zone: zone})149 default:150 return nil, newDNSError(syscall.EWINDOWS, name, "")151 }152 }153 return addrs, nil154 }155156 type ret struct {157 addrs []IPAddr158 err error159 }160161 var ch chan ret162 if ctx.Err() == nil {163 ch = make(chan ret, 1)164 go func() {165 addr, err := getaddr()166 ch <- ret{addrs: addr, err: err}167 }()168 }169170 select {171 case r := <-ch:172 return r.addrs, r.err173 case <-ctx.Done():174 // TODO(bradfitz,brainman): cancel the ongoing175 // GetAddrInfoW? It would require conditionally using176 // GetAddrInfoEx with lpOverlapped, which requires177 // Windows 8 or newer. I guess we'll need oldLookupIP,178 // newLookupIP, and newerLookUP.179 //180 // For now we just let it finish and write to the181 // buffered channel.182 return nil, newDNSError(mapErr(ctx.Err()), name, "")183 }184}185186func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {187 if systemConf().mustUseGoResolver(r) {188 return lookupPortMap(network, service)189 }190191 // TODO(bradfitz): finish ctx plumbing192 if err := acquireThread(ctx); err != nil {193 return 0, newDNSError(mapErr(err), network+"/"+service, "")194 }195 defer releaseThread()196197 var hints syscall.AddrinfoW198199 switch network {200 case "ip": // no hints201 case "tcp", "tcp4", "tcp6":202 hints.Socktype = syscall.SOCK_STREAM203 hints.Protocol = syscall.IPPROTO_TCP204 case "udp", "udp4", "udp6":205 hints.Socktype = syscall.SOCK_DGRAM206 hints.Protocol = syscall.IPPROTO_UDP207 default:208 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}209 }210211 switch ipVersion(network) {212 case '4':213 hints.Family = syscall.AF_INET214 case '6':215 hints.Family = syscall.AF_INET6216 }217218 servicep, err := syscall.UTF16PtrFromString(service)219 if err != nil {220 return 0, newDNSError(err, network+"/"+service, "")221 }222223 var result *syscall.AddrinfoW224 e := syscall.GetAddrInfoW(nil, servicep, &hints, &result)225 if e != nil {226 if port, err := lookupPortMap(network, service); err == nil {227 return port, nil228 }229230 // The _WSATYPE_NOT_FOUND error is returned by GetAddrInfoW231 // when the service name is unknown. We are also checking232 // for _WSAHOST_NOT_FOUND here to match the cgo (unix) version233 // cgo_unix.go (cgoLookupServicePort).234 if e == _WSATYPE_NOT_FOUND || e == _WSAHOST_NOT_FOUND {235 return 0, newDNSError(errUnknownPort, network+"/"+service, "")236 }237 return 0, newDNSError(winError("getaddrinfow", e), network+"/"+service, "")238 }239 defer syscall.FreeAddrInfoW(result)240 if result == nil {241 return 0, newDNSError(syscall.EINVAL, network+"/"+service, "")242 }243 addr := unsafe.Pointer(result.Addr)244 switch result.Family {245 case syscall.AF_INET:246 a := (*syscall.RawSockaddrInet4)(addr)247 return int(syscall.Ntohs(a.Port)), nil248 case syscall.AF_INET6:249 a := (*syscall.RawSockaddrInet6)(addr)250 return int(syscall.Ntohs(a.Port)), nil251 }252 return 0, newDNSError(syscall.EINVAL, network+"/"+service, "")253}254255func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {256 if order, conf := systemConf().hostLookupOrder(r, name); order != hostLookupCgo {257 return r.goLookupCNAME(ctx, name, order, conf)258 }259260 // TODO(bradfitz): finish ctx plumbing261 if err := acquireThread(ctx); err != nil {262 return "", newDNSError(mapErr(err), name, "")263 }264 defer releaseThread()265 var rec *syscall.DNSRecord266 e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &rec, nil)267 // windows returns DNS_INFO_NO_RECORDS if there are no CNAME-s268 if errno, ok := e.(syscall.Errno); ok && errno == syscall.DNS_INFO_NO_RECORDS {269 // if there are no aliases, the canonical name is the input name270 return absDomainName(name), nil271 }272 if e != nil {273 return "", newDNSError(winError("dnsquery", e), name, "")274 }275 defer syscall.DnsRecordListFree(rec, 1)276277 namep, err := syscall.UTF16PtrFromString(name)278 if err != nil {279 return "", newDNSError(err, name, "")280 }281282 resolved := resolveCNAME(namep, rec)283 cname := windows.UTF16PtrToString(resolved)284 return absDomainName(cname), nil285}286287func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {288 if systemConf().mustUseGoResolver(r) {289 return r.goLookupSRV(ctx, service, proto, name)290 }291 // TODO(bradfitz): finish ctx plumbing292 if err := acquireThread(ctx); err != nil {293 return "", nil, newDNSError(mapErr(err), name, "")294 }295 defer releaseThread()296 var target string297 if service == "" && proto == "" {298 target = name299 } else {300 target = "_" + service + "._" + proto + "." + name301 }302 var rec *syscall.DNSRecord303 e := syscall.DnsQuery(target, syscall.DNS_TYPE_SRV, 0, nil, &rec, nil)304 if e != nil {305 return "", nil, newDNSError(winError("dnsquery", e), name, "")306 }307 defer syscall.DnsRecordListFree(rec, 1)308309 srvs := make([]*SRV, 0, 10)310 for _, p := range validRecs(rec, syscall.DNS_TYPE_SRV, target) {311 v := (*syscall.DNSSRVData)(unsafe.Pointer(&p.Data[0]))312 srvs = append(srvs, &SRV{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Target))[:])), v.Port, v.Priority, v.Weight})313 }314 byPriorityWeight(srvs).sort()315 return absDomainName(target), srvs, nil316}317318func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {319 if systemConf().mustUseGoResolver(r) {320 return r.goLookupMX(ctx, name)321 }322 // TODO(bradfitz): finish ctx plumbing.323 if err := acquireThread(ctx); err != nil {324 return nil, newDNSError(mapErr(err), name, "")325 }326 defer releaseThread()327 var rec *syscall.DNSRecord328 e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &rec, nil)329 if e != nil {330 return nil, newDNSError(winError("dnsquery", e), name, "")331 }332 defer syscall.DnsRecordListFree(rec, 1)333334 mxs := make([]*MX, 0, 10)335 for _, p := range validRecs(rec, syscall.DNS_TYPE_MX, name) {336 v := (*syscall.DNSMXData)(unsafe.Pointer(&p.Data[0]))337 mxs = append(mxs, &MX{absDomainName(windows.UTF16PtrToString(v.NameExchange)), v.Preference})338 }339 byPref(mxs).sort()340 return mxs, nil341}342343func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {344 if systemConf().mustUseGoResolver(r) {345 return r.goLookupNS(ctx, name)346 }347 // TODO(bradfitz): finish ctx plumbing.348 if err := acquireThread(ctx); err != nil {349 return nil, newDNSError(mapErr(err), name, "")350 }351 defer releaseThread()352 var rec *syscall.DNSRecord353 e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &rec, nil)354 if e != nil {355 return nil, newDNSError(winError("dnsquery", e), name, "")356 }357 defer syscall.DnsRecordListFree(rec, 1)358359 nss := make([]*NS, 0, 10)360 for _, p := range validRecs(rec, syscall.DNS_TYPE_NS, name) {361 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))362 nss = append(nss, &NS{absDomainName(syscall.UTF16ToString((*[256]uint16)(unsafe.Pointer(v.Host))[:]))})363 }364 return nss, nil365}366367func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {368 if systemConf().mustUseGoResolver(r) {369 return r.goLookupTXT(ctx, name)370 }371 // TODO(bradfitz): finish ctx plumbing.372 if err := acquireThread(ctx); err != nil {373 return nil, newDNSError(mapErr(err), name, "")374 }375 defer releaseThread()376 var rec *syscall.DNSRecord377 e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &rec, nil)378 if e != nil {379 return nil, newDNSError(winError("dnsquery", e), name, "")380 }381 defer syscall.DnsRecordListFree(rec, 1)382383 txts := make([]string, 0, 10)384 for _, p := range validRecs(rec, syscall.DNS_TYPE_TEXT, name) {385 d := (*syscall.DNSTXTData)(unsafe.Pointer(&p.Data[0]))386 s := ""387 for _, v := range (*[1 << 10]*uint16)(unsafe.Pointer(&(d.StringArray[0])))[:d.StringCount:d.StringCount] {388 s += windows.UTF16PtrToString(v)389 }390 txts = append(txts, s)391 }392 return txts, nil393}394395func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {396 if order, conf := systemConf().addrLookupOrder(r, addr); order != hostLookupCgo {397 return r.goLookupPTR(ctx, addr, order, conf)398 }399400 // TODO(bradfitz): finish ctx plumbing.401 if err := acquireThread(ctx); err != nil {402 return nil, newDNSError(mapErr(err), addr, "")403 }404 defer releaseThread()405 arpa, err := reverseaddr(addr)406 if err != nil {407 return nil, err408 }409 var rec *syscall.DNSRecord410 e := syscall.DnsQuery(arpa, syscall.DNS_TYPE_PTR, 0, nil, &rec, nil)411 if e != nil {412 return nil, newDNSError(winError("dnsquery", e), addr, "")413 }414 defer syscall.DnsRecordListFree(rec, 1)415416 ptrs := make([]string, 0, 10)417 for _, p := range validRecs(rec, syscall.DNS_TYPE_PTR, arpa) {418 v := (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0]))419 ptrs = append(ptrs, absDomainName(windows.UTF16PtrToString(v.Host)))420 }421 return ptrs, nil422}423424const dnsSectionMask = 0x0003425426// returns only results applicable to name and resolves CNAME entries.427func validRecs(r *syscall.DNSRecord, dnstype uint16, name string) []*syscall.DNSRecord {428 cname, err := syscall.UTF16PtrFromString(name)429 if err != nil {430 return nil431 }432 if dnstype != syscall.DNS_TYPE_CNAME {433 cname = resolveCNAME(cname, r)434 }435 rec := make([]*syscall.DNSRecord, 0, 10)436 for p := r; p != nil; p = p.Next {437 // in case of a local machine, DNS records are returned with DNSREC_QUESTION flag instead of DNS_ANSWER438 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer && p.Dw&dnsSectionMask != syscall.DnsSectionQuestion {439 continue440 }441 if p.Type != dnstype {442 continue443 }444 if !syscall.DnsNameCompare(cname, p.Name) {445 continue446 }447 rec = append(rec, p)448 }449 return rec450}451452// returns the last CNAME in chain.453func resolveCNAME(name *uint16, r *syscall.DNSRecord) *uint16 {454 // limit cname resolving to 10 in case of an infinite CNAME loop455Cname:456 for cnameloop := 0; cnameloop < 10; cnameloop++ {457 for p := r; p != nil; p = p.Next {458 if p.Dw&dnsSectionMask != syscall.DnsSectionAnswer {459 continue460 }461 if p.Type != syscall.DNS_TYPE_CNAME {462 continue463 }464 if !syscall.DnsNameCompare(name, p.Name) {465 continue466 }467 name = (*syscall.DNSPTRData)(unsafe.Pointer(&p.Data[0])).Host468 continue Cname469 }470 break471 }472 return name473}474475// concurrentThreadsLimit returns the number of threads we permit to476// run concurrently doing DNS lookups.477func concurrentThreadsLimit() int {478 return 500479}
Findings
✓ No findings reported for this file.