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