resolv_unix.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. //go:build !windows
  2. package local
  3. import (
  4. "bufio"
  5. "net"
  6. "net/netip"
  7. "os"
  8. "strings"
  9. "time"
  10. _ "unsafe"
  11. )
  12. func dnsReadConfig(name string) *dnsConfig {
  13. conf := &dnsConfig{
  14. ndots: 1,
  15. timeout: 5 * time.Second,
  16. attempts: 2,
  17. }
  18. file, err := os.Open(name)
  19. if err != nil {
  20. conf.servers = defaultNS
  21. conf.search = dnsDefaultSearch()
  22. conf.err = err
  23. return conf
  24. }
  25. defer file.Close()
  26. fi, err := file.Stat()
  27. if err == nil {
  28. conf.mtime = fi.ModTime()
  29. } else {
  30. conf.servers = defaultNS
  31. conf.search = dnsDefaultSearch()
  32. conf.err = err
  33. return conf
  34. }
  35. reader := bufio.NewReader(file)
  36. var (
  37. prefix []byte
  38. line []byte
  39. isPrefix bool
  40. )
  41. for {
  42. line, isPrefix, err = reader.ReadLine()
  43. if err != nil {
  44. break
  45. }
  46. if isPrefix {
  47. prefix = append(prefix, line...)
  48. continue
  49. } else if len(prefix) > 0 {
  50. line = append(prefix, line...)
  51. prefix = nil
  52. }
  53. if len(line) > 0 && (line[0] == ';' || line[0] == '#') {
  54. continue
  55. }
  56. f := strings.Fields(string(line))
  57. if len(f) < 1 {
  58. continue
  59. }
  60. switch f[0] {
  61. case "nameserver":
  62. if len(f) > 1 && len(conf.servers) < 3 {
  63. if _, err := netip.ParseAddr(f[1]); err == nil {
  64. conf.servers = append(conf.servers, net.JoinHostPort(f[1], "53"))
  65. }
  66. }
  67. case "domain":
  68. if len(f) > 1 {
  69. conf.search = []string{ensureRooted(f[1])}
  70. }
  71. case "search":
  72. conf.search = make([]string, 0, len(f)-1)
  73. for i := 1; i < len(f); i++ {
  74. name := ensureRooted(f[i])
  75. if name == "." {
  76. continue
  77. }
  78. conf.search = append(conf.search, name)
  79. }
  80. case "options":
  81. for _, s := range f[1:] {
  82. switch {
  83. case strings.HasPrefix(s, "ndots:"):
  84. n, _, _ := dtoi(s[6:])
  85. if n < 0 {
  86. n = 0
  87. } else if n > 15 {
  88. n = 15
  89. }
  90. conf.ndots = n
  91. case strings.HasPrefix(s, "timeout:"):
  92. n, _, _ := dtoi(s[8:])
  93. if n < 1 {
  94. n = 1
  95. }
  96. conf.timeout = time.Duration(n) * time.Second
  97. case strings.HasPrefix(s, "attempts:"):
  98. n, _, _ := dtoi(s[9:])
  99. if n < 1 {
  100. n = 1
  101. }
  102. conf.attempts = n
  103. case s == "rotate":
  104. conf.rotate = true
  105. case s == "single-request" || s == "single-request-reopen":
  106. conf.singleRequest = true
  107. case s == "use-vc" || s == "usevc" || s == "tcp":
  108. conf.useTCP = true
  109. case s == "trust-ad":
  110. conf.trustAD = true
  111. case s == "edns0":
  112. case s == "no-reload":
  113. conf.noReload = true
  114. default:
  115. conf.unknownOpt = true
  116. }
  117. }
  118. case "lookup":
  119. conf.lookup = f[1:]
  120. default:
  121. conf.unknownOpt = true
  122. }
  123. }
  124. if len(conf.servers) == 0 {
  125. conf.servers = defaultNS
  126. }
  127. if len(conf.search) == 0 {
  128. conf.search = dnsDefaultSearch()
  129. }
  130. return conf
  131. }
  132. //go:linkname defaultNS net.defaultNS
  133. var defaultNS []string
  134. func dnsDefaultSearch() []string {
  135. hn, err := os.Hostname()
  136. if err != nil {
  137. return nil
  138. }
  139. if i := strings.IndexRune(hn, '.'); i >= 0 && i < len(hn)-1 {
  140. return []string{ensureRooted(hn[i+1:])}
  141. }
  142. return nil
  143. }
  144. func ensureRooted(s string) string {
  145. if len(s) > 0 && s[len(s)-1] == '.' {
  146. return s
  147. }
  148. return s + "."
  149. }
  150. const big = 0xFFFFFF
  151. func dtoi(s string) (n int, i int, ok bool) {
  152. n = 0
  153. for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
  154. n = n*10 + int(s[i]-'0')
  155. if n >= big {
  156. return big, i, false
  157. }
  158. }
  159. if i == 0 {
  160. return 0, 0, false
  161. }
  162. return n, i, true
  163. }