From 431d52f2500f413450752d496c2013080fe065fa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 25 Oct 2023 19:20:44 +0800 Subject: [PATCH] chore: system resolver can autoupdate --- dns/system.go | 116 ++++++++++++++++++++++++++++++++++++++++++++------ dns/util.go | 11 +---- 2 files changed, 104 insertions(+), 23 deletions(-) diff --git a/dns/system.go b/dns/system.go index f5ab0efb..20282929 100644 --- a/dns/system.go +++ b/dns/system.go @@ -1,23 +1,113 @@ package dns import ( + "context" + "fmt" "net" + "strings" + "sync" + "time" + + "github.com/Dreamacro/clash/log" + + D "github.com/miekg/dns" + "golang.org/x/exp/slices" ) -func loadSystemResolver() (clients []dnsClient, err error) { - nameservers, err := dnsReadConfig() +const ( + SystemDnsFlushTime = 5 * time.Minute + SystemDnsDeleteTimes = 12 // 12*5 = 60min +) + +type systemDnsClient struct { + disableTimes uint32 + dnsClient +} + +type systemClient struct { + mu sync.Mutex + dnsClients map[string]*systemDnsClient + lastFlush time.Time +} + +func (c *systemClient) getDnsClients() ([]dnsClient, error) { + c.mu.Lock() + defer c.mu.Unlock() + var err error + if time.Since(c.lastFlush) > SystemDnsFlushTime { + var nameservers []string + if nameservers, err = dnsReadConfig(); err == nil { + log.Debugln("[DNS] system dns update to %s", nameservers) + for _, addr := range nameservers { + if _, ok := c.dnsClients[addr]; !ok { + clients := transform( + []NameServer{{ + Addr: net.JoinHostPort(addr, "53"), + Net: "udp", + }}, + nil, + ) + if len(clients) > 0 { + c.dnsClients[addr] = &systemDnsClient{ + disableTimes: 0, + dnsClient: clients[0], + } + } + } + } + available := 0 + for nameserver, sdc := range c.dnsClients { + if slices.Contains(nameservers, nameserver) { + sdc.disableTimes = 0 // enable + available++ + } else { + if sdc.disableTimes > SystemDnsDeleteTimes { + delete(c.dnsClients, nameserver) // drop too old dnsClient + } else { + sdc.disableTimes++ + } + } + } + if available > 0 { + c.lastFlush = time.Now() + } + } + } + dnsClients := make([]dnsClient, 0, len(c.dnsClients)) + for _, sdc := range c.dnsClients { + if sdc.disableTimes == 0 { + dnsClients = append(dnsClients, sdc.dnsClient) + } + } + if len(dnsClients) > 0 { + return dnsClients, nil + } + return nil, err +} + +func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { + dnsClients, err := c.getDnsClients() if err != nil { return } - if len(nameservers) == 0 { - return - } - servers := make([]NameServer, 0, len(nameservers)) - for _, addr := range nameservers { - servers = append(servers, NameServer{ - Addr: net.JoinHostPort(addr, "53"), - Net: "udp", - }) - } - return transform(servers, nil), nil + msg, _, err = batchExchange(ctx, dnsClients, m) + return +} + +// Address implements dnsClient +func (c *systemClient) Address() string { + dnsClients, _ := c.getDnsClients() + addrs := make([]string, 0, len(dnsClients)) + for _, c := range dnsClients { + addrs = append(addrs, c.Address()) + } + return fmt.Sprintf("system(%s)", strings.Join(addrs, ",")) +} + +var _ dnsClient = (*systemClient)(nil) + +func newSystemClient() *systemClient { + return &systemClient{ + dnsClients: map[string]*systemDnsClient{}, + } } diff --git a/dns/util.go b/dns/util.go index 6c196517..e8bdfd0b 100644 --- a/dns/util.go +++ b/dns/util.go @@ -107,16 +107,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret = append(ret, newDHCPClient(s.Addr)) continue case "system": - clients, err := loadSystemResolver() - if err != nil { - log.Errorln("[DNS:system] load system resolver failed: %s", err.Error()) - continue - } - if len(clients) == 0 { - log.Errorln("[DNS:system] no nameserver found in system") - continue - } - ret = append(ret, clients...) + ret = append(ret, newSystemClient()) continue case "rcode": ret = append(ret, newRCodeClient(s.Addr))