...

Source file src/pkg/net/dnsclient_unix.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	// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
     6	
     7	// DNS client: see RFC 1035.
     8	// Has to be linked into package net for Dial.
     9	
    10	// TODO(rsc):
    11	//	Could potentially handle many outstanding lookups faster.
    12	//	Random UDP source port (net.Dial should do that for us).
    13	//	Random request IDs.
    14	
    15	package net
    16	
    17	import (
    18		"context"
    19		"errors"
    20		"io"
    21		"math/rand"
    22		"os"
    23		"sync"
    24		"time"
    25	
    26		"golang.org/x/net/dns/dnsmessage"
    27	)
    28	
    29	const (
    30		// to be used as a useTCP parameter to exchange
    31		useTCPOnly  = true
    32		useUDPOrTCP = false
    33	)
    34	
    35	var (
    36		errLameReferral              = errors.New("lame referral")
    37		errCannotUnmarshalDNSMessage = errors.New("cannot unmarshal DNS message")
    38		errCannotMarshalDNSMessage   = errors.New("cannot marshal DNS message")
    39		errServerMisbehaving         = errors.New("server misbehaving")
    40		errInvalidDNSResponse        = errors.New("invalid DNS response")
    41		errNoAnswerFromDNSServer     = errors.New("no answer from DNS server")
    42	
    43		// errServerTemporarlyMisbehaving is like errServerMisbehaving, except
    44		// that when it gets translated to a DNSError, the IsTemporary field
    45		// gets set to true.
    46		errServerTemporarlyMisbehaving = errors.New("server misbehaving")
    47	)
    48	
    49	func newRequest(q dnsmessage.Question) (id uint16, udpReq, tcpReq []byte, err error) {
    50		id = uint16(rand.Int()) ^ uint16(time.Now().UnixNano())
    51		b := dnsmessage.NewBuilder(make([]byte, 2, 514), dnsmessage.Header{ID: id, RecursionDesired: true})
    52		b.EnableCompression()
    53		if err := b.StartQuestions(); err != nil {
    54			return 0, nil, nil, err
    55		}
    56		if err := b.Question(q); err != nil {
    57			return 0, nil, nil, err
    58		}
    59		tcpReq, err = b.Finish()
    60		udpReq = tcpReq[2:]
    61		l := len(tcpReq) - 2
    62		tcpReq[0] = byte(l >> 8)
    63		tcpReq[1] = byte(l)
    64		return id, udpReq, tcpReq, err
    65	}
    66	
    67	func checkResponse(reqID uint16, reqQues dnsmessage.Question, respHdr dnsmessage.Header, respQues dnsmessage.Question) bool {
    68		if !respHdr.Response {
    69			return false
    70		}
    71		if reqID != respHdr.ID {
    72			return false
    73		}
    74		if reqQues.Type != respQues.Type || reqQues.Class != respQues.Class || !equalASCIIName(reqQues.Name, respQues.Name) {
    75			return false
    76		}
    77		return true
    78	}
    79	
    80	func dnsPacketRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
    81		if _, err := c.Write(b); err != nil {
    82			return dnsmessage.Parser{}, dnsmessage.Header{}, err
    83		}
    84	
    85		b = make([]byte, 512) // see RFC 1035
    86		for {
    87			n, err := c.Read(b)
    88			if err != nil {
    89				return dnsmessage.Parser{}, dnsmessage.Header{}, err
    90			}
    91			var p dnsmessage.Parser
    92			// Ignore invalid responses as they may be malicious
    93			// forgery attempts. Instead continue waiting until
    94			// timeout. See golang.org/issue/13281.
    95			h, err := p.Start(b[:n])
    96			if err != nil {
    97				continue
    98			}
    99			q, err := p.Question()
   100			if err != nil || !checkResponse(id, query, h, q) {
   101				continue
   102			}
   103			return p, h, nil
   104		}
   105	}
   106	
   107	func dnsStreamRoundTrip(c Conn, id uint16, query dnsmessage.Question, b []byte) (dnsmessage.Parser, dnsmessage.Header, error) {
   108		if _, err := c.Write(b); err != nil {
   109			return dnsmessage.Parser{}, dnsmessage.Header{}, err
   110		}
   111	
   112		b = make([]byte, 1280) // 1280 is a reasonable initial size for IP over Ethernet, see RFC 4035
   113		if _, err := io.ReadFull(c, b[:2]); err != nil {
   114			return dnsmessage.Parser{}, dnsmessage.Header{}, err
   115		}
   116		l := int(b[0])<<8 | int(b[1])
   117		if l > len(b) {
   118			b = make([]byte, l)
   119		}
   120		n, err := io.ReadFull(c, b[:l])
   121		if err != nil {
   122			return dnsmessage.Parser{}, dnsmessage.Header{}, err
   123		}
   124		var p dnsmessage.Parser
   125		h, err := p.Start(b[:n])
   126		if err != nil {
   127			return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
   128		}
   129		q, err := p.Question()
   130		if err != nil {
   131			return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotUnmarshalDNSMessage
   132		}
   133		if !checkResponse(id, query, h, q) {
   134			return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
   135		}
   136		return p, h, nil
   137	}
   138	
   139	// exchange sends a query on the connection and hopes for a response.
   140	func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Question, timeout time.Duration, useTCP bool) (dnsmessage.Parser, dnsmessage.Header, error) {
   141		q.Class = dnsmessage.ClassINET
   142		id, udpReq, tcpReq, err := newRequest(q)
   143		if err != nil {
   144			return dnsmessage.Parser{}, dnsmessage.Header{}, errCannotMarshalDNSMessage
   145		}
   146		var networks []string
   147		if useTCP {
   148			networks = []string{"tcp"}
   149		} else {
   150			networks = []string{"udp", "tcp"}
   151		}
   152		for _, network := range networks {
   153			ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
   154			defer cancel()
   155	
   156			c, err := r.dial(ctx, network, server)
   157			if err != nil {
   158				return dnsmessage.Parser{}, dnsmessage.Header{}, err
   159			}
   160			if d, ok := ctx.Deadline(); ok && !d.IsZero() {
   161				c.SetDeadline(d)
   162			}
   163			var p dnsmessage.Parser
   164			var h dnsmessage.Header
   165			if _, ok := c.(PacketConn); ok {
   166				p, h, err = dnsPacketRoundTrip(c, id, q, udpReq)
   167			} else {
   168				p, h, err = dnsStreamRoundTrip(c, id, q, tcpReq)
   169			}
   170			c.Close()
   171			if err != nil {
   172				return dnsmessage.Parser{}, dnsmessage.Header{}, mapErr(err)
   173			}
   174			if err := p.SkipQuestion(); err != dnsmessage.ErrSectionDone {
   175				return dnsmessage.Parser{}, dnsmessage.Header{}, errInvalidDNSResponse
   176			}
   177			if h.Truncated { // see RFC 5966
   178				continue
   179			}
   180			return p, h, nil
   181		}
   182		return dnsmessage.Parser{}, dnsmessage.Header{}, errNoAnswerFromDNSServer
   183	}
   184	
   185	// checkHeader performs basic sanity checks on the header.
   186	func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
   187		if h.RCode == dnsmessage.RCodeNameError {
   188			return errNoSuchHost
   189		}
   190	
   191		_, err := p.AnswerHeader()
   192		if err != nil && err != dnsmessage.ErrSectionDone {
   193			return errCannotUnmarshalDNSMessage
   194		}
   195	
   196		// libresolv continues to the next server when it receives
   197		// an invalid referral response. See golang.org/issue/15434.
   198		if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
   199			return errLameReferral
   200		}
   201	
   202		if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
   203			// None of the error codes make sense
   204			// for the query we sent. If we didn't get
   205			// a name error and we didn't get success,
   206			// the server is behaving incorrectly or
   207			// having temporary trouble.
   208			if h.RCode == dnsmessage.RCodeServerFailure {
   209				return errServerTemporarlyMisbehaving
   210			}
   211			return errServerMisbehaving
   212		}
   213	
   214		return nil
   215	}
   216	
   217	func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
   218		for {
   219			h, err := p.AnswerHeader()
   220			if err == dnsmessage.ErrSectionDone {
   221				return errNoSuchHost
   222			}
   223			if err != nil {
   224				return errCannotUnmarshalDNSMessage
   225			}
   226			if h.Type == qtype {
   227				return nil
   228			}
   229			if err := p.SkipAnswer(); err != nil {
   230				return errCannotUnmarshalDNSMessage
   231			}
   232		}
   233	}
   234	
   235	// Do a lookup for a single name, which must be rooted
   236	// (otherwise answer will not find the answers).
   237	func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
   238		var lastErr error
   239		serverOffset := cfg.serverOffset()
   240		sLen := uint32(len(cfg.servers))
   241	
   242		n, err := dnsmessage.NewName(name)
   243		if err != nil {
   244			return dnsmessage.Parser{}, "", errCannotMarshalDNSMessage
   245		}
   246		q := dnsmessage.Question{
   247			Name:  n,
   248			Type:  qtype,
   249			Class: dnsmessage.ClassINET,
   250		}
   251	
   252		for i := 0; i < cfg.attempts; i++ {
   253			for j := uint32(0); j < sLen; j++ {
   254				server := cfg.servers[(serverOffset+j)%sLen]
   255	
   256				p, h, err := r.exchange(ctx, server, q, cfg.timeout, cfg.useTCP)
   257				if err != nil {
   258					dnsErr := &DNSError{
   259						Err:    err.Error(),
   260						Name:   name,
   261						Server: server,
   262					}
   263					if nerr, ok := err.(Error); ok && nerr.Timeout() {
   264						dnsErr.IsTimeout = true
   265					}
   266					// Set IsTemporary for socket-level errors. Note that this flag
   267					// may also be used to indicate a SERVFAIL response.
   268					if _, ok := err.(*OpError); ok {
   269						dnsErr.IsTemporary = true
   270					}
   271					lastErr = dnsErr
   272					continue
   273				}
   274	
   275				if err := checkHeader(&p, h); err != nil {
   276					dnsErr := &DNSError{
   277						Err:    err.Error(),
   278						Name:   name,
   279						Server: server,
   280					}
   281					if err == errServerTemporarlyMisbehaving {
   282						dnsErr.IsTemporary = true
   283					}
   284					if err == errNoSuchHost {
   285						// The name does not exist, so trying
   286						// another server won't help.
   287	
   288						dnsErr.IsNotFound = true
   289						return p, server, dnsErr
   290					}
   291					lastErr = dnsErr
   292					continue
   293				}
   294	
   295				err = skipToAnswer(&p, qtype)
   296				if err == nil {
   297					return p, server, nil
   298				}
   299				lastErr = &DNSError{
   300					Err:    err.Error(),
   301					Name:   name,
   302					Server: server,
   303				}
   304				if err == errNoSuchHost {
   305					// The name does not exist, so trying another
   306					// server won't help.
   307	
   308					lastErr.(*DNSError).IsNotFound = true
   309					return p, server, lastErr
   310				}
   311			}
   312		}
   313		return dnsmessage.Parser{}, "", lastErr
   314	}
   315	
   316	// A resolverConfig represents a DNS stub resolver configuration.
   317	type resolverConfig struct {
   318		initOnce sync.Once // guards init of resolverConfig
   319	
   320		// ch is used as a semaphore that only allows one lookup at a
   321		// time to recheck resolv.conf.
   322		ch          chan struct{} // guards lastChecked and modTime
   323		lastChecked time.Time     // last time resolv.conf was checked
   324	
   325		mu        sync.RWMutex // protects dnsConfig
   326		dnsConfig *dnsConfig   // parsed resolv.conf structure used in lookups
   327	}
   328	
   329	var resolvConf resolverConfig
   330	
   331	// init initializes conf and is only called via conf.initOnce.
   332	func (conf *resolverConfig) init() {
   333		// Set dnsConfig and lastChecked so we don't parse
   334		// resolv.conf twice the first time.
   335		conf.dnsConfig = systemConf().resolv
   336		if conf.dnsConfig == nil {
   337			conf.dnsConfig = dnsReadConfig("/etc/resolv.conf")
   338		}
   339		conf.lastChecked = time.Now()
   340	
   341		// Prepare ch so that only one update of resolverConfig may
   342		// run at once.
   343		conf.ch = make(chan struct{}, 1)
   344	}
   345	
   346	// tryUpdate tries to update conf with the named resolv.conf file.
   347	// The name variable only exists for testing. It is otherwise always
   348	// "/etc/resolv.conf".
   349	func (conf *resolverConfig) tryUpdate(name string) {
   350		conf.initOnce.Do(conf.init)
   351	
   352		// Ensure only one update at a time checks resolv.conf.
   353		if !conf.tryAcquireSema() {
   354			return
   355		}
   356		defer conf.releaseSema()
   357	
   358		now := time.Now()
   359		if conf.lastChecked.After(now.Add(-5 * time.Second)) {
   360			return
   361		}
   362		conf.lastChecked = now
   363	
   364		var mtime time.Time
   365		if fi, err := os.Stat(name); err == nil {
   366			mtime = fi.ModTime()
   367		}
   368		if mtime.Equal(conf.dnsConfig.mtime) {
   369			return
   370		}
   371	
   372		dnsConf := dnsReadConfig(name)
   373		conf.mu.Lock()
   374		conf.dnsConfig = dnsConf
   375		conf.mu.Unlock()
   376	}
   377	
   378	func (conf *resolverConfig) tryAcquireSema() bool {
   379		select {
   380		case conf.ch <- struct{}{}:
   381			return true
   382		default:
   383			return false
   384		}
   385	}
   386	
   387	func (conf *resolverConfig) releaseSema() {
   388		<-conf.ch
   389	}
   390	
   391	func (r *Resolver) lookup(ctx context.Context, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
   392		if !isDomainName(name) {
   393			// We used to use "invalid domain name" as the error,
   394			// but that is a detail of the specific lookup mechanism.
   395			// Other lookups might allow broader name syntax
   396			// (for example Multicast DNS allows UTF-8; see RFC 6762).
   397			// For consistency with libc resolvers, report no such host.
   398			return dnsmessage.Parser{}, "", &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
   399		}
   400		resolvConf.tryUpdate("/etc/resolv.conf")
   401		resolvConf.mu.RLock()
   402		conf := resolvConf.dnsConfig
   403		resolvConf.mu.RUnlock()
   404		var (
   405			p      dnsmessage.Parser
   406			server string
   407			err    error
   408		)
   409		for _, fqdn := range conf.nameList(name) {
   410			p, server, err = r.tryOneName(ctx, conf, fqdn, qtype)
   411			if err == nil {
   412				break
   413			}
   414			if nerr, ok := err.(Error); ok && nerr.Temporary() && r.strictErrors() {
   415				// If we hit a temporary error with StrictErrors enabled,
   416				// stop immediately instead of trying more names.
   417				break
   418			}
   419		}
   420		if err == nil {
   421			return p, server, nil
   422		}
   423		if err, ok := err.(*DNSError); ok {
   424			// Show original name passed to lookup, not suffixed one.
   425			// In general we might have tried many suffixes; showing
   426			// just one is misleading. See also golang.org/issue/6324.
   427			err.Name = name
   428		}
   429		return dnsmessage.Parser{}, "", err
   430	}
   431	
   432	// avoidDNS reports whether this is a hostname for which we should not
   433	// use DNS. Currently this includes only .onion, per RFC 7686. See
   434	// golang.org/issue/13705. Does not cover .local names (RFC 6762),
   435	// see golang.org/issue/16739.
   436	func avoidDNS(name string) bool {
   437		if name == "" {
   438			return true
   439		}
   440		if name[len(name)-1] == '.' {
   441			name = name[:len(name)-1]
   442		}
   443		return stringsHasSuffixFold(name, ".onion")
   444	}
   445	
   446	// nameList returns a list of names for sequential DNS queries.
   447	func (conf *dnsConfig) nameList(name string) []string {
   448		if avoidDNS(name) {
   449			return nil
   450		}
   451	
   452		// Check name length (see isDomainName).
   453		l := len(name)
   454		rooted := l > 0 && name[l-1] == '.'
   455		if l > 254 || l == 254 && rooted {
   456			return nil
   457		}
   458	
   459		// If name is rooted (trailing dot), try only that name.
   460		if rooted {
   461			return []string{name}
   462		}
   463	
   464		hasNdots := count(name, '.') >= conf.ndots
   465		name += "."
   466		l++
   467	
   468		// Build list of search choices.
   469		names := make([]string, 0, 1+len(conf.search))
   470		// If name has enough dots, try unsuffixed first.
   471		if hasNdots {
   472			names = append(names, name)
   473		}
   474		// Try suffixes that are not too long (see isDomainName).
   475		for _, suffix := range conf.search {
   476			if l+len(suffix) <= 254 {
   477				names = append(names, name+suffix)
   478			}
   479		}
   480		// Try unsuffixed, if not tried first above.
   481		if !hasNdots {
   482			names = append(names, name)
   483		}
   484		return names
   485	}
   486	
   487	// hostLookupOrder specifies the order of LookupHost lookup strategies.
   488	// It is basically a simplified representation of nsswitch.conf.
   489	// "files" means /etc/hosts.
   490	type hostLookupOrder int
   491	
   492	const (
   493		// hostLookupCgo means defer to cgo.
   494		hostLookupCgo      hostLookupOrder = iota
   495		hostLookupFilesDNS                 // files first
   496		hostLookupDNSFiles                 // dns first
   497		hostLookupFiles                    // only files
   498		hostLookupDNS                      // only DNS
   499	)
   500	
   501	var lookupOrderName = map[hostLookupOrder]string{
   502		hostLookupCgo:      "cgo",
   503		hostLookupFilesDNS: "files,dns",
   504		hostLookupDNSFiles: "dns,files",
   505		hostLookupFiles:    "files",
   506		hostLookupDNS:      "dns",
   507	}
   508	
   509	func (o hostLookupOrder) String() string {
   510		if s, ok := lookupOrderName[o]; ok {
   511			return s
   512		}
   513		return "hostLookupOrder=" + itoa(int(o)) + "??"
   514	}
   515	
   516	// goLookupHost is the native Go implementation of LookupHost.
   517	// Used only if cgoLookupHost refuses to handle the request
   518	// (that is, only if cgoLookupHost is the stub in cgo_stub.go).
   519	// Normally we let cgo use the C library resolver instead of
   520	// depending on our lookup code, so that Go and C get the same
   521	// answers.
   522	func (r *Resolver) goLookupHost(ctx context.Context, name string) (addrs []string, err error) {
   523		return r.goLookupHostOrder(ctx, name, hostLookupFilesDNS)
   524	}
   525	
   526	func (r *Resolver) goLookupHostOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []string, err error) {
   527		if order == hostLookupFilesDNS || order == hostLookupFiles {
   528			// Use entries from /etc/hosts if they match.
   529			addrs = lookupStaticHost(name)
   530			if len(addrs) > 0 || order == hostLookupFiles {
   531				return
   532			}
   533		}
   534		ips, _, err := r.goLookupIPCNAMEOrder(ctx, name, order)
   535		if err != nil {
   536			return
   537		}
   538		addrs = make([]string, 0, len(ips))
   539		for _, ip := range ips {
   540			addrs = append(addrs, ip.String())
   541		}
   542		return
   543	}
   544	
   545	// lookup entries from /etc/hosts
   546	func goLookupIPFiles(name string) (addrs []IPAddr) {
   547		for _, haddr := range lookupStaticHost(name) {
   548			haddr, zone := splitHostZone(haddr)
   549			if ip := ParseIP(haddr); ip != nil {
   550				addr := IPAddr{IP: ip, Zone: zone}
   551				addrs = append(addrs, addr)
   552			}
   553		}
   554		sortByRFC6724(addrs)
   555		return
   556	}
   557	
   558	// goLookupIP is the native Go implementation of LookupIP.
   559	// The libc versions are in cgo_*.go.
   560	func (r *Resolver) goLookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
   561		order := systemConf().hostLookupOrder(r, host)
   562		addrs, _, err = r.goLookupIPCNAMEOrder(ctx, host, order)
   563		return
   564	}
   565	
   566	func (r *Resolver) goLookupIPCNAMEOrder(ctx context.Context, name string, order hostLookupOrder) (addrs []IPAddr, cname dnsmessage.Name, err error) {
   567		if order == hostLookupFilesDNS || order == hostLookupFiles {
   568			addrs = goLookupIPFiles(name)
   569			if len(addrs) > 0 || order == hostLookupFiles {
   570				return addrs, dnsmessage.Name{}, nil
   571			}
   572		}
   573		if !isDomainName(name) {
   574			// See comment in func lookup above about use of errNoSuchHost.
   575			return nil, dnsmessage.Name{}, &DNSError{Err: errNoSuchHost.Error(), Name: name, IsNotFound: true}
   576		}
   577		resolvConf.tryUpdate("/etc/resolv.conf")
   578		resolvConf.mu.RLock()
   579		conf := resolvConf.dnsConfig
   580		resolvConf.mu.RUnlock()
   581		type result struct {
   582			p      dnsmessage.Parser
   583			server string
   584			error
   585		}
   586		lane := make(chan result, 1)
   587		qtypes := [...]dnsmessage.Type{dnsmessage.TypeA, dnsmessage.TypeAAAA}
   588		var queryFn func(fqdn string, qtype dnsmessage.Type)
   589		var responseFn func(fqdn string, qtype dnsmessage.Type) result
   590		if conf.singleRequest {
   591			queryFn = func(fqdn string, qtype dnsmessage.Type) {}
   592			responseFn = func(fqdn string, qtype dnsmessage.Type) result {
   593				dnsWaitGroup.Add(1)
   594				defer dnsWaitGroup.Done()
   595				p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
   596				return result{p, server, err}
   597			}
   598		} else {
   599			queryFn = func(fqdn string, qtype dnsmessage.Type) {
   600				dnsWaitGroup.Add(1)
   601				go func(qtype dnsmessage.Type) {
   602					p, server, err := r.tryOneName(ctx, conf, fqdn, qtype)
   603					lane <- result{p, server, err}
   604					dnsWaitGroup.Done()
   605				}(qtype)
   606			}
   607			responseFn = func(fqdn string, qtype dnsmessage.Type) result {
   608				return <-lane
   609			}
   610		}
   611		var lastErr error
   612		for _, fqdn := range conf.nameList(name) {
   613			for _, qtype := range qtypes {
   614				queryFn(fqdn, qtype)
   615			}
   616			hitStrictError := false
   617			for _, qtype := range qtypes {
   618				result := responseFn(fqdn, qtype)
   619				if result.error != nil {
   620					if nerr, ok := result.error.(Error); ok && nerr.Temporary() && r.strictErrors() {
   621						// This error will abort the nameList loop.
   622						hitStrictError = true
   623						lastErr = result.error
   624					} else if lastErr == nil || fqdn == name+"." {
   625						// Prefer error for original name.
   626						lastErr = result.error
   627					}
   628					continue
   629				}
   630	
   631				// Presotto says it's okay to assume that servers listed in
   632				// /etc/resolv.conf are recursive resolvers.
   633				//
   634				// We asked for recursion, so it should have included all the
   635				// answers we need in this one packet.
   636				//
   637				// Further, RFC 1035 section 4.3.1 says that "the recursive
   638				// response to a query will be... The answer to the query,
   639				// possibly preface by one or more CNAME RRs that specify
   640				// aliases encountered on the way to an answer."
   641				//
   642				// Therefore, we should be able to assume that we can ignore
   643				// CNAMEs and that the A and AAAA records we requested are
   644				// for the canonical name.
   645	
   646			loop:
   647				for {
   648					h, err := result.p.AnswerHeader()
   649					if err != nil && err != dnsmessage.ErrSectionDone {
   650						lastErr = &DNSError{
   651							Err:    "cannot marshal DNS message",
   652							Name:   name,
   653							Server: result.server,
   654						}
   655					}
   656					if err != nil {
   657						break
   658					}
   659					switch h.Type {
   660					case dnsmessage.TypeA:
   661						a, err := result.p.AResource()
   662						if err != nil {
   663							lastErr = &DNSError{
   664								Err:    "cannot marshal DNS message",
   665								Name:   name,
   666								Server: result.server,
   667							}
   668							break loop
   669						}
   670						addrs = append(addrs, IPAddr{IP: IP(a.A[:])})
   671	
   672					case dnsmessage.TypeAAAA:
   673						aaaa, err := result.p.AAAAResource()
   674						if err != nil {
   675							lastErr = &DNSError{
   676								Err:    "cannot marshal DNS message",
   677								Name:   name,
   678								Server: result.server,
   679							}
   680							break loop
   681						}
   682						addrs = append(addrs, IPAddr{IP: IP(aaaa.AAAA[:])})
   683	
   684					default:
   685						if err := result.p.SkipAnswer(); err != nil {
   686							lastErr = &DNSError{
   687								Err:    "cannot marshal DNS message",
   688								Name:   name,
   689								Server: result.server,
   690							}
   691							break loop
   692						}
   693						continue
   694					}
   695					if cname.Length == 0 && h.Name.Length != 0 {
   696						cname = h.Name
   697					}
   698				}
   699			}
   700			if hitStrictError {
   701				// If either family hit an error with StrictErrors enabled,
   702				// discard all addresses. This ensures that network flakiness
   703				// cannot turn a dualstack hostname IPv4/IPv6-only.
   704				addrs = nil
   705				break
   706			}
   707			if len(addrs) > 0 {
   708				break
   709			}
   710		}
   711		if lastErr, ok := lastErr.(*DNSError); ok {
   712			// Show original name passed to lookup, not suffixed one.
   713			// In general we might have tried many suffixes; showing
   714			// just one is misleading. See also golang.org/issue/6324.
   715			lastErr.Name = name
   716		}
   717		sortByRFC6724(addrs)
   718		if len(addrs) == 0 {
   719			if order == hostLookupDNSFiles {
   720				addrs = goLookupIPFiles(name)
   721			}
   722			if len(addrs) == 0 && lastErr != nil {
   723				return nil, dnsmessage.Name{}, lastErr
   724			}
   725		}
   726		return addrs, cname, nil
   727	}
   728	
   729	// goLookupCNAME is the native Go (non-cgo) implementation of LookupCNAME.
   730	func (r *Resolver) goLookupCNAME(ctx context.Context, host string) (string, error) {
   731		order := systemConf().hostLookupOrder(r, host)
   732		_, cname, err := r.goLookupIPCNAMEOrder(ctx, host, order)
   733		return cname.String(), err
   734	}
   735	
   736	// goLookupPTR is the native Go implementation of LookupAddr.
   737	// Used only if cgoLookupPTR refuses to handle the request (that is,
   738	// only if cgoLookupPTR is the stub in cgo_stub.go).
   739	// Normally we let cgo use the C library resolver instead of depending
   740	// on our lookup code, so that Go and C get the same answers.
   741	func (r *Resolver) goLookupPTR(ctx context.Context, addr string) ([]string, error) {
   742		names := lookupStaticAddr(addr)
   743		if len(names) > 0 {
   744			return names, nil
   745		}
   746		arpa, err := reverseaddr(addr)
   747		if err != nil {
   748			return nil, err
   749		}
   750		p, server, err := r.lookup(ctx, arpa, dnsmessage.TypePTR)
   751		if err != nil {
   752			return nil, err
   753		}
   754		var ptrs []string
   755		for {
   756			h, err := p.AnswerHeader()
   757			if err == dnsmessage.ErrSectionDone {
   758				break
   759			}
   760			if err != nil {
   761				return nil, &DNSError{
   762					Err:    "cannot marshal DNS message",
   763					Name:   addr,
   764					Server: server,
   765				}
   766			}
   767			if h.Type != dnsmessage.TypePTR {
   768				err := p.SkipAnswer()
   769				if err != nil {
   770					return nil, &DNSError{
   771						Err:    "cannot marshal DNS message",
   772						Name:   addr,
   773						Server: server,
   774					}
   775				}
   776				continue
   777			}
   778			ptr, err := p.PTRResource()
   779			if err != nil {
   780				return nil, &DNSError{
   781					Err:    "cannot marshal DNS message",
   782					Name:   addr,
   783					Server: server,
   784				}
   785			}
   786			ptrs = append(ptrs, ptr.PTR.String())
   787	
   788		}
   789		return ptrs, nil
   790	}
   791	

View as plain text