Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/lookup_unix.go

Documentation: net

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

View as plain text