]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/lookup_plan9.go
cmd/compile/internal/inline: score call sites exposed by inlines
[gostls13.git] / src / net / lookup_plan9.go
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 // 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
20
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)
24                 if err != nil {
25                         return nil, err
26                 }
27                 defer file.Close()
28
29                 _, err = file.Seek(0, io.SeekStart)
30                 if err != nil {
31                         return nil, err
32                 }
33                 _, err = file.WriteString(query)
34                 if err != nil {
35                         return nil, err
36                 }
37                 _, err = file.Seek(0, io.SeekStart)
38                 if err != nil {
39                         return nil, err
40                 }
41                 buf := make([]byte, bufSize)
42                 for {
43                         n, _ := file.Read(buf)
44                         if n <= 0 {
45                                 break
46                         }
47                         addrs = append(addrs, string(buf[:n]))
48                 }
49                 return addrs, nil
50         }
51
52         type ret struct {
53                 addrs []string
54                 err   error
55         }
56
57         ch := make(chan ret, 1)
58         go func() {
59                 addrs, err := queryAddrs()
60                 ch <- ret{addrs: addrs, err: err}
61         }()
62
63         select {
64         case r := <-ch:
65                 return r.addrs, r.err
66         case <-ctx.Done():
67                 return nil, &DNSError{
68                         Name:      query,
69                         Err:       ctx.Err().Error(),
70                         IsTimeout: ctx.Err() == context.DeadlineExceeded,
71                 }
72         }
73 }
74
75 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
76         switch net {
77         case "tcp4", "tcp6":
78                 net = "tcp"
79         case "udp4", "udp6":
80                 net = "udp"
81         }
82         if host == "" {
83                 host = "*"
84         }
85         return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
86 }
87
88 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
89         ips := "*"
90         if len(ip) != 0 && !ip.IsUnspecified() {
91                 ips = ip.String()
92         }
93         lines, err := queryCS(ctx, net, ips, itoa.Itoa(port))
94         if err != nil {
95                 return
96         }
97         f := getFields(lines[0])
98         if len(f) < 2 {
99                 return "", "", errors.New("bad response from ndb/cs")
100         }
101         clone, dest = f[0], f[1]
102         return
103 }
104
105 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
106         return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
107 }
108
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") {
113                 return &DNSError{
114                         Err:        errNoSuchHost.Error(),
115                         Name:       name,
116                         IsNotFound: true,
117                 }
118         }
119         return &DNSError{
120                 Err:  err.Error(),
121                 Name: name,
122         }
123 }
124
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.
132                         out := []byte(in)
133                         for i := 0; i < len(in); i++ {
134                                 c := in[i]
135                                 if 'A' <= c && c <= 'Z' {
136                                         c += 'a' - 'A'
137                                 }
138                                 out[i] = c
139                         }
140                         return string(out)
141                 }
142         }
143         return in
144 }
145
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)
150         if err != nil {
151                 return 0, err
152         }
153         if len(lines) == 0 {
154                 return 0, UnknownNetworkError(name)
155         }
156         f := getFields(lines[0])
157         if len(f) < 2 {
158                 return 0, UnknownNetworkError(name)
159         }
160         s := f[1]
161         if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok {
162                 return n, nil
163         }
164         return 0, UnknownNetworkError(name)
165 }
166
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")
171         if err != nil {
172                 if stringsHasSuffix(err.Error(), "dns failure") {
173                         return nil, &DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
174                 }
175                 return nil, handlePlan9DNSError(err, host)
176         }
177 loop:
178         for _, line := range lines {
179                 f := getFields(line)
180                 if len(f) < 2 {
181                         continue
182                 }
183                 addr := f[1]
184                 if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
185                         addr = addr[:i] // remove port
186                 }
187                 if ParseIP(addr) == nil {
188                         continue
189                 }
190                 // only return unique addresses
191                 for _, a := range addrs {
192                         if a == addr {
193                                 continue loop
194                         }
195                 }
196                 addrs = append(addrs, addr)
197         }
198         return
199 }
200
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)
204         }
205
206         lits, err := r.lookupHost(ctx, host)
207         if err != nil {
208                 return
209         }
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)
215                 }
216         }
217         return
218 }
219
220 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
221         switch network {
222         case "ip": // no hints
223                 if p, err := r.lookupPortWithNetwork(ctx, "tcp", "ip", service); err == nil {
224                         return p, nil
225                 }
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)
231         default:
232                 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
233         }
234 }
235
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))
238         if err != nil {
239                 if stringsHasSuffix(err.Error(), "can't translate service") {
240                         return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
241                 }
242                 return
243         }
244         if len(lines) == 0 {
245                 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
246         }
247         f := getFields(lines[0])
248         if len(f) < 2 {
249                 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
250         }
251         s := f[1]
252         if i := bytealg.IndexByteString(s, '!'); i >= 0 {
253                 s = s[i+1:] // remove address
254         }
255         if n, _, ok := dtoi(s); ok {
256                 return n, nil
257         }
258         return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
259 }
260
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)
264         }
265
266         lines, err := queryDNS(ctx, name, "cname")
267         if err != nil {
268                 if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
269                         return absDomainName(name), nil
270                 }
271                 return "", handlePlan9DNSError(err, cname)
272         }
273         if len(lines) > 0 {
274                 if f := getFields(lines[0]); len(f) >= 3 {
275                         return f[2] + ".", nil
276                 }
277         }
278         return "", errors.New("bad response from ndb/dns")
279 }
280
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)
284         }
285         var target string
286         if service == "" && proto == "" {
287                 target = name
288         } else {
289                 target = "_" + service + "._" + proto + "." + name
290         }
291         lines, err := queryDNS(ctx, target, "srv")
292         if err != nil {
293                 return "", nil, handlePlan9DNSError(err, name)
294         }
295         for _, line := range lines {
296                 f := getFields(line)
297                 if len(f) < 6 {
298                         continue
299                 }
300                 port, _, portOk := dtoi(f[4])
301                 priority, _, priorityOk := dtoi(f[3])
302                 weight, _, weightOk := dtoi(f[2])
303                 if !(portOk && priorityOk && weightOk) {
304                         continue
305                 }
306                 addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)})
307                 cname = absDomainName(f[0])
308         }
309         byPriorityWeight(addrs).sort()
310         return
311 }
312
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)
316         }
317         lines, err := queryDNS(ctx, name, "mx")
318         if err != nil {
319                 return nil, handlePlan9DNSError(err, name)
320         }
321         for _, line := range lines {
322                 f := getFields(line)
323                 if len(f) < 4 {
324                         continue
325                 }
326                 if pref, _, ok := dtoi(f[2]); ok {
327                         mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)})
328                 }
329         }
330         byPref(mx).sort()
331         return
332 }
333
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)
337         }
338         lines, err := queryDNS(ctx, name, "ns")
339         if err != nil {
340                 return nil, handlePlan9DNSError(err, name)
341         }
342         for _, line := range lines {
343                 f := getFields(line)
344                 if len(f) < 3 {
345                         continue
346                 }
347                 ns = append(ns, &NS{absDomainName(f[2])})
348         }
349         return
350 }
351
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)
355         }
356         lines, err := queryDNS(ctx, name, "txt")
357         if err != nil {
358                 return nil, handlePlan9DNSError(err, name)
359         }
360         for _, line := range lines {
361                 if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
362                         txt = append(txt, line[i+1:])
363                 }
364         }
365         return
366 }
367
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)
371         }
372         arpa, err := reverseaddr(addr)
373         if err != nil {
374                 return
375         }
376         lines, err := queryDNS(ctx, arpa, "ptr")
377         if err != nil {
378                 return nil, handlePlan9DNSError(err, addr)
379         }
380         for _, line := range lines {
381                 f := getFields(line)
382                 if len(f) < 3 {
383                         continue
384                 }
385                 name = append(name, absDomainName(f[2]))
386         }
387         return
388 }
389
390 // concurrentThreadsLimit returns the number of threads we permit to
391 // run concurrently doing DNS lookups.
392 func concurrentThreadsLimit() int {
393         return 500
394 }