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