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

View as plain text