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