...

Source file src/pkg/net/lookup_unix.go

     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 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, 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, 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	}
   354	

View as plain text