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 //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
7 // Read system DNS config from /etc/resolv.conf
19 defaultNS
= []string{"127.0.0.1:53", "[::1]:53"}
20 getHostname
= os
.Hostname
// variable for testing
23 type dnsConfig
struct {
24 servers
[]string // server addresses (in host:port form) to use
25 search
[]string // rooted suffixes to append to local name
26 ndots
int // number of dots in name to trigger absolute lookup
27 timeout time
.Duration
// wait before giving up on a query, including retries
28 attempts
int // lost packets before giving up on server
29 rotate
bool // round robin among servers
30 unknownOpt
bool // anything unknown was encountered
31 lookup
[]string // OpenBSD top-level database "lookup" order
32 err error
// any error that occurs during open of resolv.conf
33 mtime time
.Time
// time of resolv.conf modification
34 soffset
uint32 // used by serverOffset
35 singleRequest
bool // use sequential A and AAAA queries instead of parallel queries
36 useTCP
bool // force usage of TCP for DNS resolutions
39 // See resolv.conf(5) on a Linux machine.
40 func dnsReadConfig(filename
string) *dnsConfig
{
43 timeout
: 5 * time
.Second
,
46 file
, err
:= open(filename
)
48 conf
.servers
= defaultNS
49 conf
.search
= dnsDefaultSearch()
54 if fi
, err
:= file
.file
.Stat(); err
== nil {
55 conf
.mtime
= fi
.ModTime()
57 conf
.servers
= defaultNS
58 conf
.search
= dnsDefaultSearch()
62 for line
, ok
:= file
.readLine(); ok
; line
, ok
= file
.readLine() {
63 if len(line
) > 0 && (line
[0] == ';' || line
[0] == '#') {
72 case "nameserver": // add one name server
73 if len(f
) > 1 && len(conf
.servers
) < 3 { // small, but the standard limit
74 // One more check: make sure server name is
75 // just an IP address. Otherwise we need DNS
77 if parseIPv4(f
[1]) != nil {
78 conf
.servers
= append(conf
.servers
, JoinHostPort(f
[1], "53"))
79 } else if ip
, _
:= parseIPv6Zone(f
[1]); ip
!= nil {
80 conf
.servers
= append(conf
.servers
, JoinHostPort(f
[1], "53"))
84 case "domain": // set search path to just this domain
86 conf
.search
= []string{ensureRooted(f
[1])}
89 case "search": // set search path to given servers
90 conf
.search
= make([]string, len(f
)-1)
91 for i
:= 0; i
< len(conf
.search
); i
++ {
92 conf
.search
[i
] = ensureRooted(f
[i
+1])
95 case "options": // magic options
96 for _
, s
:= range f
[1:] {
98 case hasPrefix(s
, "ndots:"):
99 n
, _
, _
:= dtoi(s
[6:])
106 case hasPrefix(s
, "timeout:"):
107 n
, _
, _
:= dtoi(s
[8:])
111 conf
.timeout
= time
.Duration(n
) * time
.Second
112 case hasPrefix(s
, "attempts:"):
113 n
, _
, _
:= dtoi(s
[9:])
120 case s
== "single-request" || s
== "single-request-reopen":
122 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
123 // "By default, glibc performs IPv4 and IPv6 lookups in parallel [...]
124 // This option disables the behavior and makes glibc
125 // perform the IPv6 and IPv4 requests sequentially."
126 conf
.singleRequest
= true
127 case s
== "use-vc" || s
== "usevc" || s
== "tcp":
128 // Linux (use-vc), FreeBSD (usevc) and OpenBSD (tcp) option:
129 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
130 // "Sets RES_USEVC in _res.options.
131 // This option forces the use of TCP for DNS resolutions."
132 // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports
133 // https://man.openbsd.org/resolv.conf.5
136 conf
.unknownOpt
= true
142 // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
143 // "the legal space-separated values are: bind, file, yp"
147 conf
.unknownOpt
= true
150 if len(conf
.servers
) == 0 {
151 conf
.servers
= defaultNS
153 if len(conf
.search
) == 0 {
154 conf
.search
= dnsDefaultSearch()
159 // serverOffset returns an offset that can be used to determine
160 // indices of servers in c.servers when making queries.
161 // When the rotate option is enabled, this offset increases.
162 // Otherwise it is always 0.
163 func (c
*dnsConfig
) serverOffset() uint32 {
165 return atomic
.AddUint32(&c
.soffset
, 1) - 1 // return 0 to start
170 func dnsDefaultSearch() []string {
171 hn
, err
:= getHostname()
176 if i
:= bytealg
.IndexByteString(hn
, '.'); i
>= 0 && i
< len(hn
)-1 {
177 return []string{ensureRooted(hn
[i
+1:])}
182 func hasPrefix(s
, prefix
string) bool {
183 return len(s
) >= len(prefix
) && s
[:len(prefix
)] == prefix
186 func ensureRooted(s
string) string {
187 if len(s
) > 0 && s
[len(s
)-1] == '.' {