github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/net/lookup_plan9.go (about)

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