Source file src/net/lookup_plan9.go

Documentation: net

     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  	"io"
    12  	"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  		if stringsHasSuffix(err.Error(), "dns failure") {
   151  			err = errNoSuchHost
   152  		}
   153  		return
   154  	}
   155  loop:
   156  	for _, line := range lines {
   157  		f := getFields(line)
   158  		if len(f) < 2 {
   159  			continue
   160  		}
   161  		addr := f[1]
   162  		if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
   163  			addr = addr[:i] // remove port
   164  		}
   165  		if ParseIP(addr) == nil {
   166  			continue
   167  		}
   168  		// only return unique addresses
   169  		for _, a := range addrs {
   170  			if a == addr {
   171  				continue loop
   172  			}
   173  		}
   174  		addrs = append(addrs, addr)
   175  	}
   176  	return
   177  }
   178  
   179  func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) {
   180  	lits, err := r.lookupHost(ctx, host)
   181  	if err != nil {
   182  		return
   183  	}
   184  	for _, lit := range lits {
   185  		host, zone := splitHostZone(lit)
   186  		if ip := ParseIP(host); ip != nil {
   187  			addr := IPAddr{IP: ip, Zone: zone}
   188  			addrs = append(addrs, addr)
   189  		}
   190  	}
   191  	return
   192  }
   193  
   194  func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
   195  	switch network {
   196  	case "tcp4", "tcp6":
   197  		network = "tcp"
   198  	case "udp4", "udp6":
   199  		network = "udp"
   200  	}
   201  	lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
   202  	if err != nil {
   203  		return
   204  	}
   205  	unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service}
   206  	if len(lines) == 0 {
   207  		return 0, unknownPortError
   208  	}
   209  	f := getFields(lines[0])
   210  	if len(f) < 2 {
   211  		return 0, unknownPortError
   212  	}
   213  	s := f[1]
   214  	if i := bytealg.IndexByteString(s, '!'); i >= 0 {
   215  		s = s[i+1:] // remove address
   216  	}
   217  	if n, _, ok := dtoi(s); ok {
   218  		return n, nil
   219  	}
   220  	return 0, unknownPortError
   221  }
   222  
   223  func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
   224  	lines, err := queryDNS(ctx, name, "cname")
   225  	if err != nil {
   226  		if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
   227  			cname = name + "."
   228  			err = nil
   229  		}
   230  		return
   231  	}
   232  	if len(lines) > 0 {
   233  		if f := getFields(lines[0]); len(f) >= 3 {
   234  			return f[2] + ".", nil
   235  		}
   236  	}
   237  	return "", errors.New("bad response from ndb/dns")
   238  }
   239  
   240  func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
   241  	var target string
   242  	if service == "" && proto == "" {
   243  		target = name
   244  	} else {
   245  		target = "_" + service + "._" + proto + "." + name
   246  	}
   247  	lines, err := queryDNS(ctx, target, "srv")
   248  	if err != nil {
   249  		return
   250  	}
   251  	for _, line := range lines {
   252  		f := getFields(line)
   253  		if len(f) < 6 {
   254  			continue
   255  		}
   256  		port, _, portOk := dtoi(f[4])
   257  		priority, _, priorityOk := dtoi(f[3])
   258  		weight, _, weightOk := dtoi(f[2])
   259  		if !(portOk && priorityOk && weightOk) {
   260  			continue
   261  		}
   262  		addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)})
   263  		cname = absDomainName([]byte(f[0]))
   264  	}
   265  	byPriorityWeight(addrs).sort()
   266  	return
   267  }
   268  
   269  func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
   270  	lines, err := queryDNS(ctx, name, "mx")
   271  	if err != nil {
   272  		return
   273  	}
   274  	for _, line := range lines {
   275  		f := getFields(line)
   276  		if len(f) < 4 {
   277  			continue
   278  		}
   279  		if pref, _, ok := dtoi(f[2]); ok {
   280  			mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)})
   281  		}
   282  	}
   283  	byPref(mx).sort()
   284  	return
   285  }
   286  
   287  func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
   288  	lines, err := queryDNS(ctx, name, "ns")
   289  	if err != nil {
   290  		return
   291  	}
   292  	for _, line := range lines {
   293  		f := getFields(line)
   294  		if len(f) < 3 {
   295  			continue
   296  		}
   297  		ns = append(ns, &NS{absDomainName([]byte(f[2]))})
   298  	}
   299  	return
   300  }
   301  
   302  func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
   303  	lines, err := queryDNS(ctx, name, "txt")
   304  	if err != nil {
   305  		return
   306  	}
   307  	for _, line := range lines {
   308  		if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
   309  			txt = append(txt, absDomainName([]byte(line[i+1:])))
   310  		}
   311  	}
   312  	return
   313  }
   314  
   315  func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
   316  	arpa, err := reverseaddr(addr)
   317  	if err != nil {
   318  		return
   319  	}
   320  	lines, err := queryDNS(ctx, arpa, "ptr")
   321  	if err != nil {
   322  		return
   323  	}
   324  	for _, line := range lines {
   325  		f := getFields(line)
   326  		if len(f) < 3 {
   327  			continue
   328  		}
   329  		name = append(name, absDomainName([]byte(f[2])))
   330  	}
   331  	return
   332  }
   333  
   334  // concurrentThreadsLimit returns the number of threads we permit to
   335  // run concurrently doing DNS lookups.
   336  func concurrentThreadsLimit() int {
   337  	return 500
   338  }
   339  

View as plain text