github.com/igoogolx/clash@v1.19.8/dns/util.go (about)

     1  package dns
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"errors"
     7  	"fmt"
     8  	C "github.com/igoogolx/clash/constant"
     9  	"net"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/igoogolx/clash/common/cache"
    14  	"github.com/igoogolx/clash/common/picker"
    15  	"github.com/igoogolx/clash/log"
    16  
    17  	D "github.com/miekg/dns"
    18  	"github.com/samber/lo"
    19  )
    20  
    21  const serverFailureCacheTTL uint32 = 5
    22  
    23  func minimalTTL(records []D.RR) uint32 {
    24  	rr := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
    25  		return r1.Header().Ttl < r2.Header().Ttl
    26  	})
    27  	if rr == nil {
    28  		return 0
    29  	}
    30  	return rr.Header().Ttl
    31  }
    32  
    33  func updateTTL(records []D.RR, ttl uint32) {
    34  	if len(records) == 0 {
    35  		return
    36  	}
    37  	delta := minimalTTL(records) - ttl
    38  	for i := range records {
    39  		records[i].Header().Ttl = lo.Clamp(records[i].Header().Ttl-delta, 1, records[i].Header().Ttl)
    40  	}
    41  }
    42  
    43  func putMsgToCache(c *cache.LruCache, key string, q D.Question, msg *D.Msg) {
    44  	// skip dns cache for acme challenge
    45  	if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
    46  		log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
    47  		return
    48  	}
    49  
    50  	var ttl uint32
    51  	if msg.Rcode == D.RcodeServerFailure {
    52  		// [...] a resolver MAY cache a server failure response.
    53  		// If it does so it MUST NOT cache it for longer than five (5) minutes [...]
    54  		ttl = serverFailureCacheTTL
    55  	} else {
    56  		ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...))
    57  	}
    58  	if ttl == 0 {
    59  		return
    60  	}
    61  	c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second))
    62  }
    63  
    64  func setMsgTTL(msg *D.Msg, ttl uint32) {
    65  	for _, answer := range msg.Answer {
    66  		answer.Header().Ttl = ttl
    67  	}
    68  
    69  	for _, ns := range msg.Ns {
    70  		ns.Header().Ttl = ttl
    71  	}
    72  
    73  	for _, extra := range msg.Extra {
    74  		extra.Header().Ttl = ttl
    75  	}
    76  }
    77  
    78  func updateMsgTTL(msg *D.Msg, ttl uint32) {
    79  	updateTTL(msg.Answer, ttl)
    80  	updateTTL(msg.Ns, ttl)
    81  	updateTTL(msg.Extra, ttl)
    82  }
    83  
    84  func isIPRequest(q D.Question) bool {
    85  	return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA)
    86  }
    87  
    88  func transform(servers []NameServer, getDialer func() (C.Proxy, error)) []dnsClient {
    89  	ret := []dnsClient{}
    90  	for _, s := range servers {
    91  		switch s.Net {
    92  		case "https":
    93  			ret = append(ret, newDoHClient(s.Addr, s.Interface, getDialer))
    94  			continue
    95  		case "dhcp":
    96  			ret = append(ret, newDHCPClient(s.Addr, getDialer))
    97  			continue
    98  		}
    99  
   100  		host, port, _ := net.SplitHostPort(s.Addr)
   101  		ret = append(ret, &client{
   102  			Client: &D.Client{
   103  				Net: s.Net,
   104  				TLSConfig: &tls.Config{
   105  					ServerName: host,
   106  				},
   107  				UDPSize: 4096,
   108  				Timeout: 5 * time.Second,
   109  			},
   110  			port:      port,
   111  			host:      host,
   112  			iface:     s.Interface,
   113  			getDialer: getDialer,
   114  		})
   115  	}
   116  	return ret
   117  }
   118  
   119  func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
   120  	msg := &D.Msg{}
   121  	msg.Answer = []D.RR{}
   122  
   123  	msg.SetRcode(r, D.RcodeSuccess)
   124  	msg.Authoritative = true
   125  	msg.RecursionAvailable = true
   126  
   127  	return msg
   128  }
   129  
   130  func msgToIP(msg *D.Msg) []net.IP {
   131  	ips := []net.IP{}
   132  
   133  	for _, answer := range msg.Answer {
   134  		switch ans := answer.(type) {
   135  		case *D.AAAA:
   136  			ips = append(ips, ans.AAAA)
   137  		case *D.A:
   138  			ips = append(ips, ans.A)
   139  		}
   140  	}
   141  
   142  	return ips
   143  }
   144  
   145  func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
   146  	fast, ctx := picker.WithContext(ctx)
   147  	for _, client := range clients {
   148  		r := client
   149  		fast.Go(func() (any, error) {
   150  			m, err := r.ExchangeContext(ctx, m)
   151  			if err != nil {
   152  				return nil, err
   153  			} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
   154  				return nil, errors.New("server failure")
   155  			}
   156  			return m, nil
   157  		})
   158  	}
   159  
   160  	elm := fast.Wait()
   161  	if elm == nil {
   162  		err := errors.New("all DNS requests failed")
   163  		if fErr := fast.Error(); fErr != nil {
   164  			err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
   165  		}
   166  		return nil, err
   167  	}
   168  
   169  	msg = elm.(*D.Msg)
   170  	return
   171  }