dnsconfig_unix.mx raw
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.
4
5 //go:build !windows
6
7 // Read system DNS config from /etc/resolv.conf
8
9 package net
10
11 import (
12 "internal/bytealg"
13 "internal/stringslite"
14 "net/netip"
15 "time"
16 )
17
18 // See resolv.conf(5) on a Linux machine.
19 func dnsReadConfig(filename string) *dnsConfig {
20 conf := &dnsConfig{
21 ndots: 1,
22 timeout: 5 * time.Second,
23 attempts: 2,
24 }
25 file, err := open(filename)
26 if err != nil {
27 conf.servers = defaultNS
28 conf.search = dnsDefaultSearch()
29 conf.err = err
30 return conf
31 }
32 defer file.close()
33 if fi, err := file.file.Stat(); err == nil {
34 conf.mtime = fi.ModTime()
35 } else {
36 conf.servers = defaultNS
37 conf.search = dnsDefaultSearch()
38 conf.err = err
39 return conf
40 }
41 for line, ok := file.readLine(); ok; line, ok = file.readLine() {
42 if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
43 // comment.
44 continue
45 }
46 f := getFields(line)
47 if len(f) < 1 {
48 continue
49 }
50 switch f[0] {
51 case "nameserver": // add one name server
52 if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit
53 // One more check: make sure server name is
54 // just an IP address. Otherwise we need DNS
55 // to look it up.
56 if _, err := netip.ParseAddr(f[1]); err == nil {
57 conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
58 }
59 }
60
61 case "domain": // set search path to just this domain
62 if len(f) > 1 {
63 conf.search = [][]byte{ensureRooted(f[1])}
64 }
65
66 case "search": // set search path to given servers
67 conf.search = [][]byte{:0:len(f)-1}
68 for i := 1; i < len(f); i++ {
69 name := ensureRooted(f[i])
70 if name == "." {
71 continue
72 }
73 conf.search = append(conf.search, name)
74 }
75
76 case "options": // magic options
77 for _, s := range f[1:] {
78 switch {
79 case stringslite.HasPrefix(s, "ndots:"):
80 n, _, _ := dtoi(s[6:])
81 if n < 0 {
82 n = 0
83 } else if n > 15 {
84 n = 15
85 }
86 conf.ndots = n
87 case stringslite.HasPrefix(s, "timeout:"):
88 n, _, _ := dtoi(s[8:])
89 if n < 1 {
90 n = 1
91 }
92 conf.timeout = time.Duration(n) * time.Second
93 case stringslite.HasPrefix(s, "attempts:"):
94 n, _, _ := dtoi(s[9:])
95 if n < 1 {
96 n = 1
97 }
98 conf.attempts = n
99 case s == "rotate":
100 conf.rotate = true
101 case s == "single-request" || s == "single-request-reopen":
102 // Linux option:
103 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
104 // "By default, glibc performs IPv4 and IPv6 lookups in parallel [...]
105 // This option disables the behavior and makes glibc
106 // perform the IPv6 and IPv4 requests sequentially."
107 conf.singleRequest = true
108 case s == "use-vc" || s == "usevc" || s == "tcp":
109 // Linux (use-vc), FreeBSD (usevc) and OpenBSD (tcp) option:
110 // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
111 // "Sets RES_USEVC in _res.options.
112 // This option forces the use of TCP for DNS resolutions."
113 // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports
114 // https://man.openbsd.org/resolv.conf.5
115 conf.useTCP = true
116 case s == "trust-ad":
117 conf.trustAD = true
118 case s == "edns0":
119 // We use EDNS by default.
120 // Ignore this option.
121 case s == "no-reload":
122 conf.noReload = true
123 default:
124 conf.unknownOpt = true
125 }
126 }
127
128 case "lookup":
129 // OpenBSD option:
130 // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
131 // "the legal space-separated values are: bind, file, yp"
132 conf.lookup = f[1:]
133
134 default:
135 conf.unknownOpt = true
136 }
137 }
138 if len(conf.servers) == 0 {
139 conf.servers = defaultNS
140 }
141 if len(conf.search) == 0 {
142 conf.search = dnsDefaultSearch()
143 }
144 return conf
145 }
146
147 func dnsDefaultSearch() [][]byte {
148 hn, err := getHostname()
149 if err != nil {
150 // best effort
151 return nil
152 }
153 if i := bytealg.IndexByteString(hn, '.'); i >= 0 && i < len(hn)-1 {
154 return [][]byte{ensureRooted(hn[i+1:])}
155 }
156 return nil
157 }
158
159 func ensureRooted(s string) string {
160 if len(s) > 0 && s[len(s)-1] == '.' {
161 return s
162 }
163 return s + "."
164 }
165