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

View as plain text