1 // Copyright 2009 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 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
7 // Read system DNS config from /etc/resolv.conf
18 defaultNS
= []string{"127.0.0.1:53", "[::1]:53"}
19 getHostname
= os
.Hostname
// variable for testing
22 type dnsConfig
struct {
23 servers
[]string // server addresses (in host:port form) to use
24 search
[]string // rooted suffixes to append to local name
25 ndots
int // number of dots in name to trigger absolute lookup
26 timeout time
.Duration
// wait before giving up on a query, including retries
27 attempts
int // lost packets before giving up on server
28 rotate
bool // round robin among servers
29 unknownOpt
bool // anything unknown was encountered
30 lookup
[]string // OpenBSD top-level database "lookup" order
31 err error
// any error that occurs during open of resolv.conf
32 mtime time
.Time
// time of resolv.conf modification
33 soffset
uint32 // used by serverOffset
36 // See resolv.conf(5) on a Linux machine.
37 func dnsReadConfig(filename
string) *dnsConfig
{
40 timeout
: 5 * time
.Second
,
43 file
, err
:= open(filename
)
45 conf
.servers
= defaultNS
46 conf
.search
= dnsDefaultSearch()
51 if fi
, err
:= file
.file
.Stat(); err
== nil {
52 conf
.mtime
= fi
.ModTime()
54 conf
.servers
= defaultNS
55 conf
.search
= dnsDefaultSearch()
59 for line
, ok
:= file
.readLine(); ok
; line
, ok
= file
.readLine() {
60 if len(line
) > 0 && (line
[0] == ';' || line
[0] == '#') {
69 case "nameserver": // add one name server
70 if len(f
) > 1 && len(conf
.servers
) < 3 { // small, but the standard limit
71 // One more check: make sure server name is
72 // just an IP address. Otherwise we need DNS
74 if parseIPv4(f
[1]) != nil {
75 conf
.servers
= append(conf
.servers
, JoinHostPort(f
[1], "53"))
76 } else if ip
, _
:= parseIPv6(f
[1], true); ip
!= nil {
77 conf
.servers
= append(conf
.servers
, JoinHostPort(f
[1], "53"))
81 case "domain": // set search path to just this domain
83 conf
.search
= []string{ensureRooted(f
[1])}
86 case "search": // set search path to given servers
87 conf
.search
= make([]string, len(f
)-1)
88 for i
:= 0; i
< len(conf
.search
); i
++ {
89 conf
.search
[i
] = ensureRooted(f
[i
+1])
92 case "options": // magic options
93 for _
, s
:= range f
[1:] {
95 case hasPrefix(s
, "ndots:"):
96 n
, _
, _
:= dtoi(s
[6:])
103 case hasPrefix(s
, "timeout:"):
104 n
, _
, _
:= dtoi(s
[8:])
108 conf
.timeout
= time
.Duration(n
) * time
.Second
109 case hasPrefix(s
, "attempts:"):
110 n
, _
, _
:= dtoi(s
[9:])
118 conf
.unknownOpt
= true
124 // http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
125 // "the legal space-separated values are: bind, file, yp"
129 conf
.unknownOpt
= true
132 if len(conf
.servers
) == 0 {
133 conf
.servers
= defaultNS
135 if len(conf
.search
) == 0 {
136 conf
.search
= dnsDefaultSearch()
141 // serverOffset returns an offset that can be used to determine
142 // indices of servers in c.servers when making queries.
143 // When the rotate option is enabled, this offset increases.
144 // Otherwise it is always 0.
145 func (c
*dnsConfig
) serverOffset() uint32 {
147 return atomic
.AddUint32(&c
.soffset
, 1) - 1 // return 0 to start
152 func dnsDefaultSearch() []string {
153 hn
, err
:= getHostname()
158 if i
:= byteIndex(hn
, '.'); i
>= 0 && i
< len(hn
)-1 {
159 return []string{ensureRooted(hn
[i
+1:])}
164 func hasPrefix(s
, prefix
string) bool {
165 return len(s
) >= len(prefix
) && s
[:len(prefix
)] == prefix
168 func ensureRooted(s
string) string {
169 if len(s
) > 0 && s
[len(s
)-1] == '.' {