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