mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-03-01 20:39:07 +00:00
146 lines
2.6 KiB
Go
146 lines
2.6 KiB
Go
package local
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
// net.maxDNSPacketSize
|
|
maxDNSPacketSize = 1232
|
|
)
|
|
|
|
type resolverConfig struct {
|
|
initOnce sync.Once
|
|
ch chan struct{}
|
|
lastChecked time.Time
|
|
dnsConfig atomic.Pointer[dnsConfig]
|
|
}
|
|
|
|
var resolvConf resolverConfig
|
|
|
|
func getSystemDNSConfig() *dnsConfig {
|
|
resolvConf.tryUpdate("/etc/resolv.conf")
|
|
return resolvConf.dnsConfig.Load()
|
|
}
|
|
|
|
func (conf *resolverConfig) init() {
|
|
conf.dnsConfig.Store(dnsReadConfig("/etc/resolv.conf"))
|
|
conf.lastChecked = time.Now()
|
|
conf.ch = make(chan struct{}, 1)
|
|
}
|
|
|
|
func (conf *resolverConfig) tryUpdate(name string) {
|
|
conf.initOnce.Do(conf.init)
|
|
|
|
if conf.dnsConfig.Load().noReload {
|
|
return
|
|
}
|
|
if !conf.tryAcquireSema() {
|
|
return
|
|
}
|
|
defer conf.releaseSema()
|
|
|
|
now := time.Now()
|
|
if conf.lastChecked.After(now.Add(-5 * time.Second)) {
|
|
return
|
|
}
|
|
conf.lastChecked = now
|
|
if runtime.GOOS != "windows" {
|
|
var mtime time.Time
|
|
if fi, err := os.Stat(name); err == nil {
|
|
mtime = fi.ModTime()
|
|
}
|
|
if mtime.Equal(conf.dnsConfig.Load().mtime) {
|
|
return
|
|
}
|
|
}
|
|
dnsConf := dnsReadConfig(name)
|
|
conf.dnsConfig.Store(dnsConf)
|
|
}
|
|
|
|
func (conf *resolverConfig) tryAcquireSema() bool {
|
|
select {
|
|
case conf.ch <- struct{}{}:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (conf *resolverConfig) releaseSema() {
|
|
<-conf.ch
|
|
}
|
|
|
|
type dnsConfig struct {
|
|
servers []string
|
|
search []string
|
|
ndots int
|
|
timeout time.Duration
|
|
attempts int
|
|
rotate bool
|
|
unknownOpt bool
|
|
lookup []string
|
|
err error
|
|
mtime time.Time
|
|
soffset uint32
|
|
singleRequest bool
|
|
useTCP bool
|
|
trustAD bool
|
|
noReload bool
|
|
}
|
|
|
|
func (c *dnsConfig) serverOffset() uint32 {
|
|
if c.rotate {
|
|
return atomic.AddUint32(&c.soffset, 1) - 1 // return 0 to start
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (conf *dnsConfig) nameList(name string) []string {
|
|
l := len(name)
|
|
rooted := l > 0 && name[l-1] == '.'
|
|
if l > 254 || l == 254 && !rooted {
|
|
return nil
|
|
}
|
|
|
|
if rooted {
|
|
if avoidDNS(name) {
|
|
return nil
|
|
}
|
|
return []string{name}
|
|
}
|
|
|
|
hasNdots := strings.Count(name, ".") >= conf.ndots
|
|
name += "."
|
|
// l++
|
|
|
|
names := make([]string, 0, 1+len(conf.search))
|
|
if hasNdots && !avoidDNS(name) {
|
|
names = append(names, name)
|
|
}
|
|
for _, suffix := range conf.search {
|
|
fqdn := name + suffix
|
|
if !avoidDNS(fqdn) && len(fqdn) <= 254 {
|
|
names = append(names, fqdn)
|
|
}
|
|
}
|
|
if !hasNdots && !avoidDNS(name) {
|
|
names = append(names, name)
|
|
}
|
|
return names
|
|
}
|
|
|
|
func avoidDNS(name string) bool {
|
|
if name == "" {
|
|
return true
|
|
}
|
|
if name[len(name)-1] == '.' {
|
|
name = name[:len(name)-1]
|
|
}
|
|
return strings.HasSuffix(name, ".onion")
|
|
}
|