github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/net/lookup_unix.go (about)

     1  // Copyright 2011 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  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"internal/bytealg"
    12  	"sync"
    13  	"syscall"
    14  
    15  	"golang.org/x/net/dns/dnsmessage"
    16  )
    17  
    18  var onceReadProtocols sync.Once
    19  
    20  // readProtocols loads contents of /etc/protocols into protocols map
    21  // for quick access.
    22  func readProtocols() {
    23  	file, err := open("/etc/protocols")
    24  	if err != nil {
    25  		return
    26  	}
    27  	defer file.close()
    28  
    29  	for line, ok := file.readLine(); ok; line, ok = file.readLine() {
    30  		// tcp    6   TCP    # transmission control protocol
    31  		if i := bytealg.IndexByteString(line, '#'); i >= 0 {
    32  			line = line[0:i]
    33  		}
    34  		f := getFields(line)
    35  		if len(f) < 2 {
    36  			continue
    37  		}
    38  		if proto, _, ok := dtoi(f[1]); ok {
    39  			if _, ok := protocols[f[0]]; !ok {
    40  				protocols[f[0]] = proto
    41  			}
    42  			for _, alias := range f[2:] {
    43  				if _, ok := protocols[alias]; !ok {
    44  					protocols[alias] = proto
    45  				}
    46  			}
    47  		}
    48  	}
    49  }
    50  
    51  // lookupProtocol looks up IP protocol name in /etc/protocols and
    52  // returns correspondent protocol number.
    53  func lookupProtocol(_ context.Context, name string) (int, error) {
    54  	onceReadProtocols.Do(readProtocols)
    55  	return lookupProtocolMap(name)
    56  }
    57  
    58  func (r *Resolver) dial(ctx context.Context, network, server string) (Conn, error) {
    59  	// Calling Dial here is scary -- we have to be sure not to
    60  	// dial a name that will require a DNS lookup, or Dial will
    61  	// call back here to translate it. The DNS config parser has
    62  	// already checked that all the cfg.servers are IP
    63  	// addresses, which Dial will use without a DNS lookup.
    64  	var c Conn
    65  	var err error
    66  	if r != nil && r.Dial != nil {
    67  		c, err = r.Dial(ctx, network, server)
    68  	} else {
    69  		var d Dialer
    70  		c, err = d.DialContext(ctx, network, server)
    71  	}
    72  	if err != nil {
    73  		return nil, mapErr(err)
    74  	}
    75  	return c, nil
    76  }
    77  
    78  func (r *Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
    79  	order := systemConf().hostLookupOrder(r, host)
    80  	if !r.preferGo() && order == hostLookupCgo {
    81  		if addrs, err, ok := cgoLookupHost(ctx, host); ok {
    82  			return addrs, err
    83  		}
    84  		// cgo not available (or netgo); fall back to Go's DNS resolver
    85  		order = hostLookupFilesDNS
    86  	}
    87  	return r.goLookupHostOrder(ctx, host, order)
    88  }
    89  
    90  func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
    91  	if r.preferGo() {
    92  		return r.goLookupIP(ctx, network, host)
    93  	}
    94  	order := systemConf().hostLookupOrder(r, host)
    95  	if order == hostLookupCgo {
    96  		if addrs, err, ok := cgoLookupIP(ctx, network, host); ok {
    97  			return addrs, err
    98  		}
    99  		// cgo not available (or netgo); fall back to Go's DNS resolver
   100  		order = hostLookupFilesDNS
   101  	}
   102  	ips, _, err := r.goLookupIPCNAMEOrder(ctx, network, host, order)
   103  	return ips, err
   104  }
   105  
   106  func (r *Resolver) lookupPort(ctx context.Context, network, service string) (int, error) {
   107  	if !r.preferGo() && systemConf().canUseCgo() {
   108  		if port, err, ok := cgoLookupPort(ctx, network, service); ok {
   109  			if err != nil {
   110  				// Issue 18213: if cgo fails, first check to see whether we
   111  				// have the answer baked-in to the net package.
   112  				if port, err := goLookupPort(network, service); err == nil {
   113  					return port, nil
   114  				}
   115  			}
   116  			return port, err
   117  		}
   118  	}
   119  	return goLookupPort(network, service)
   120  }
   121  
   122  func (r *Resolver) lookupCNAME(ctx context.Context, name string) (string, error) {
   123  	if !r.preferGo() && systemConf().canUseCgo() {
   124  		if cname, err, ok := cgoLookupCNAME(ctx, name); ok {
   125  			return cname, err
   126  		}
   127  	}
   128  	return r.goLookupCNAME(ctx, name)
   129  }
   130  
   131  func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (string, []*SRV, error) {
   132  	var target string
   133  	if service == "" && proto == "" {
   134  		target = name
   135  	} else {
   136  		target = "_" + service + "._" + proto + "." + name
   137  	}
   138  	p, server, err := r.lookup(ctx, target, dnsmessage.TypeSRV)
   139  	if err != nil {
   140  		return "", nil, err
   141  	}
   142  	var srvs []*SRV
   143  	var cname dnsmessage.Name
   144  	for {
   145  		h, err := p.AnswerHeader()
   146  		if err == dnsmessage.ErrSectionDone {
   147  			break
   148  		}
   149  		if err != nil {
   150  			return "", nil, &DNSError{
   151  				Err:    "cannot unmarshal DNS message",
   152  				Name:   name,
   153  				Server: server,
   154  			}
   155  		}
   156  		if h.Type != dnsmessage.TypeSRV {
   157  			if err := p.SkipAnswer(); err != nil {
   158  				return "", nil, &DNSError{
   159  					Err:    "cannot unmarshal DNS message",
   160  					Name:   name,
   161  					Server: server,
   162  				}
   163  			}
   164  			continue
   165  		}
   166  		if cname.Length == 0 && h.Name.Length != 0 {
   167  			cname = h.Name
   168  		}
   169  		srv, err := p.SRVResource()
   170  		if err != nil {
   171  			return "", nil, &DNSError{
   172  				Err:    "cannot unmarshal DNS message",
   173  				Name:   name,
   174  				Server: server,
   175  			}
   176  		}
   177  		srvs = append(srvs, &SRV{Target: srv.Target.String(), Port: srv.Port, Priority: srv.Priority, Weight: srv.Weight})
   178  	}
   179  	byPriorityWeight(srvs).sort()
   180  	return cname.String(), srvs, nil
   181  }
   182  
   183  func (r *Resolver) lookupMX(ctx context.Context, name string) ([]*MX, error) {
   184  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeMX)
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  	var mxs []*MX
   189  	for {
   190  		h, err := p.AnswerHeader()
   191  		if err == dnsmessage.ErrSectionDone {
   192  			break
   193  		}
   194  		if err != nil {
   195  			return nil, &DNSError{
   196  				Err:    "cannot unmarshal DNS message",
   197  				Name:   name,
   198  				Server: server,
   199  			}
   200  		}
   201  		if h.Type != dnsmessage.TypeMX {
   202  			if err := p.SkipAnswer(); err != nil {
   203  				return nil, &DNSError{
   204  					Err:    "cannot unmarshal DNS message",
   205  					Name:   name,
   206  					Server: server,
   207  				}
   208  			}
   209  			continue
   210  		}
   211  		mx, err := p.MXResource()
   212  		if err != nil {
   213  			return nil, &DNSError{
   214  				Err:    "cannot unmarshal DNS message",
   215  				Name:   name,
   216  				Server: server,
   217  			}
   218  		}
   219  		mxs = append(mxs, &MX{Host: mx.MX.String(), Pref: mx.Pref})
   220  
   221  	}
   222  	byPref(mxs).sort()
   223  	return mxs, nil
   224  }
   225  
   226  func (r *Resolver) lookupNS(ctx context.Context, name string) ([]*NS, error) {
   227  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeNS)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	var nss []*NS
   232  	for {
   233  		h, err := p.AnswerHeader()
   234  		if err == dnsmessage.ErrSectionDone {
   235  			break
   236  		}
   237  		if err != nil {
   238  			return nil, &DNSError{
   239  				Err:    "cannot unmarshal DNS message",
   240  				Name:   name,
   241  				Server: server,
   242  			}
   243  		}
   244  		if h.Type != dnsmessage.TypeNS {
   245  			if err := p.SkipAnswer(); err != nil {
   246  				return nil, &DNSError{
   247  					Err:    "cannot unmarshal DNS message",
   248  					Name:   name,
   249  					Server: server,
   250  				}
   251  			}
   252  			continue
   253  		}
   254  		ns, err := p.NSResource()
   255  		if err != nil {
   256  			return nil, &DNSError{
   257  				Err:    "cannot unmarshal DNS message",
   258  				Name:   name,
   259  				Server: server,
   260  			}
   261  		}
   262  		nss = append(nss, &NS{Host: ns.NS.String()})
   263  	}
   264  	return nss, nil
   265  }
   266  
   267  func (r *Resolver) lookupTXT(ctx context.Context, name string) ([]string, error) {
   268  	p, server, err := r.lookup(ctx, name, dnsmessage.TypeTXT)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	var txts []string
   273  	for {
   274  		h, err := p.AnswerHeader()
   275  		if err == dnsmessage.ErrSectionDone {
   276  			break
   277  		}
   278  		if err != nil {
   279  			return nil, &DNSError{
   280  				Err:    "cannot unmarshal DNS message",
   281  				Name:   name,
   282  				Server: server,
   283  			}
   284  		}
   285  		if h.Type != dnsmessage.TypeTXT {
   286  			if err := p.SkipAnswer(); err != nil {
   287  				return nil, &DNSError{
   288  					Err:    "cannot unmarshal DNS message",
   289  					Name:   name,
   290  					Server: server,
   291  				}
   292  			}
   293  			continue
   294  		}
   295  		txt, err := p.TXTResource()
   296  		if err != nil {
   297  			return nil, &DNSError{
   298  				Err:    "cannot unmarshal DNS message",
   299  				Name:   name,
   300  				Server: server,
   301  			}
   302  		}
   303  		// Multiple strings in one TXT record need to be
   304  		// concatenated without separator to be consistent
   305  		// with previous Go resolver.
   306  		n := 0
   307  		for _, s := range txt.TXT {
   308  			n += len(s)
   309  		}
   310  		txtJoin := make([]byte, 0, n)
   311  		for _, s := range txt.TXT {
   312  			txtJoin = append(txtJoin, s...)
   313  		}
   314  		if len(txts) == 0 {
   315  			txts = make([]string, 0, 1)
   316  		}
   317  		txts = append(txts, string(txtJoin))
   318  	}
   319  	return txts, nil
   320  }
   321  
   322  func (r *Resolver) lookupAddr(ctx context.Context, addr string) ([]string, error) {
   323  	if !r.preferGo() && systemConf().canUseCgo() {
   324  		if ptrs, err, ok := cgoLookupPTR(ctx, addr); ok {
   325  			return ptrs, err
   326  		}
   327  	}
   328  	return r.goLookupPTR(ctx, addr)
   329  }
   330  
   331  // concurrentThreadsLimit returns the number of threads we permit to
   332  // run concurrently doing DNS lookups via cgo. A DNS lookup may use a
   333  // file descriptor so we limit this to less than the number of
   334  // permitted open files. On some systems, notably Darwin, if
   335  // getaddrinfo is unable to open a file descriptor it simply returns
   336  // EAI_NONAME rather than a useful error. Limiting the number of
   337  // concurrent getaddrinfo calls to less than the permitted number of
   338  // file descriptors makes that error less likely. We don't bother to
   339  // apply the same limit to DNS lookups run directly from Go, because
   340  // there we will return a meaningful "too many open files" error.
   341  func concurrentThreadsLimit() int {
   342  	var rlim syscall.Rlimit
   343  	if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil {
   344  		return 500
   345  	}
   346  	r := int(rlim.Cur)
   347  	if r > 500 {
   348  		r = 500
   349  	} else if r > 30 {
   350  		r -= 30
   351  	}
   352  	return r
   353  }