...
Run Format

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

View as plain text