Source file
src/net/lookup_plan9.go
Documentation: net
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "errors"
10 "internal/bytealg"
11 "internal/itoa"
12 "io"
13 "os"
14 )
15
16
17
18
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
126
127
128 func toLower(in string) string {
129 for _, c := range in {
130 if 'A' <= c && c <= 'Z' {
131
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
147
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
169
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]
186 }
187 if ParseIP(addr) == nil {
188 continue
189 }
190
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":
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:]
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
391
392 func concurrentThreadsLimit() int {
393 return 500
394 }
395
View as plain text