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