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  }