Source file src/net/lookup_plan9.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  package net
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"internal/bytealg"
    11  	"internal/itoa"
    12  	"io"
    13  	"os"
    14  )
    15  
    16  func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) {
    17  	queryAddrs := func() (addrs []string, err error) {
    18  		file, err := os.OpenFile(filename, os.O_RDWR, 0)
    19  		if err != nil {
    20  			return nil, err
    21  		}
    22  		defer file.Close()
    23  
    24  		_, err = file.Seek(0, io.SeekStart)
    25  		if err != nil {
    26  			return nil, err
    27  		}
    28  		_, err = file.WriteString(query)
    29  		if err != nil {
    30  			return nil, err
    31  		}
    32  		_, err = file.Seek(0, io.SeekStart)
    33  		if err != nil {
    34  			return nil, err
    35  		}
    36  		buf := make([]byte, bufSize)
    37  		for {
    38  			n, _ := file.Read(buf)
    39  			if n <= 0 {
    40  				break
    41  			}
    42  			addrs = append(addrs, string(buf[:n]))
    43  		}
    44  		return addrs, nil
    45  	}
    46  
    47  	type ret struct {
    48  		addrs []string
    49  		err   error
    50  	}
    51  
    52  	ch := make(chan ret, 1)
    53  	go func() {
    54  		addrs, err := queryAddrs()
    55  		ch <- ret{addrs: addrs, err: err}
    56  	}()
    57  
    58  	select {
    59  	case r := <-ch:
    60  		return r.addrs, r.err
    61  	case <-ctx.Done():
    62  		return nil, &DNSError{
    63  			Name:      query,
    64  			Err:       ctx.Err().Error(),
    65  			IsTimeout: ctx.Err() == context.DeadlineExceeded,
    66  		}
    67  	}
    68  }
    69  
    70  func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
    71  	switch net {
    72  	case "tcp4", "tcp6":
    73  		net = "tcp"
    74  	case "udp4", "udp6":
    75  		net = "udp"
    76  	}
    77  	if host == "" {
    78  		host = "*"
    79  	}
    80  	return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
    81  }
    82  
    83  func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
    84  	ips := "*"
    85  	if len(ip) != 0 && !ip.IsUnspecified() {
    86  		ips = ip.String()
    87  	}
    88  	lines, err := queryCS(ctx, net, ips, itoa.Itoa(port))
    89  	if err != nil {
    90  		return
    91  	}
    92  	f := getFields(lines[0])
    93  	if len(f) < 2 {
    94  		return "", "", errors.New("bad response from ndb/cs")
    95  	}
    96  	clone, dest = f[0], f[1]
    97  	return
    98  }
    99  
   100  func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
   101  	return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
   102  }
   103  
   104  // toLower returns a lower-case version of in. Restricting us to
   105  // ASCII is sufficient to handle the IP protocol names and allow
   106  // us to not depend on the strings and unicode packages.
   107  func toLower(in string) string {
   108  	for _, c := range in {
   109  		if 'A' <= c && c <= 'Z' {
   110  			// Has upper case; need to fix.
   111  			out := []byte(in)
   112  			for i := 0; i < len(in); i++ {
   113  				c := in[i]
   114  				if 'A' <= c && c <= 'Z' {
   115  					c += 'a' - 'A'
   116  				}
   117  				out[i] = c
   118  			}
   119  			return string(out)
   120  		}
   121  	}
   122  	return in
   123  }
   124  
   125  // lookupProtocol looks up IP protocol name and returns
   126  // the corresponding protocol number.
   127  func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
   128  	lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
   129  	if err != nil {
   130  		return 0, err
   131  	}
   132  	if len(lines) == 0 {
   133  		return 0, UnknownNetworkError(name)
   134  	}
   135  	f := getFields(lines[0])
   136  	if len(f) < 2 {
   137  		return 0, UnknownNetworkError(name)
   138  	}
   139  	s := f[1]
   140  	if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok {
   141  		return n, nil
   142  	}
   143  	return 0, UnknownNetworkError(name)
   144  }
   145  
   146  func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
   147  	// Use netdir/cs instead of netdir/dns because cs knows about
   148  	// host names in local network (e.g. from /lib/ndb/local)
   149  	lines, err := queryCS(ctx, "net", host, "1")
   150  	if err != nil {
   151  		dnsError := &DNSError{Err: err.Error(), Name: host}
   152  		if stringsHasSuffix(err.Error(), "dns failure") {
   153  			dnsError.Err = errNoSuchHost.Error()
   154  			dnsError.IsNotFound = true
   155  		}
   156  		return nil, dnsError
   157  	}
   158  loop:
   159  	for _, line := range lines {
   160  		f := getFields(line)
   161  		if len(f) < 2 {
   162  			continue
   163  		}
   164  		addr := f[1]
   165  		if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
   166  			addr = addr[:i] // remove port
   167  		}
   168  		if ParseIP(addr) == nil {
   169  			continue
   170  		}
   171  		// only return unique addresses
   172  		for _, a := range addrs {
   173  			if a == addr {
   174  				continue loop
   175  			}
   176  		}
   177  		addrs = append(addrs, addr)
   178  	}
   179  	return
   180  }
   181  
   182  // preferGoOverPlan9 reports whether the resolver should use the
   183  // "PreferGo" implementation rather than asking plan9 services
   184  // for the answers.
   185  func (r *Resolver) preferGoOverPlan9() bool {
   186  	conf := systemConf()
   187  	order := conf.hostLookupOrder(r, "") // name is unused
   188  
   189  	// TODO(bradfitz): for now we only permit use of the PreferGo
   190  	// implementation when there's a non-nil Resolver with a
   191  	// non-nil Dialer. This is a sign that they the code is trying
   192  	// to use their DNS-speaking net.Conn (such as an in-memory
   193  	// DNS cache) and they don't want to actually hit the network.
   194  	// Once we add support for looking the default DNS servers
   195  	// from plan9, though, then we can relax this.
   196  	return order != hostLookupCgo && r != nil && r.Dial != nil
   197  }
   198  
   199  func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
   200  	if r.preferGoOverPlan9() {
   201  		return r.goLookupIP(ctx, network, host)
   202  	}
   203  	lits, err := r.lookupHost(ctx, host)
   204  	if err != nil {
   205  		return
   206  	}
   207  	for _, lit := range lits {
   208  		host, zone := splitHostZone(lit)
   209  		if ip := ParseIP(host); ip != nil {
   210  			addr := IPAddr{IP: ip, Zone: zone}
   211  			addrs = append(addrs, addr)
   212  		}
   213  	}
   214  	return
   215  }
   216  
   217  func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
   218  	switch network {
   219  	case "tcp4", "tcp6":
   220  		network = "tcp"
   221  	case "udp4", "udp6":
   222  		network = "udp"
   223  	}
   224  	lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
   225  	if err != nil {
   226  		return
   227  	}
   228  	unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service}
   229  	if len(lines) == 0 {
   230  		return 0, unknownPortError
   231  	}
   232  	f := getFields(lines[0])
   233  	if len(f) < 2 {
   234  		return 0, unknownPortError
   235  	}
   236  	s := f[1]
   237  	if i := bytealg.IndexByteString(s, '!'); i >= 0 {
   238  		s = s[i+1:] // remove address
   239  	}
   240  	if n, _, ok := dtoi(s); ok {
   241  		return n, nil
   242  	}
   243  	return 0, unknownPortError
   244  }
   245  
   246  func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
   247  	if r.preferGoOverPlan9() {
   248  		return r.goLookupCNAME(ctx, name)
   249  	}
   250  	lines, err := queryDNS(ctx, name, "cname")
   251  	if err != nil {
   252  		if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
   253  			cname = name + "."
   254  			err = nil
   255  		}
   256  		return
   257  	}
   258  	if len(lines) > 0 {
   259  		if f := getFields(lines[0]); len(f) >= 3 {
   260  			return f[2] + ".", nil
   261  		}
   262  	}
   263  	return "", errors.New("bad response from ndb/dns")
   264  }
   265  
   266  func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
   267  	if r.preferGoOverPlan9() {
   268  		return r.goLookupSRV(ctx, service, proto, name)
   269  	}
   270  	var target string
   271  	if service == "" && proto == "" {
   272  		target = name
   273  	} else {
   274  		target = "_" + service + "._" + proto + "." + name
   275  	}
   276  	lines, err := queryDNS(ctx, target, "srv")
   277  	if err != nil {
   278  		return
   279  	}
   280  	for _, line := range lines {
   281  		f := getFields(line)
   282  		if len(f) < 6 {
   283  			continue
   284  		}
   285  		port, _, portOk := dtoi(f[4])
   286  		priority, _, priorityOk := dtoi(f[3])
   287  		weight, _, weightOk := dtoi(f[2])
   288  		if !(portOk && priorityOk && weightOk) {
   289  			continue
   290  		}
   291  		addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)})
   292  		cname = absDomainName(f[0])
   293  	}
   294  	byPriorityWeight(addrs).sort()
   295  	return
   296  }
   297  
   298  func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
   299  	if r.preferGoOverPlan9() {
   300  		return r.goLookupMX(ctx, name)
   301  	}
   302  	lines, err := queryDNS(ctx, name, "mx")
   303  	if err != nil {
   304  		return
   305  	}
   306  	for _, line := range lines {
   307  		f := getFields(line)
   308  		if len(f) < 4 {
   309  			continue
   310  		}
   311  		if pref, _, ok := dtoi(f[2]); ok {
   312  			mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)})
   313  		}
   314  	}
   315  	byPref(mx).sort()
   316  	return
   317  }
   318  
   319  func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
   320  	if r.preferGoOverPlan9() {
   321  		return r.goLookupNS(ctx, name)
   322  	}
   323  	lines, err := queryDNS(ctx, name, "ns")
   324  	if err != nil {
   325  		return
   326  	}
   327  	for _, line := range lines {
   328  		f := getFields(line)
   329  		if len(f) < 3 {
   330  			continue
   331  		}
   332  		ns = append(ns, &NS{absDomainName(f[2])})
   333  	}
   334  	return
   335  }
   336  
   337  func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
   338  	if r.preferGoOverPlan9() {
   339  		return r.goLookupTXT(ctx, name)
   340  	}
   341  	lines, err := queryDNS(ctx, name, "txt")
   342  	if err != nil {
   343  		return
   344  	}
   345  	for _, line := range lines {
   346  		if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
   347  			txt = append(txt, line[i+1:])
   348  		}
   349  	}
   350  	return
   351  }
   352  
   353  func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
   354  	if r.preferGoOverPlan9() {
   355  		return r.goLookupPTR(ctx, addr)
   356  	}
   357  	arpa, err := reverseaddr(addr)
   358  	if err != nil {
   359  		return
   360  	}
   361  	lines, err := queryDNS(ctx, arpa, "ptr")
   362  	if err != nil {
   363  		return
   364  	}
   365  	for _, line := range lines {
   366  		f := getFields(line)
   367  		if len(f) < 3 {
   368  			continue
   369  		}
   370  		name = append(name, absDomainName(f[2]))
   371  	}
   372  	return
   373  }
   374  
   375  // concurrentThreadsLimit returns the number of threads we permit to
   376  // run concurrently doing DNS lookups.
   377  func concurrentThreadsLimit() int {
   378  	return 500
   379  }
   380  

View as plain text