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