...

Source file src/net/dnsclient_unix.go

Documentation: net

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

View as plain text