resolv.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package local
  2. import (
  3. "os"
  4. "runtime"
  5. "strings"
  6. "sync"
  7. "sync/atomic"
  8. "time"
  9. )
  10. const (
  11. // net.maxDNSPacketSize
  12. maxDNSPacketSize = 1232
  13. )
  14. type resolverConfig struct {
  15. initOnce sync.Once
  16. ch chan struct{}
  17. lastChecked time.Time
  18. dnsConfig atomic.Pointer[dnsConfig]
  19. }
  20. var resolvConf resolverConfig
  21. func getSystemDNSConfig() *dnsConfig {
  22. resolvConf.tryUpdate("/etc/resolv.conf")
  23. return resolvConf.dnsConfig.Load()
  24. }
  25. func (conf *resolverConfig) init() {
  26. conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
  27. conf.lastChecked = time.Now()
  28. conf.ch = make(chan struct{}, 1)
  29. }
  30. func (conf *resolverConfig) tryUpdate(name string) {
  31. conf.initOnce.Do(conf.init)
  32. if conf.dnsConfig.Load().noReload {
  33. return
  34. }
  35. if !conf.tryAcquireSema() {
  36. return
  37. }
  38. defer conf.releaseSema()
  39. now := time.Now()
  40. if conf.lastChecked.After(now.Add(-5 * time.Second)) {
  41. return
  42. }
  43. conf.lastChecked = now
  44. if runtime.GOOS != "windows" {
  45. var mtime time.Time
  46. if fi, err := os.Stat(name); err == nil {
  47. mtime = fi.ModTime()
  48. }
  49. if mtime.Equal(conf.dnsConfig.Load().mtime) {
  50. return
  51. }
  52. }
  53. dnsConf := dnsReadConfig(name)
  54. conf.dnsConfig.Store(dnsConf)
  55. }
  56. func (conf *resolverConfig) tryAcquireSema() bool {
  57. select {
  58. case conf.ch <- struct{}{}:
  59. return true
  60. default:
  61. return false
  62. }
  63. }
  64. func (conf *resolverConfig) releaseSema() {
  65. <-conf.ch
  66. }
  67. type dnsConfig struct {
  68. servers []string
  69. search []string
  70. ndots int
  71. timeout time.Duration
  72. attempts int
  73. rotate bool
  74. unknownOpt bool
  75. lookup []string
  76. err error
  77. mtime time.Time
  78. soffset uint32
  79. singleRequest bool
  80. useTCP bool
  81. trustAD bool
  82. noReload bool
  83. }
  84. func (c *dnsConfig) serverOffset() uint32 {
  85. if c.rotate {
  86. return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
  87. }
  88. return 0
  89. }
  90. func (conf *dnsConfig) nameList(name string) []string {
  91. l := len(name)
  92. rooted := l > 0 && name[l-1] == '.'
  93. if l > 254 || l == 254 && !rooted {
  94. return nil
  95. }
  96. if rooted {
  97. if avoidDNS(name) {
  98. return nil
  99. }
  100. return []string{name}
  101. }
  102. hasNdots := strings.Count(name, ".") >= conf.ndots
  103. name += "."
  104. // l++
  105. names := make([]string, 0, 1+len(conf.search))
  106. if hasNdots && !avoidDNS(name) {
  107. names = append(names, name)
  108. }
  109. for _, suffix := range conf.search {
  110. fqdn := name + suffix
  111. if !avoidDNS(fqdn) && len(fqdn) <= 254 {
  112. names = append(names, fqdn)
  113. }
  114. }
  115. if !hasNdots && !avoidDNS(name) {
  116. names = append(names, name)
  117. }
  118. return names
  119. }
  120. func avoidDNS(name string) bool {
  121. if name == "" {
  122. return true
  123. }
  124. if name[len(name)-1] == '.' {
  125. name = name[:len(name)-1]
  126. }
  127. return strings.HasSuffix(name, ".onion")
  128. }