testsuite: Skip 90020.c on AIX.
[official-gcc.git] / libgo / go / net / lookup_plan9.go
blob6a2d48eedace46d23e2a40c4f10ab38466a39900
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.
5 package net
7 import (
8 "context"
9 "errors"
10 "internal/bytealg"
11 "io"
12 "os"
15 func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) {
16 queryAddrs := func() (addrs []string, err error) {
17 file, err := os.OpenFile(filename, os.O_RDWR, 0)
18 if err != nil {
19 return nil, err
21 defer file.Close()
23 _, err = file.Seek(0, io.SeekStart)
24 if err != nil {
25 return nil, err
27 _, err = file.WriteString(query)
28 if err != nil {
29 return nil, err
31 _, err = file.Seek(0, io.SeekStart)
32 if err != nil {
33 return nil, err
35 buf := make([]byte, bufSize)
36 for {
37 n, _ := file.Read(buf)
38 if n <= 0 {
39 break
41 addrs = append(addrs, string(buf[:n]))
43 return addrs, nil
46 type ret struct {
47 addrs []string
48 err error
51 ch := make(chan ret, 1)
52 go func() {
53 addrs, err := queryAddrs()
54 ch <- ret{addrs: addrs, err: err}
55 }()
57 select {
58 case r := <-ch:
59 return r.addrs, r.err
60 case <-ctx.Done():
61 return nil, &DNSError{
62 Name: query,
63 Err: ctx.Err().Error(),
64 IsTimeout: ctx.Err() == context.DeadlineExceeded,
69 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
70 switch net {
71 case "tcp4", "tcp6":
72 net = "tcp"
73 case "udp4", "udp6":
74 net = "udp"
76 if host == "" {
77 host = "*"
79 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
82 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
83 ips := "*"
84 if len(ip) != 0 && !ip.IsUnspecified() {
85 ips = ip.String()
87 lines, err := queryCS(ctx, net, ips, itoa(port))
88 if err != nil {
89 return
91 f := getFields(lines[0])
92 if len(f) < 2 {
93 return "", "", errors.New("bad response from ndb/cs")
95 clone, dest = f[0], f[1]
96 return
99 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
100 return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
103 // toLower returns a lower-case version of in. Restricting us to
104 // ASCII is sufficient to handle the IP protocol names and allow
105 // us to not depend on the strings and unicode packages.
106 func toLower(in string) string {
107 for _, c := range in {
108 if 'A' <= c && c <= 'Z' {
109 // Has upper case; need to fix.
110 out := []byte(in)
111 for i := 0; i < len(in); i++ {
112 c := in[i]
113 if 'A' <= c && c <= 'Z' {
114 c += 'a' - 'A'
116 out[i] = c
118 return string(out)
121 return in
124 // lookupProtocol looks up IP protocol name and returns
125 // the corresponding protocol number.
126 func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
127 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
128 if err != nil {
129 return 0, err
131 if len(lines) == 0 {
132 return 0, UnknownNetworkError(name)
134 f := getFields(lines[0])
135 if len(f) < 2 {
136 return 0, UnknownNetworkError(name)
138 s := f[1]
139 if n, _, ok := dtoi(s[bytealg.IndexByteString(s, '=')+1:]); ok {
140 return n, nil
142 return 0, UnknownNetworkError(name)
145 func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
146 // Use netdir/cs instead of netdir/dns because cs knows about
147 // host names in local network (e.g. from /lib/ndb/local)
148 lines, err := queryCS(ctx, "net", host, "1")
149 if err != nil {
150 dnsError := &DNSError{Err: err.Error(), Name: host}
151 if stringsHasSuffix(err.Error(), "dns failure") {
152 dnsError.Err = errNoSuchHost.Error()
153 dnsError.IsNotFound = true
155 return nil, dnsError
157 loop:
158 for _, line := range lines {
159 f := getFields(line)
160 if len(f) < 2 {
161 continue
163 addr := f[1]
164 if i := bytealg.IndexByteString(addr, '!'); i >= 0 {
165 addr = addr[:i] // remove port
167 if ParseIP(addr) == nil {
168 continue
170 // only return unique addresses
171 for _, a := range addrs {
172 if a == addr {
173 continue loop
176 addrs = append(addrs, addr)
178 return
181 func (r *Resolver) lookupIP(ctx context.Context, _, host string) (addrs []IPAddr, err error) {
182 lits, err := r.lookupHost(ctx, host)
183 if err != nil {
184 return
186 for _, lit := range lits {
187 host, zone := splitHostZone(lit)
188 if ip := ParseIP(host); ip != nil {
189 addr := IPAddr{IP: ip, Zone: zone}
190 addrs = append(addrs, addr)
193 return
196 func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
197 switch network {
198 case "tcp4", "tcp6":
199 network = "tcp"
200 case "udp4", "udp6":
201 network = "udp"
203 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
204 if err != nil {
205 return
207 unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service}
208 if len(lines) == 0 {
209 return 0, unknownPortError
211 f := getFields(lines[0])
212 if len(f) < 2 {
213 return 0, unknownPortError
215 s := f[1]
216 if i := bytealg.IndexByteString(s, '!'); i >= 0 {
217 s = s[i+1:] // remove address
219 if n, _, ok := dtoi(s); ok {
220 return n, nil
222 return 0, unknownPortError
225 func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
226 lines, err := queryDNS(ctx, name, "cname")
227 if err != nil {
228 if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
229 cname = name + "."
230 err = nil
232 return
234 if len(lines) > 0 {
235 if f := getFields(lines[0]); len(f) >= 3 {
236 return f[2] + ".", nil
239 return "", errors.New("bad response from ndb/dns")
242 func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
243 var target string
244 if service == "" && proto == "" {
245 target = name
246 } else {
247 target = "_" + service + "._" + proto + "." + name
249 lines, err := queryDNS(ctx, target, "srv")
250 if err != nil {
251 return
253 for _, line := range lines {
254 f := getFields(line)
255 if len(f) < 6 {
256 continue
258 port, _, portOk := dtoi(f[4])
259 priority, _, priorityOk := dtoi(f[3])
260 weight, _, weightOk := dtoi(f[2])
261 if !(portOk && priorityOk && weightOk) {
262 continue
264 addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)})
265 cname = absDomainName([]byte(f[0]))
267 byPriorityWeight(addrs).sort()
268 return
271 func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
272 lines, err := queryDNS(ctx, name, "mx")
273 if err != nil {
274 return
276 for _, line := range lines {
277 f := getFields(line)
278 if len(f) < 4 {
279 continue
281 if pref, _, ok := dtoi(f[2]); ok {
282 mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)})
285 byPref(mx).sort()
286 return
289 func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
290 lines, err := queryDNS(ctx, name, "ns")
291 if err != nil {
292 return
294 for _, line := range lines {
295 f := getFields(line)
296 if len(f) < 3 {
297 continue
299 ns = append(ns, &NS{absDomainName([]byte(f[2]))})
301 return
304 func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
305 lines, err := queryDNS(ctx, name, "txt")
306 if err != nil {
307 return
309 for _, line := range lines {
310 if i := bytealg.IndexByteString(line, '\t'); i >= 0 {
311 txt = append(txt, absDomainName([]byte(line[i+1:])))
314 return
317 func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
318 arpa, err := reverseaddr(addr)
319 if err != nil {
320 return
322 lines, err := queryDNS(ctx, arpa, "ptr")
323 if err != nil {
324 return
326 for _, line := range lines {
327 f := getFields(line)
328 if len(f) < 3 {
329 continue
331 name = append(name, absDomainName([]byte(f[2])))
333 return
336 // concurrentThreadsLimit returns the number of threads we permit to
337 // run concurrently doing DNS lookups.
338 func concurrentThreadsLimit() int {
339 return 500