resolv_unix.go 2.9 KB

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