Source file
src/net/lookup_plan9.go
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "errors"
10 "internal/bytealg"
11 "internal/itoa"
12 "internal/stringslite"
13 "io"
14 "os"
15 )
16
17
18
19
20 const cgoAvailable = true
21
22 func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) {
23 queryAddrs := func() (addrs []string, err error) {
24 file, err := os.OpenFile(filename, os.O_RDWR, 0)
25 if err != nil {
26 return nil, err
27 }
28 defer file.Close()
29
30 _, err = file.Seek(0, io.SeekStart)
31 if err != nil {
32 return nil, err
33 }
34 _, err = file.WriteString(query)
35 if err != nil {
36 return nil, err
37 }
38 _, err = file.Seek(0, io.SeekStart)
39 if err != nil {
40 return nil, err
41 }
42 buf := make([]byte, bufSize)
43 for {
44 n, _ := file.Read(buf)
45 if n <= 0 {
46 break
47 }
48 addrs = append(addrs, string(buf[:n]))
49 }
50 return addrs, nil
51 }
52
53 type ret struct {
54 addrs []string
55 err error
56 }
57
58 ch := make(chan ret, 1)
59 go func() {
60 addrs, err := queryAddrs()
61 ch <- ret{addrs: addrs, err: err}
62 }()
63
64 select {
65 case r := <-ch:
66 return r.addrs, r.err
67 case <-ctx.Done():
68 return nil, &DNSError{
69 Name: query,
70 Err: ctx.Err().Error(),
71 IsTimeout: ctx.Err() == context.DeadlineExceeded,
72 }
73 }
74 }
75
76 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
77 switch net {
78 case "tcp4", "tcp6":
79 net = "tcp"
80 case "udp4", "udp6":
81 net = "udp"
82 }
83 if host == "" {
84 host = "*"
85 }
86 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
87 }
88
89 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
90 ips := "*"
91 if len(ip) != 0 && !ip.IsUnspecified() {
92 ips = ip.String()
93 }
94 lines, err := queryCS(ctx, net, ips, itoa.Itoa(port))
95 if err != nil {
96 return
97 }
98 f := getFields(lines[0])
99 if len(f) < 2 {
100 return "", "", errors.New("bad response from ndb/cs")
101 }
102 clone, dest = f[0], f[1]
103 return
104 }
105
106 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
107 return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
108 }
109
110 func handlePlan9DNSError(err error, name string) error {
111 if stringslite.HasSuffix(err.Error(), "dns: name does not exist") ||
112 stringslite.HasSuffix(err.Error(), "dns: resource does not exist; negrcode 0") ||
113 stringslite.HasSuffix(err.Error(), "dns: resource does not exist; negrcode") ||
114 stringslite.HasSuffix(err.Error(), "dns failure") {
115 err = errNoSuchHost
116 }
117 return newDNSError(err, name, "")
118 }
119
120
121
122
123 func toLower(in string) string {
124 for _, c := range in {
125 if 'A' <= c && c <= 'Z' {
126
127 out := []byte(in)
128 for i := 0; i < len(in); i++ {
129 c := in[i]
130 if 'A' <= c && c <= 'Z' {
131 c += 'a' - 'A'
132 }
133 out[i] = c
134 }
135 return string(out)
136 }
137 }
138 return in
139 }
140
141
142
143 func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
144 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
145 if err != nil {
146 return 0, err
147 }
148 if len(lines) == 0 {
149 return 0, UnknownNetworkError(name)
150 }
151 f := getFields(lines[0])
152 if len(f) < 2 {
153 return 0, UnknownNetworkError(name)
154 }
155 s := f[1]
156 if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok {
157 return n, nil
158 }
159 return 0, UnknownNetworkError(name)
160 }
161
162 func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
163
164
165 lines, err := queryCS(ctx, "net", host, "1")
166 if err != nil {
167 return nil, handlePlan9DNSError(err, host)
168 }
169 loop:
170 for _, line := range lines {
171 f := getFields(line)
172 if len(f) < 2 {
173 continue
174 }
175 addr := f[1]
176 if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
177 addr = addr[:i]
178 }
179 if ParseIP(addr) == nil {
180 continue
181 }
182
183 for _, a := range addrs {
184 if a == addr {
185 continue loop
186 }
187 }
188 addrs = append(addrs, addr)
189 }
190 return
191 }
192
193 func (r *Resolver) lookupIP(ctx context.Context, network, host string) (addrs []IPAddr, err error) {
194 if order, conf := systemConf().hostLookupOrder(r, host); order != hostLookupCgo {
195 return r.goLookupIP(ctx, network, host, order, conf)
196 }
197
198 lits, err := r.lookupHost(ctx, host)
199 if err != nil {
200 return
201 }
202 for _, lit := range lits {
203 host, zone := splitHostZone(lit)
204 if ip := ParseIP(host); ip != nil {
205 addr := IPAddr{IP: ip, Zone: zone}
206 addrs = append(addrs, addr)
207 }
208 }
209 return
210 }
211
212 func (r *Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
213 switch network {
214 case "ip":
215 if p, err := r.lookupPortWithNetwork(ctx, "tcp", "ip", service); err == nil {
216 return p, nil
217 }
218 return r.lookupPortWithNetwork(ctx, "udp", "ip", service)
219 case "tcp", "tcp4", "tcp6":
220 return r.lookupPortWithNetwork(ctx, "tcp", "tcp", service)
221 case "udp", "udp4", "udp6":
222 return r.lookupPortWithNetwork(ctx, "udp", "udp", service)
223 default:
224 return 0, &DNSError{Err: "unknown network", Name: network + "/" + service}
225 }
226 }
227
228 func (*Resolver) lookupPortWithNetwork(ctx context.Context, network, errNetwork, service string) (port int, err error) {
229 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
230 if err != nil {
231 if stringslite.HasSuffix(err.Error(), "can't translate service") {
232 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
233 }
234 return
235 }
236 if len(lines) == 0 {
237 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
238 }
239 f := getFields(lines[0])
240 if len(f) < 2 {
241 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
242 }
243 s := f[1]
244 if i := bytealg.IndexByteString(s, '!'); i >= 0 {
245 s = s[i+1:]
246 }
247 if n, _, ok := dtoi(s); ok {
248 return n, nil
249 }
250 return 0, &DNSError{Err: "unknown port", Name: errNetwork + "/" + service, IsNotFound: true}
251 }
252
253 func (r *Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
254 if order, conf := systemConf().hostLookupOrder(r, name); order != hostLookupCgo {
255 return r.goLookupCNAME(ctx, name, order, conf)
256 }
257
258 lines, err := queryDNS(ctx, name, "cname")
259 if err != nil {
260 if stringslite.HasSuffix(err.Error(), "dns failure") ||
261 stringslite.HasSuffix(err.Error(), "resource does not exist; negrcode 0") ||
262 stringslite.HasSuffix(err.Error(), "resource does not exist; negrcode") {
263 return absDomainName(name), nil
264 }
265 return "", handlePlan9DNSError(err, cname)
266 }
267 if len(lines) > 0 {
268 if f := getFields(lines[0]); len(f) >= 3 {
269 return f[2] + ".", nil
270 }
271 }
272 return "", errors.New("bad response from ndb/dns")
273 }
274
275 func (r *Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
276 if systemConf().mustUseGoResolver(r) {
277 return r.goLookupSRV(ctx, service, proto, name)
278 }
279 var target string
280 if service == "" && proto == "" {
281 target = name
282 } else {
283 target = "_" + service + "._" + proto + "." + name
284 }
285 lines, err := queryDNS(ctx, target, "srv")
286 if err != nil {
287 return "", nil, handlePlan9DNSError(err, name)
288 }
289 for _, line := range lines {
290 f := getFields(line)
291 if len(f) < 6 {
292 continue
293 }
294 port, _, portOk := dtoi(f[4])
295 priority, _, priorityOk := dtoi(f[3])
296 weight, _, weightOk := dtoi(f[2])
297 if !(portOk && priorityOk && weightOk) {
298 continue
299 }
300 addrs = append(addrs, &SRV{absDomainName(f[5]), uint16(port), uint16(priority), uint16(weight)})
301 cname = absDomainName(f[0])
302 }
303 byPriorityWeight(addrs).sort()
304 return
305 }
306
307 func (r *Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
308 if systemConf().mustUseGoResolver(r) {
309 return r.goLookupMX(ctx, name)
310 }
311 lines, err := queryDNS(ctx, name, "mx")
312 if err != nil {
313 return nil, handlePlan9DNSError(err, name)
314 }
315 for _, line := range lines {
316 f := getFields(line)
317 if len(f) < 4 {
318 continue
319 }
320 if pref, _, ok := dtoi(f[2]); ok {
321 mx = append(mx, &MX{absDomainName(f[3]), uint16(pref)})
322 }
323 }
324 byPref(mx).sort()
325 return
326 }
327
328 func (r *Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
329 if systemConf().mustUseGoResolver(r) {
330 return r.goLookupNS(ctx, name)
331 }
332 lines, err := queryDNS(ctx, name, "ns")
333 if err != nil {
334 return nil, handlePlan9DNSError(err, name)
335 }
336 for _, line := range lines {
337 f := getFields(line)
338 if len(f) < 3 {
339 continue
340 }
341 ns = append(ns, &NS{absDomainName(f[2])})
342 }
343 return
344 }
345
346 func (r *Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
347 if systemConf().mustUseGoResolver(r) {
348 return r.goLookupTXT(ctx, name)
349 }
350 lines, err := queryDNS(ctx, name, "txt")
351 if err != nil {
352 return nil, handlePlan9DNSError(err, name)
353 }
354 for _, line := range lines {
355 if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
356 txt = append(txt, line[i+1:])
357 }
358 }
359 return
360 }
361
362 func (r *Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
363 if order, conf := systemConf().addrLookupOrder(r, addr); order != hostLookupCgo {
364 return r.goLookupPTR(ctx, addr, order, conf)
365 }
366 arpa, err := reverseaddr(addr)
367 if err != nil {
368 return
369 }
370 lines, err := queryDNS(ctx, arpa, "ptr")
371 if err != nil {
372 return nil, handlePlan9DNSError(err, addr)
373 }
374 for _, line := range lines {
375 f := getFields(line)
376 if len(f) < 3 {
377 continue
378 }
379 name = append(name, absDomainName(f[2]))
380 }
381 return
382 }
383
384
385
386 func concurrentThreadsLimit() int {
387 return 500
388 }
389
View as plain text