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 5package net 6 7import ( 8 "context" 9 "errors" 10 "internal/bytealg" 11 "internal/itoa" 12 "internal/stringslite" 13 "io" 14 "os" 15) 16 17// cgoAvailable set to true to indicate that the cgo resolver 18// is available on Plan 9. Note that on Plan 9 the cgo resolver 19// does not actually use cgo. 20const cgoAvailable = true 21 22func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) { 23 queryAddrs := func() (addrs []string, err error) { 24 file, err := os.OpenFile(filename, os.O_RDWR, 0) 25 if err != nil { 26 return nil, err 27 } 28 defer file.Close() 29 30 _, err = file.Seek(0, io.SeekStart) 31 if err != nil { 32 return nil, err 33 } 34 _, err = file.WriteString(query) 35 if err != nil { 36 return nil, err 37 } 38 _, err = file.Seek(0, io.SeekStart) 39 if err != nil { 40 return nil, err 41 } 42 buf := make([]byte, bufSize) 43 for { 44 n, _ := file.Read(buf) 45 if n <= 0 { 46 break 47 } 48 addrs = append(addrs, string(buf[:n])) 49 } 50 return addrs, nil 51 } 52 53 type ret struct { 54 addrs []string 55 err error 56 } 57 58 ch := make(chan ret, 1) 59 go func() { 60 addrs, err := queryAddrs() 61 ch <- ret{addrs: addrs, err: err} 62 }() 63 64 select { 65 case r := <-ch: 66 return r.addrs, r.err 67 case <-ctx.Done(): 68 return nil, &DNSError{ 69 Name: query, 70 Err: ctx.Err().Error(), 71 IsTimeout: ctx.Err() == context.DeadlineExceeded, 72 } 73 } 74} 75 76func queryCS(ctx context.Context, net, host, service string) (res []string, err error) { 77 switch net { 78 case "tcp4", "tcp6": 79 net = "tcp" 80 case "udp4", "udp6": 81 net = "udp" 82 } 83 if host == "" { 84 host = "*" 85 } 86 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128) 87} 88 89func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) { 90 ips := "*" 91 if len(ip) != 0 && !ip.IsUnspecified() { 92 ips = ip.String() 93 } 94 lines, err := queryCS(ctx, net, ips, itoa.Itoa(port)) 95 if err != nil { 96 return 97 } 98 f := getFields(lines[0]) 99 if len(f) < 2 { 100 return "", "", errors.New("bad response from ndb/cs") 101 } 102 clone, dest = f[0], f[1] 103 return 104} 105 106func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) { 107 return query(ctx, netdir+"/dns", addr+" "+typ, 1024) 108} 109 110func handlePlan9DNSError(err error, name string) error { 111 if stringslite.HasSuffix(err.Error(), "dns: name does not exist") || 112 stringslite.HasSuffix(err.Error(), "dns: resource does not exist; negrcode 0") || 113 stringslite.HasSuffix(err.Error(), "dns: resource does not exist; negrcode") || 114 stringslite.HasSuffix(err.Error(), "dns failure") { 115 err = errNoSuchHost 116 } 117 return newDNSError(err, name, "") 118} 119 120// toLower returns a lower-case version of in. Restricting us to 121// ASCII is sufficient to handle the IP protocol names and allow 122// us to not depend on the strings and unicode packages. 123func toLower(in string) string { 124 for _, c := range in { 125 if 'A' <= c && c <= 'Z' { 126 // Has upper case; need to fix. 127 out := []byte(in) 128 for i := 0; i < len(in); i++ { 129 c := in[i] 130 if 'A' <= c && c <= 'Z' { 131 c += 'a' - 'A' 132 } 133 out[i] = c 134 } 135 return string(out) 136 } 137 } 138 return in 139} 140 141// lookupProtocol looks up IP protocol name and returns 142// the corresponding protocol number. 143func lookupProtocol(ctx context.Context, name string) (proto int, err error) { 144 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128) 145 if err != nil { 146 return 0, err 147 } 148 if len(lines) == 0 { 149 return 0, UnknownNetworkError(name) 150 } 151 f := getFields(lines[0]) 152 if len(f) < 2 { 153 return 0, UnknownNetworkError(name) 154 } 155 s := f[1] 156 if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok { 157 return n, nil 158 } 159 return 0, UnknownNetworkError(name) 160} 161 162func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) { 163 // Use netdir/cs instead of netdir/dns because cs knows about 164 // host names in local network (e.g. from /lib/ndb/local) 165 lines, err := queryCS(ctx, "net", host, "1") 166 if err != nil { 167 return nil, handlePlan9DNSError(err, host) 168 } 169loop: 170 for _, line := range lines { 171 f := getFields(line) 172 if len(f) < 2 { 173 continue 174 } 175 addr := f[1] 176 if i := bytealg.IndexByteString(addr, '!'); i >= 0 { 177 addr = addr[:i] // remove port 178 } 179 if ParseIP(addr) == nil { 180 continue 181 } 182 // only return unique addresses 183 for _, a := range addrs { 184 if a == addr { 185 continue loop 186 } 187 } 188 addrs = append(addrs, addr) 189 } 190 return 191} 192 193func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) { 194 if order, conf := systemConf().hostLookupOrder(r, host); order != hostLookupCgo { 195 return r.goLookupIP(ctx, network, host, order, conf) 196 } 197 198 lits, err := r.lookupHost(ctx, host) 199 if err != nil { 200 return 201 } 202 for _, lit := range lits { 203 host, zone := splitHostZone(lit) 204 if ip := ParseIP(host); ip != nil { 205 addr := IPAddr{IP: ip, Zone: zone} 206 addrs = append(addrs, addr) 207 } 208 } 209 return 210} 211 212func (r *Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) { 213 switch network { 214 case "ip": // no hints 215 if p, err := r.lookupPortWithNetwork(ctx, "tcp", "ip", service); err == nil { 216 return p, nil 217 } 218 return r.lookupPortWithNetwork(ctx, "udp", "ip", service) 219 case "tcp", "tcp4", "tcp6": 220 return r.lookupPortWithNetwork(ctx, "tcp", "tcp", service) 221 case "udp", "udp4", "udp6": 222 return r.lookupPortWithNetwork(ctx, "udp", "udp", service) 223 default: 224 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service} 225 } 226} 227 228func (*Resolver) lookupPortWithNetwork(ctx context.Context, network, errNetwork, service string) (port int, err error) { 229 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service)) 230 if err != nil { 231 if stringslite.HasSuffix(err.Error(), "can't translate service") { 232 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true} 233 } 234 return 235 } 236 if len(lines) == 0 { 237 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true} 238 } 239 f := getFields(lines[0]) 240 if len(f) < 2 { 241 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true} 242 } 243 s := f[1] 244 if i := bytealg.IndexByteString(s, '!'); i >= 0 { 245 s = s[i+1:] // remove address 246 } 247 if n, _, ok := dtoi(s); ok { 248 return n, nil 249 } 250 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true} 251} 252 253func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { 254 if order, conf := systemConf().hostLookupOrder(r, name); order != hostLookupCgo { 255 return r.goLookupCNAME(ctx, name, order, conf) 256 } 257 258 lines, err := queryDNS(ctx, name, "cname") 259 if err != nil { 260 if stringslite.HasSuffix(err.Error(), "dns failure") || 261 stringslite.HasSuffix(err.Error(), "resource does not exist; negrcode 0") || 262 stringslite.HasSuffix(err.Error(), "resource does not exist; negrcode") { 263 return absDomainName(name), nil 264 } 265 return "", handlePlan9DNSError(err, cname) 266 } 267 if len(lines) > 0 { 268 if f := getFields(lines[0]); len(f) >= 3 { 269 return f[2] + ".", nil 270 } 271 } 272 return "", errors.New("bad response from ndb/dns") 273} 274 275func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { 276 if systemConf().mustUseGoResolver(r) { 277 return r.goLookupSRV(ctx, service, proto, name) 278 } 279 var target string 280 if service == "" && proto == "" { 281 target = name 282 } else { 283 target = "_" + service + "._" + proto + "." + name 284 } 285 lines, err := queryDNS(ctx, target, "srv") 286 if err != nil { 287 return "", nil, handlePlan9DNSError(err, name) 288 } 289 for _, line := range lines { 290 f := getFields(line) 291 if len(f) < 6 { 292 continue 293 } 294 port, _, portOk := dtoi(f[4]) 295 priority, _, priorityOk := dtoi(f[3]) 296 weight, _, weightOk := dtoi(f[2]) 297 if !(portOk && priorityOk && weightOk) { 298 continue 299 } 300 addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)}) 301 cname = absDomainName(f[0]) 302 } 303 byPriorityWeight(addrs).sort() 304 return 305} 306 307func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { 308 if systemConf().mustUseGoResolver(r) { 309 return r.goLookupMX(ctx, name) 310 } 311 lines, err := queryDNS(ctx, name, "mx") 312 if err != nil { 313 return nil, handlePlan9DNSError(err, name) 314 } 315 for _, line := range lines { 316 f := getFields(line) 317 if len(f) < 4 { 318 continue 319 } 320 if pref, _, ok := dtoi(f[2]); ok { 321 mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)}) 322 } 323 } 324 byPref(mx).sort() 325 return 326} 327 328func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { 329 if systemConf().mustUseGoResolver(r) { 330 return r.goLookupNS(ctx, name) 331 } 332 lines, err := queryDNS(ctx, name, "ns") 333 if err != nil { 334 return nil, handlePlan9DNSError(err, name) 335 } 336 for _, line := range lines { 337 f := getFields(line) 338 if len(f) < 3 { 339 continue 340 } 341 ns = append(ns, &NS{absDomainName(f[2])}) 342 } 343 return 344} 345 346func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { 347 if systemConf().mustUseGoResolver(r) { 348 return r.goLookupTXT(ctx, name) 349 } 350 lines, err := queryDNS(ctx, name, "txt") 351 if err != nil { 352 return nil, handlePlan9DNSError(err, name) 353 } 354 for _, line := range lines { 355 if i := bytealg.IndexByteString(line, '\t'); i >= 0 { 356 txt = append(txt, line[i+1:]) 357 } 358 } 359 return 360} 361 362func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { 363 if order, conf := systemConf().addrLookupOrder(r, addr); order != hostLookupCgo { 364 return r.goLookupPTR(ctx, addr, order, conf) 365 } 366 arpa, err := reverseaddr(addr) 367 if err != nil { 368 return 369 } 370 lines, err := queryDNS(ctx, arpa, "ptr") 371 if err != nil { 372 return nil, handlePlan9DNSError(err, addr) 373 } 374 for _, line := range lines { 375 f := getFields(line) 376 if len(f) < 3 { 377 continue 378 } 379 name = append(name, absDomainName(f[2])) 380 } 381 return 382} 383 384// concurrentThreadsLimit returns the number of threads we permit to 385// run concurrently doing DNS lookups. 386func concurrentThreadsLimit() int { 387 return 500 388} 389