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.
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)
23 _
, err
= file
.Seek(0, io
.SeekStart
)
27 _
, err
= file
.WriteString(query
)
31 _
, err
= file
.Seek(0, io
.SeekStart
)
35 buf
:= make([]byte, bufSize
)
37 n
, _
:= file
.Read(buf
)
41 addrs
= append(addrs
, string(buf
[:n
]))
51 ch
:= make(chan ret
, 1)
53 addrs
, err
:= queryAddrs()
54 ch
<- ret
{addrs
: addrs
, err
: err
}
61 return nil, &DNSError
{
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
) {
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
) {
84 if len(ip
) != 0 && !ip
.IsUnspecified() {
87 lines
, err
:= queryCS(ctx
, net
, ips
, itoa(port
))
91 f
:= getFields(lines
[0])
93 return "", "", errors
.New("bad response from ndb/cs")
95 clone
, dest
= f
[0], f
[1]
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.
111 for i
:= 0; i
< len(in
); i
++ {
113 if 'A' <= c
&& c
<= 'Z' {
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)
132 return 0, UnknownNetworkError(name
)
134 f
:= getFields(lines
[0])
136 return 0, UnknownNetworkError(name
)
139 if n
, _
, ok
:= dtoi(s
[bytealg
.IndexByteString(s
, '=')+1:]); ok
{
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")
150 dnsError
:= &DNSError
{Err
: err
.Error(), Name
: host
}
151 if stringsHasSuffix(err
.Error(), "dns failure") {
152 dnsError
.Err
= errNoSuchHost
.Error()
153 dnsError
.IsNotFound
= true
158 for _
, line
:= range lines
{
164 if i
:= bytealg
.IndexByteString(addr
, '!'); i
>= 0 {
165 addr
= addr
[:i
] // remove port
167 if ParseIP(addr
) == nil {
170 // only return unique addresses
171 for _
, a
:= range addrs
{
176 addrs
= append(addrs
, addr
)
181 func (r
*Resolver
) lookupIP(ctx context
.Context
, _
, host
string) (addrs
[]IPAddr
, err error
) {
182 lits
, err
:= r
.lookupHost(ctx
, host
)
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
)
196 func (*Resolver
) lookupPort(ctx context
.Context
, network
, service
string) (port
int, err error
) {
203 lines
, err
:= queryCS(ctx
, network
, "127.0.0.1", toLower(service
))
207 unknownPortError
:= &AddrError
{Err
: "unknown port", Addr
: network
+ "/" + service
}
209 return 0, unknownPortError
211 f
:= getFields(lines
[0])
213 return 0, unknownPortError
216 if i
:= bytealg
.IndexByteString(s
, '!'); i
>= 0 {
217 s
= s
[i
+1:] // remove address
219 if n
, _
, ok
:= dtoi(s
); ok
{
222 return 0, unknownPortError
225 func (*Resolver
) lookupCNAME(ctx context
.Context
, name
string) (cname
string, err error
) {
226 lines
, err
:= queryDNS(ctx
, name
, "cname")
228 if stringsHasSuffix(err
.Error(), "dns failure") ||
stringsHasSuffix(err
.Error(), "resource does not exist; negrcode 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
) {
244 if service
== "" && proto
== "" {
247 target
= "_" + service
+ "._" + proto
+ "." + name
249 lines
, err
:= queryDNS(ctx
, target
, "srv")
253 for _
, line
:= range lines
{
258 port
, _
, portOk
:= dtoi(f
[4])
259 priority
, _
, priorityOk
:= dtoi(f
[3])
260 weight
, _
, weightOk
:= dtoi(f
[2])
261 if !(portOk
&& priorityOk
&& weightOk
) {
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()
271 func (*Resolver
) lookupMX(ctx context
.Context
, name
string) (mx
[]*MX
, err error
) {
272 lines
, err
:= queryDNS(ctx
, name
, "mx")
276 for _
, line
:= range lines
{
281 if pref
, _
, ok
:= dtoi(f
[2]); ok
{
282 mx
= append(mx
, &MX
{absDomainName([]byte(f
[3])), uint16(pref
)})
289 func (*Resolver
) lookupNS(ctx context
.Context
, name
string) (ns
[]*NS
, err error
) {
290 lines
, err
:= queryDNS(ctx
, name
, "ns")
294 for _
, line
:= range lines
{
299 ns
= append(ns
, &NS
{absDomainName([]byte(f
[2]))})
304 func (*Resolver
) lookupTXT(ctx context
.Context
, name
string) (txt
[]string, err error
) {
305 lines
, err
:= queryDNS(ctx
, name
, "txt")
309 for _
, line
:= range lines
{
310 if i
:= bytealg
.IndexByteString(line
, '\t'); i
>= 0 {
311 txt
= append(txt
, absDomainName([]byte(line
[i
+1:])))
317 func (*Resolver
) lookupAddr(ctx context
.Context
, addr
string) (name
[]string, err error
) {
318 arpa
, err
:= reverseaddr(addr
)
322 lines
, err
:= queryDNS(ctx
, arpa
, "ptr")
326 for _
, line
:= range lines
{
331 name
= append(name
, absDomainName([]byte(f
[2])))
336 // concurrentThreadsLimit returns the number of threads we permit to
337 // run concurrently doing DNS lookups.
338 func concurrentThreadsLimit() int {