github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/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 func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) { 183 lits, err := r.lookupHost(ctx, host) 184 if err != nil { 185 return 186 } 187 for _, lit := range lits { 188 host, zone := splitHostZone(lit) 189 if ip := ParseIP(host); ip != nil { 190 addr := IPAddr{IP: ip, Zone: zone} 191 addrs = append(addrs, addr) 192 } 193 } 194 return 195 } 196 197 func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) { 198 switch network { 199 case "tcp4", "tcp6": 200 network = "tcp" 201 case "udp4", "udp6": 202 network = "udp" 203 } 204 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service)) 205 if err != nil { 206 return 207 } 208 unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service} 209 if len(lines) == 0 { 210 return 0, unknownPortError 211 } 212 f := getFields(lines[0]) 213 if len(f) < 2 { 214 return 0, unknownPortError 215 } 216 s := f[1] 217 if i := bytealg.IndexByteString(s, '!'); i >= 0 { 218 s = s[i+1:] // remove address 219 } 220 if n, _, ok := dtoi(s); ok { 221 return n, nil 222 } 223 return 0, unknownPortError 224 } 225 226 func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) { 227 lines, err := queryDNS(ctx, name, "cname") 228 if err != nil { 229 if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") { 230 cname = name + "." 231 err = nil 232 } 233 return 234 } 235 if len(lines) > 0 { 236 if f := getFields(lines[0]); len(f) >= 3 { 237 return f[2] + ".", nil 238 } 239 } 240 return "", errors.New("bad response from ndb/dns") 241 } 242 243 func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) { 244 var target string 245 if service == "" && proto == "" { 246 target = name 247 } else { 248 target = "_" + service + "._" + proto + "." + name 249 } 250 lines, err := queryDNS(ctx, target, "srv") 251 if err != nil { 252 return 253 } 254 for _, line := range lines { 255 f := getFields(line) 256 if len(f) < 6 { 257 continue 258 } 259 port, _, portOk := dtoi(f[4]) 260 priority, _, priorityOk := dtoi(f[3]) 261 weight, _, weightOk := dtoi(f[2]) 262 if !(portOk && priorityOk && weightOk) { 263 continue 264 } 265 addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)}) 266 cname = absDomainName([]byte(f[0])) 267 } 268 byPriorityWeight(addrs).sort() 269 return 270 } 271 272 func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) { 273 lines, err := queryDNS(ctx, name, "mx") 274 if err != nil { 275 return 276 } 277 for _, line := range lines { 278 f := getFields(line) 279 if len(f) < 4 { 280 continue 281 } 282 if pref, _, ok := dtoi(f[2]); ok { 283 mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)}) 284 } 285 } 286 byPref(mx).sort() 287 return 288 } 289 290 func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) { 291 lines, err := queryDNS(ctx, name, "ns") 292 if err != nil { 293 return 294 } 295 for _, line := range lines { 296 f := getFields(line) 297 if len(f) < 3 { 298 continue 299 } 300 ns = append(ns, &NS{absDomainName([]byte(f[2]))}) 301 } 302 return 303 } 304 305 func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) { 306 lines, err := queryDNS(ctx, name, "txt") 307 if err != nil { 308 return 309 } 310 for _, line := range lines { 311 if i := bytealg.IndexByteString(line, '\t'); i >= 0 { 312 txt = append(txt, line[i+1:]) 313 } 314 } 315 return 316 } 317 318 func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) { 319 arpa, err := reverseaddr(addr) 320 if err != nil { 321 return 322 } 323 lines, err := queryDNS(ctx, arpa, "ptr") 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 name = append(name, absDomainName([]byte(f[2]))) 333 } 334 return 335 } 336 337 // concurrentThreadsLimit returns the number of threads we permit to 338 // run concurrently doing DNS lookups. 339 func concurrentThreadsLimit() int { 340 return 500 341 }