Home | History | Annotate | Download | only in 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) (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") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
    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 }
    308