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