...

Source file src/net/lookup_windows.go

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

View as plain text