github.com/kelleygo/clashcore@v1.0.2/dns/system.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/kelleygo/clashcore/component/resolver" 12 "github.com/kelleygo/clashcore/log" 13 14 D "github.com/miekg/dns" 15 "golang.org/x/exp/slices" 16 ) 17 18 const ( 19 SystemDnsFlushTime = 5 * time.Minute 20 SystemDnsDeleteTimes = 12 // 12*5 = 60min 21 ) 22 23 type systemDnsClient struct { 24 disableTimes uint32 25 dnsClient 26 } 27 28 type systemClient struct { 29 mu sync.Mutex 30 dnsClients map[string]*systemDnsClient 31 lastFlush time.Time 32 } 33 34 func (c *systemClient) getDnsClients() ([]dnsClient, error) { 35 c.mu.Lock() 36 defer c.mu.Unlock() 37 var err error 38 if time.Since(c.lastFlush) > SystemDnsFlushTime { 39 var nameservers []string 40 if nameservers, err = dnsReadConfig(); err == nil { 41 log.Debugln("[DNS] system dns update to %s", nameservers) 42 for _, addr := range nameservers { 43 if resolver.IsSystemDnsBlacklisted(addr) { 44 continue 45 } 46 if _, ok := c.dnsClients[addr]; !ok { 47 clients := transform( 48 []NameServer{{ 49 Addr: net.JoinHostPort(addr, "53"), 50 Net: "udp", 51 }}, 52 nil, 53 ) 54 if len(clients) > 0 { 55 c.dnsClients[addr] = &systemDnsClient{ 56 disableTimes: 0, 57 dnsClient: clients[0], 58 } 59 } 60 } 61 } 62 available := 0 63 for nameserver, sdc := range c.dnsClients { 64 if slices.Contains(nameservers, nameserver) { 65 sdc.disableTimes = 0 // enable 66 available++ 67 } else { 68 if sdc.disableTimes > SystemDnsDeleteTimes { 69 delete(c.dnsClients, nameserver) // drop too old dnsClient 70 } else { 71 sdc.disableTimes++ 72 } 73 } 74 } 75 if available > 0 { 76 c.lastFlush = time.Now() 77 } 78 } 79 } 80 dnsClients := make([]dnsClient, 0, len(c.dnsClients)) 81 for _, sdc := range c.dnsClients { 82 if sdc.disableTimes == 0 { 83 dnsClients = append(dnsClients, sdc.dnsClient) 84 } 85 } 86 if len(dnsClients) > 0 { 87 return dnsClients, nil 88 } 89 return nil, err 90 } 91 92 func (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { 93 dnsClients, err := c.getDnsClients() 94 if err != nil { 95 return 96 } 97 msg, _, err = batchExchange(ctx, dnsClients, m) 98 return 99 } 100 101 // Address implements dnsClient 102 func (c *systemClient) Address() string { 103 dnsClients, _ := c.getDnsClients() 104 addrs := make([]string, 0, len(dnsClients)) 105 for _, c := range dnsClients { 106 addrs = append(addrs, c.Address()) 107 } 108 return fmt.Sprintf("system(%s)", strings.Join(addrs, ",")) 109 } 110 111 var _ dnsClient = (*systemClient)(nil) 112 113 func newSystemClient() *systemClient { 114 return &systemClient{ 115 dnsClients: map[string]*systemDnsClient{}, 116 } 117 }