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.
16 // cgoAvailable set to true to indicate that the cgo resolver
17 // is available on Plan 9. Note that on Plan 9 the cgo resolver
18 // does not actually use cgo.
19 const cgoAvailable = true
21 func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) {
22 queryAddrs := func() (addrs []string, err error) {
23 file, err := os.OpenFile(filename, os.O_RDWR, 0)
29 _, err = file.Seek(0, io.SeekStart)
33 _, err = file.WriteString(query)
37 _, err = file.Seek(0, io.SeekStart)
41 buf := make([]byte, bufSize)
43 n, _ := file.Read(buf)
47 addrs = append(addrs, string(buf[:n]))
57 ch := make(chan ret, 1)
59 addrs, err := queryAddrs()
60 ch <- ret{addrs: addrs, err: err}
67 return nil, &DNSError{
69 Err: ctx.Err().Error(),
70 IsTimeout: ctx.Err() == context.DeadlineExceeded,
75 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
85 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
88 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
90 if len(ip) != 0 && !ip.IsUnspecified() {
93 lines, err := queryCS(ctx, net, ips, itoa.Itoa(port))
97 f := getFields(lines[0])
99 return "", "", errors.New("bad response from ndb/cs")
101 clone, dest = f[0], f[1]
105 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
106 return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
109 func handlePlan9DNSError(err error, name string) error {
110 if stringsHasSuffix(err.Error(), "dns: name does not exist") ||
111 stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode 0") ||
112 stringsHasSuffix(err.Error(), "dns: resource does not exist; negrcode") {
114 Err: errNoSuchHost.Error(),
125 // toLower returns a lower-case version of in. Restricting us to
126 // ASCII is sufficient to handle the IP protocol names and allow
127 // us to not depend on the strings and unicode packages.
128 func toLower(in string) string {
129 for _, c := range in {
130 if 'A' <= c && c <= 'Z' {
131 // Has upper case; need to fix.
133 for i := 0; i < len(in); i++ {
135 if 'A' <= c && c <= 'Z' {
146 // lookupProtocol looks up IP protocol name and returns
147 // the corresponding protocol number.
148 func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
149 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
154 return 0, UnknownNetworkError(name)
156 f := getFields(lines[0])
158 return 0, UnknownNetworkError(name)
161 if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok {
164 return 0, UnknownNetworkError(name)
167 func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
168 // Use netdir/cs instead of netdir/dns because cs knows about
169 // host names in local network (e.g. from /lib/ndb/local)
170 lines, err := queryCS(ctx, "net", host, "1")
172 if stringsHasSuffix(err.Error(), "dns failure") {
173 return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
175 return nil, handlePlan9DNSError(err, host)
178 for _, line := range lines {
184 if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
185 addr = addr[:i] // remove port
187 if ParseIP(addr) == nil {
190 // only return unique addresses
191 for _, a := range addrs {
196 addrs = append(addrs, addr)
201 func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
202 if order, conf := systemConf().hostLookupOrder(r, host); order != hostLookupCgo {
203 return r.goLookupIP(ctx, network, host, order, conf)
206 lits, err := r.lookupHost(ctx, host)
210 for _, lit := range lits {
211 host, zone := splitHostZone(lit)
212 if ip := ParseIP(host); ip != nil {
213 addr := IPAddr{IP: ip, Zone: zone}
214 addrs = append(addrs, addr)
220 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
222 case "ip": // no hints
223 if p, err := r.lookupPortWithNetwork(ctx, "tcp", "ip", service); err == nil {
226 return r.lookupPortWithNetwork(ctx, "udp", "ip", service)
227 case "tcp", "tcp4", "tcp6":
228 return r.lookupPortWithNetwork(ctx, "tcp", "tcp", service)
229 case "udp", "udp4", "udp6":
230 return r.lookupPortWithNetwork(ctx, "udp", "udp", service)
232 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
236 func (*Resolver) lookupPortWithNetwork(ctx context.Context, network, errNetwork, service string) (port int, err error) {
237 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
239 if stringsHasSuffix(err.Error(), "can't translate service") {
240 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
245 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
247 f := getFields(lines[0])
249 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
252 if i := bytealg.IndexByteString(s, '!'); i >= 0 {
253 s = s[i+1:] // remove address
255 if n, _, ok := dtoi(s); ok {
258 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
261 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
262 if order, conf := systemConf().hostLookupOrder(r, name); order != hostLookupCgo {
263 return r.goLookupCNAME(ctx, name, order, conf)
266 lines, err := queryDNS(ctx, name, "cname")
268 if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
269 return absDomainName(name), nil
271 return "", handlePlan9DNSError(err, cname)
274 if f := getFields(lines[0]); len(f) >= 3 {
275 return f[2] + ".", nil
278 return "", errors.New("bad response from ndb/dns")
281 func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
282 if systemConf().mustUseGoResolver(r) {
283 return r.goLookupSRV(ctx, service, proto, name)
286 if service == "" && proto == "" {
289 target = "_" + service + "._" + proto + "." + name
291 lines, err := queryDNS(ctx, target, "srv")
293 return "", nil, handlePlan9DNSError(err, name)
295 for _, line := range lines {
300 port, _, portOk := dtoi(f[4])
301 priority, _, priorityOk := dtoi(f[3])
302 weight, _, weightOk := dtoi(f[2])
303 if !(portOk && priorityOk && weightOk) {
306 addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)})
307 cname = absDomainName(f[0])
309 byPriorityWeight(addrs).sort()
313 func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
314 if systemConf().mustUseGoResolver(r) {
315 return r.goLookupMX(ctx, name)
317 lines, err := queryDNS(ctx, name, "mx")
319 return nil, handlePlan9DNSError(err, name)
321 for _, line := range lines {
326 if pref, _, ok := dtoi(f[2]); ok {
327 mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)})
334 func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
335 if systemConf().mustUseGoResolver(r) {
336 return r.goLookupNS(ctx, name)
338 lines, err := queryDNS(ctx, name, "ns")
340 return nil, handlePlan9DNSError(err, name)
342 for _, line := range lines {
347 ns = append(ns, &NS{absDomainName(f[2])})
352 func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
353 if systemConf().mustUseGoResolver(r) {
354 return r.goLookupTXT(ctx, name)
356 lines, err := queryDNS(ctx, name, "txt")
358 return nil, handlePlan9DNSError(err, name)
360 for _, line := range lines {
361 if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
362 txt = append(txt, line[i+1:])
368 func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
369 if order, conf := systemConf().addrLookupOrder(r, addr); order != hostLookupCgo {
370 return r.goLookupPTR(ctx, addr, order, conf)
372 arpa, err := reverseaddr(addr)
376 lines, err := queryDNS(ctx, arpa, "ptr")
378 return nil, handlePlan9DNSError(err, addr)
380 for _, line := range lines {
385 name = append(name, absDomainName(f[2]))
390 // concurrentThreadsLimit returns the number of threads we permit to
391 // run concurrently doing DNS lookups.
392 func concurrentThreadsLimit() int {