lookup_plan9.mx raw

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