libgo: update to Go 1.11
[official-gcc.git] / libgo / go / net / lookup_plan9.go
blob5547f0b0eeb3a19b06d0f76dc76057505c1e731f
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 "io"
11 "os"
14 func query(ctx context.Context, filename, query string, bufSize int) (addrs []string, err error) {
15 queryAddrs := func() (addrs []string, err error) {
16 file, err := os.OpenFile(filename, os.O_RDWR, 0)
17 if err != nil {
18 return nil, err
20 defer file.Close()
22 _, err = file.Seek(0, io.SeekStart)
23 if err != nil {
24 return nil, err
26 _, err = file.WriteString(query)
27 if err != nil {
28 return nil, err
30 _, err = file.Seek(0, io.SeekStart)
31 if err != nil {
32 return nil, err
34 buf := make([]byte, bufSize)
35 for {
36 n, _ := file.Read(buf)
37 if n <= 0 {
38 break
40 addrs = append(addrs, string(buf[:n]))
42 return addrs, nil
45 type ret struct {
46 addrs []string
47 err error
50 ch := make(chan ret, 1)
51 go func() {
52 addrs, err := queryAddrs()
53 ch <- ret{addrs: addrs, err: err}
54 }()
56 select {
57 case r := <-ch:
58 return r.addrs, r.err
59 case <-ctx.Done():
60 return nil, &DNSError{
61 Name: query,
62 Err: ctx.Err().Error(),
63 IsTimeout: ctx.Err() == context.DeadlineExceeded,
68 func queryCS(ctx context.Context, net, host, service string) (res []string, err error) {
69 switch net {
70 case "tcp4", "tcp6":
71 net = "tcp"
72 case "udp4", "udp6":
73 net = "udp"
75 if host == "" {
76 host = "*"
78 return query(ctx, netdir+"/cs", net+"!"+host+"!"+service, 128)
81 func queryCS1(ctx context.Context, net string, ip IP, port int) (clone, dest string, err error) {
82 ips := "*"
83 if len(ip) != 0 && !ip.IsUnspecified() {
84 ips = ip.String()
86 lines, err := queryCS(ctx, net, ips, itoa(port))
87 if err != nil {
88 return
90 f := getFields(lines[0])
91 if len(f) < 2 {
92 return "", "", errors.New("bad response from ndb/cs")
94 clone, dest = f[0], f[1]
95 return
98 func queryDNS(ctx context.Context, addr string, typ string) (res []string, err error) {
99 return query(ctx, netdir+"/dns", addr+" "+typ, 1024)
102 // toLower returns a lower-case version of in. Restricting us to
103 // ASCII is sufficient to handle the IP protocol names and allow
104 // us to not depend on the strings and unicode packages.
105 func toLower(in string) string {
106 for _, c := range in {
107 if 'A' <= c && c <= 'Z' {
108 // Has upper case; need to fix.
109 out := []byte(in)
110 for i := 0; i < len(in); i++ {
111 c := in[i]
112 if 'A' <= c && c <= 'Z' {
113 c += 'a' - 'A'
115 out[i] = c
117 return string(out)
120 return in
123 // lookupProtocol looks up IP protocol name and returns
124 // the corresponding protocol number.
125 func lookupProtocol(ctx context.Context, name string) (proto int, err error) {
126 lines, err := query(ctx, netdir+"/cs", "!protocol="+toLower(name), 128)
127 if err != nil {
128 return 0, err
130 if len(lines) == 0 {
131 return 0, UnknownNetworkError(name)
133 f := getFields(lines[0])
134 if len(f) < 2 {
135 return 0, UnknownNetworkError(name)
137 s := f[1]
138 if n, _, ok := dtoi(s[byteIndex(s, '=')+1:]); ok {
139 return n, nil
141 return 0, UnknownNetworkError(name)
144 func (*Resolver) lookupHost(ctx context.Context, host string) (addrs []string, err error) {
145 // Use netdir/cs instead of netdir/dns because cs knows about
146 // host names in local network (e.g. from /lib/ndb/local)
147 lines, err := queryCS(ctx, "net", host, "1")
148 if err != nil {
149 if stringsHasSuffix(err.Error(), "dns failure") {
150 err = errNoSuchHost
152 return
154 loop:
155 for _, line := range lines {
156 f := getFields(line)
157 if len(f) < 2 {
158 continue
160 addr := f[1]
161 if i := byteIndex(addr, '!'); i >= 0 {
162 addr = addr[:i] // remove port
164 if ParseIP(addr) == nil {
165 continue
167 // only return unique addresses
168 for _, a := range addrs {
169 if a == addr {
170 continue loop
173 addrs = append(addrs, addr)
175 return
178 func (r *Resolver) lookupIP(ctx context.Context, host string) (addrs []IPAddr, err error) {
179 lits, err := r.lookupHost(ctx, host)
180 if err != nil {
181 return
183 for _, lit := range lits {
184 host, zone := splitHostZone(lit)
185 if ip := ParseIP(host); ip != nil {
186 addr := IPAddr{IP: ip, Zone: zone}
187 addrs = append(addrs, addr)
190 return
193 func (*Resolver) lookupPort(ctx context.Context, network, service string) (port int, err error) {
194 switch network {
195 case "tcp4", "tcp6":
196 network = "tcp"
197 case "udp4", "udp6":
198 network = "udp"
200 lines, err := queryCS(ctx, network, "127.0.0.1", toLower(service))
201 if err != nil {
202 return
204 unknownPortError := &AddrError{Err: "unknown port", Addr: network + "/" + service}
205 if len(lines) == 0 {
206 return 0, unknownPortError
208 f := getFields(lines[0])
209 if len(f) < 2 {
210 return 0, unknownPortError
212 s := f[1]
213 if i := byteIndex(s, '!'); i >= 0 {
214 s = s[i+1:] // remove address
216 if n, _, ok := dtoi(s); ok {
217 return n, nil
219 return 0, unknownPortError
222 func (*Resolver) lookupCNAME(ctx context.Context, name string) (cname string, err error) {
223 lines, err := queryDNS(ctx, name, "cname")
224 if err != nil {
225 if stringsHasSuffix(err.Error(), "dns failure") || stringsHasSuffix(err.Error(), "resource does not exist; negrcode 0") {
226 cname = name + "."
227 err = nil
229 return
231 if len(lines) > 0 {
232 if f := getFields(lines[0]); len(f) >= 3 {
233 return f[2] + ".", nil
236 return "", errors.New("bad response from ndb/dns")
239 func (*Resolver) lookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) {
240 var target string
241 if service == "" && proto == "" {
242 target = name
243 } else {
244 target = "_" + service + "._" + proto + "." + name
246 lines, err := queryDNS(ctx, target, "srv")
247 if err != nil {
248 return
250 for _, line := range lines {
251 f := getFields(line)
252 if len(f) < 6 {
253 continue
255 port, _, portOk := dtoi(f[4])
256 priority, _, priorityOk := dtoi(f[3])
257 weight, _, weightOk := dtoi(f[2])
258 if !(portOk && priorityOk && weightOk) {
259 continue
261 addrs = append(addrs, &SRV{absDomainName([]byte(f[5])), uint16(port), uint16(priority), uint16(weight)})
262 cname = absDomainName([]byte(f[0]))
264 byPriorityWeight(addrs).sort()
265 return
268 func (*Resolver) lookupMX(ctx context.Context, name string) (mx []*MX, err error) {
269 lines, err := queryDNS(ctx, name, "mx")
270 if err != nil {
271 return
273 for _, line := range lines {
274 f := getFields(line)
275 if len(f) < 4 {
276 continue
278 if pref, _, ok := dtoi(f[2]); ok {
279 mx = append(mx, &MX{absDomainName([]byte(f[3])), uint16(pref)})
282 byPref(mx).sort()
283 return
286 func (*Resolver) lookupNS(ctx context.Context, name string) (ns []*NS, err error) {
287 lines, err := queryDNS(ctx, name, "ns")
288 if err != nil {
289 return
291 for _, line := range lines {
292 f := getFields(line)
293 if len(f) < 3 {
294 continue
296 ns = append(ns, &NS{absDomainName([]byte(f[2]))})
298 return
301 func (*Resolver) lookupTXT(ctx context.Context, name string) (txt []string, err error) {
302 lines, err := queryDNS(ctx, name, "txt")
303 if err != nil {
304 return
306 for _, line := range lines {
307 if i := byteIndex(line, '\t'); i >= 0 {
308 txt = append(txt, absDomainName([]byte(line[i+1:])))
311 return
314 func (*Resolver) lookupAddr(ctx context.Context, addr string) (name []string, err error) {
315 arpa, err := reverseaddr(addr)
316 if err != nil {
317 return
319 lines, err := queryDNS(ctx, arpa, "ptr")
320 if err != nil {
321 return
323 for _, line := range lines {
324 f := getFields(line)
325 if len(f) < 3 {
326 continue
328 name = append(name, absDomainName([]byte(f[2])))
330 return
333 // concurrentThreadsLimit returns the number of threads we permit to
334 // run concurrently doing DNS lookups.
335 func concurrentThreadsLimit() int {
336 return 500