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.
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)
22 _
, err
= file
.Seek(0, io
.SeekStart
)
26 _
, err
= file
.WriteString(query
)
30 _
, err
= file
.Seek(0, io
.SeekStart
)
34 buf
:= make([]byte, bufSize
)
36 n
, _
:= file
.Read(buf
)
40 addrs
= append(addrs
, string(buf
[:n
]))
50 ch
:= make(chan ret
, 1)
52 addrs
, err
:= queryAddrs()
53 ch
<- ret
{addrs
: addrs
, err
: err
}
60 return nil, &DNSError
{
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
) {
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
) {
83 if len(ip
) != 0 && !ip
.IsUnspecified() {
86 lines
, err
:= queryCS(ctx
, net
, ips
, itoa(port
))
90 f
:= getFields(lines
[0])
92 return "", "", errors
.New("bad response from ndb/cs")
94 clone
, dest
= f
[0], f
[1]
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.
110 for i
:= 0; i
< len(in
); i
++ {
112 if 'A' <= c
&& c
<= 'Z' {
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)
131 return 0, UnknownNetworkError(name
)
133 f
:= getFields(lines
[0])
135 return 0, UnknownNetworkError(name
)
138 if n
, _
, ok
:= dtoi(s
[byteIndex(s
, '=')+1:]); ok
{
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")
149 if stringsHasSuffix(err
.Error(), "dns failure") {
155 for _
, line
:= range lines
{
161 if i
:= byteIndex(addr
, '!'); i
>= 0 {
162 addr
= addr
[:i
] // remove port
164 if ParseIP(addr
) == nil {
167 // only return unique addresses
168 for _
, a
:= range addrs
{
173 addrs
= append(addrs
, addr
)
178 func (r
*Resolver
) lookupIP(ctx context
.Context
, host
string) (addrs
[]IPAddr
, err error
) {
179 lits
, err
:= r
.lookupHost(ctx
, host
)
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
)
193 func (*Resolver
) lookupPort(ctx context
.Context
, network
, service
string) (port
int, err error
) {
200 lines
, err
:= queryCS(ctx
, network
, "127.0.0.1", toLower(service
))
204 unknownPortError
:= &AddrError
{Err
: "unknown port", Addr
: network
+ "/" + service
}
206 return 0, unknownPortError
208 f
:= getFields(lines
[0])
210 return 0, unknownPortError
213 if i
:= byteIndex(s
, '!'); i
>= 0 {
214 s
= s
[i
+1:] // remove address
216 if n
, _
, ok
:= dtoi(s
); ok
{
219 return 0, unknownPortError
222 func (*Resolver
) lookupCNAME(ctx context
.Context
, name
string) (cname
string, err error
) {
223 lines
, err
:= queryDNS(ctx
, name
, "cname")
225 if stringsHasSuffix(err
.Error(), "dns failure") ||
stringsHasSuffix(err
.Error(), "resource does not exist; negrcode 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
) {
241 if service
== "" && proto
== "" {
244 target
= "_" + service
+ "._" + proto
+ "." + name
246 lines
, err
:= queryDNS(ctx
, target
, "srv")
250 for _
, line
:= range lines
{
255 port
, _
, portOk
:= dtoi(f
[4])
256 priority
, _
, priorityOk
:= dtoi(f
[3])
257 weight
, _
, weightOk
:= dtoi(f
[2])
258 if !(portOk
&& priorityOk
&& weightOk
) {
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()
268 func (*Resolver
) lookupMX(ctx context
.Context
, name
string) (mx
[]*MX
, err error
) {
269 lines
, err
:= queryDNS(ctx
, name
, "mx")
273 for _
, line
:= range lines
{
278 if pref
, _
, ok
:= dtoi(f
[2]); ok
{
279 mx
= append(mx
, &MX
{absDomainName([]byte(f
[3])), uint16(pref
)})
286 func (*Resolver
) lookupNS(ctx context
.Context
, name
string) (ns
[]*NS
, err error
) {
287 lines
, err
:= queryDNS(ctx
, name
, "ns")
291 for _
, line
:= range lines
{
296 ns
= append(ns
, &NS
{absDomainName([]byte(f
[2]))})
301 func (*Resolver
) lookupTXT(ctx context
.Context
, name
string) (txt
[]string, err error
) {
302 lines
, err
:= queryDNS(ctx
, name
, "txt")
306 for _
, line
:= range lines
{
307 if i
:= byteIndex(line
, '\t'); i
>= 0 {
308 txt
= append(txt
, absDomainName([]byte(line
[i
+1:])))
314 func (*Resolver
) lookupAddr(ctx context
.Context
, addr
string) (name
[]string, err error
) {
315 arpa
, err
:= reverseaddr(addr
)
319 lines
, err
:= queryDNS(ctx
, arpa
, "ptr")
323 for _
, line
:= range lines
{
328 name
= append(name
, absDomainName([]byte(f
[2])))
333 // concurrentThreadsLimit returns the number of threads we permit to
334 // run concurrently doing DNS lookups.
335 func concurrentThreadsLimit() int {