...
Run Format

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

View as plain text