dnsconfig_unix.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
  5. // Read system DNS config from /etc/resolv.conf
  6. package net
  7. import (
  8. "internal/bytealg"
  9. "os"
  10. "sync/atomic"
  11. "time"
  12. )
  13. var (
  14. defaultNS = []string{"127.0.0.1:53", "[::1]:53"}
  15. getHostname = os.Hostname // variable for testing
  16. )
  17. type dnsConfig struct {
  18. servers []string // server addresses (in host:port form) to use
  19. search []string // rooted suffixes to append to local name
  20. ndots int // number of dots in name to trigger absolute lookup
  21. timeout time.Duration // wait before giving up on a query, including retries
  22. attempts int // lost packets before giving up on server
  23. rotate bool // round robin among servers
  24. unknownOpt bool // anything unknown was encountered
  25. lookup []string // OpenBSD top-level database "lookup" order
  26. err error // any error that occurs during open of resolv.conf
  27. mtime time.Time // time of resolv.conf modification
  28. soffset uint32 // used by serverOffset
  29. singleRequest bool // use sequential A and AAAA queries instead of parallel queries
  30. useTCP bool // force usage of TCP for DNS resolutions
  31. }
  32. // See resolv.conf(5) on a Linux machine.
  33. func dnsReadConfig(filename string) *dnsConfig {
  34. conf := &dnsConfig{
  35. ndots: 1,
  36. timeout: 5 * time.Second,
  37. attempts: 2,
  38. }
  39. file, err := open(filename)
  40. if err != nil {
  41. conf.servers = defaultNS
  42. conf.search = dnsDefaultSearch()
  43. conf.err = err
  44. return conf
  45. }
  46. defer file.close()
  47. if fi, err := file.file.Stat(); err == nil {
  48. conf.mtime = fi.ModTime()
  49. } else {
  50. conf.servers = defaultNS
  51. conf.search = dnsDefaultSearch()
  52. conf.err = err
  53. return conf
  54. }
  55. for line, ok := file.readLine(); ok; line, ok = file.readLine() {
  56. if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
  57. // comment.
  58. continue
  59. }
  60. f := getFields(line)
  61. if len(f) < 1 {
  62. continue
  63. }
  64. switch f[0] {
  65. case "nameserver": // add one name server
  66. if len(f) > 1 && len(conf.servers) < 3 { // small, but the standard limit
  67. // One more check: make sure server name is
  68. // just an IP address. Otherwise we need DNS
  69. // to look it up.
  70. if parseIPv4(f[1]) != nil {
  71. conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
  72. } else if ip, _ := parseIPv6Zone(f[1]); ip != nil {
  73. conf.servers = append(conf.servers, JoinHostPort(f[1], "53"))
  74. }
  75. }
  76. case "domain": // set search path to just this domain
  77. if len(f) > 1 {
  78. conf.search = []string{ensureRooted(f[1])}
  79. }
  80. case "search": // set search path to given servers
  81. conf.search = make([]string, len(f)-1)
  82. for i := 0; i < len(conf.search); i++ {
  83. conf.search[i] = ensureRooted(f[i+1])
  84. }
  85. case "options": // magic options
  86. for _, s := range f[1:] {
  87. switch {
  88. case hasPrefix(s, "ndots:"):
  89. n, _, _ := dtoi(s[6:])
  90. if n < 0 {
  91. n = 0
  92. } else if n > 15 {
  93. n = 15
  94. }
  95. conf.ndots = n
  96. case hasPrefix(s, "timeout:"):
  97. n, _, _ := dtoi(s[8:])
  98. if n < 1 {
  99. n = 1
  100. }
  101. conf.timeout = time.Duration(n) * time.Second
  102. case hasPrefix(s, "attempts:"):
  103. n, _, _ := dtoi(s[9:])
  104. if n < 1 {
  105. n = 1
  106. }
  107. conf.attempts = n
  108. case s == "rotate":
  109. conf.rotate = true
  110. case s == "single-request" || s == "single-request-reopen":
  111. // Linux option:
  112. // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
  113. // "By default, glibc performs IPv4 and IPv6 lookups in parallel [...]
  114. // This option disables the behavior and makes glibc
  115. // perform the IPv6 and IPv4 requests sequentially."
  116. conf.singleRequest = true
  117. case s == "use-vc" || s == "usevc" || s == "tcp":
  118. // Linux (use-vc), FreeBSD (usevc) and OpenBSD (tcp) option:
  119. // http://man7.org/linux/man-pages/man5/resolv.conf.5.html
  120. // "Sets RES_USEVC in _res.options.
  121. // This option forces the use of TCP for DNS resolutions."
  122. // https://www.freebsd.org/cgi/man.cgi?query=resolv.conf&sektion=5&manpath=freebsd-release-ports
  123. // https://man.openbsd.org/resolv.conf.5
  124. conf.useTCP = true
  125. default:
  126. conf.unknownOpt = true
  127. }
  128. }
  129. case "lookup":
  130. // OpenBSD option:
  131. // https://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man5/resolv.conf.5
  132. // "the legal space-separated values are: bind, file, yp"
  133. conf.lookup = f[1:]
  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. // serverOffset returns an offset that can be used to determine
  147. // indices of servers in c.servers when making queries.
  148. // When the rotate option is enabled, this offset increases.
  149. // Otherwise it is always 0.
  150. func (c *dnsConfig) serverOffset() uint32 {
  151. if c.rotate {
  152. return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
  153. }
  154. return 0
  155. }
  156. func dnsDefaultSearch() []string {
  157. hn, err := getHostname()
  158. if err != nil {
  159. // best effort
  160. return nil
  161. }
  162. if i := bytealg.IndexByteString(hn, '.'); i >= 0 && i < len(hn)-1 {
  163. return []string{ensureRooted(hn[i+1:])}
  164. }
  165. return nil
  166. }
  167. func hasPrefix(s, prefix string) bool {
  168. return len(s) >= len(prefix) && s[:len(prefix)] == prefix
  169. }
  170. func ensureRooted(s string) string {
  171. if len(s) > 0 && s[len(s)-1] == '.' {
  172. return s
  173. }
  174. return s + "."
  175. }