resolv.go 2.8 KB

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