2019-06-28 12:29:08 +08:00
|
|
|
package dns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2022-04-06 04:25:53 +08:00
|
|
|
"net/netip"
|
2019-06-28 12:29:08 +08:00
|
|
|
"time"
|
|
|
|
|
2023-12-02 17:07:36 +08:00
|
|
|
"github.com/metacubex/mihomo/common/arc"
|
|
|
|
"github.com/metacubex/mihomo/common/lru"
|
2023-11-03 21:01:45 +08:00
|
|
|
"github.com/metacubex/mihomo/component/fakeip"
|
|
|
|
"github.com/metacubex/mihomo/component/resolver"
|
|
|
|
"github.com/metacubex/mihomo/component/trie"
|
|
|
|
C "github.com/metacubex/mihomo/constant"
|
|
|
|
"github.com/metacubex/mihomo/constant/provider"
|
|
|
|
"github.com/metacubex/mihomo/log"
|
2019-06-28 12:29:08 +08:00
|
|
|
|
|
|
|
D "github.com/miekg/dns"
|
2023-09-02 20:56:42 +08:00
|
|
|
"github.com/samber/lo"
|
2023-11-08 20:19:48 +08:00
|
|
|
"golang.org/x/exp/maps"
|
2019-07-14 19:29:58 +08:00
|
|
|
"golang.org/x/sync/singleflight"
|
2019-06-28 12:29:08 +08:00
|
|
|
)
|
|
|
|
|
2020-02-15 21:42:46 +08:00
|
|
|
type dnsClient interface {
|
2019-06-28 12:29:08 +08:00
|
|
|
ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error)
|
2023-01-28 22:33:03 +08:00
|
|
|
Address() string
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2023-12-02 17:07:36 +08:00
|
|
|
type dnsCache interface {
|
|
|
|
GetWithExpire(key string) (*D.Msg, time.Time, bool)
|
|
|
|
SetWithExpire(key string, value *D.Msg, expire time.Time)
|
|
|
|
}
|
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
type result struct {
|
|
|
|
Msg *D.Msg
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
|
|
|
type Resolver struct {
|
2020-09-28 22:17:10 +08:00
|
|
|
ipv6 bool
|
2023-03-10 23:38:16 +08:00
|
|
|
ipv6Timeout time.Duration
|
2023-03-12 15:00:59 +08:00
|
|
|
hosts *trie.DomainTrie[resolver.HostValue]
|
2020-09-28 22:17:10 +08:00
|
|
|
main []dnsClient
|
|
|
|
fallback []dnsClient
|
2024-08-15 20:04:24 +08:00
|
|
|
fallbackDomainFilters []C.Rule
|
|
|
|
fallbackIPFilters []C.Rule
|
2020-09-28 22:17:10 +08:00
|
|
|
group singleflight.Group
|
2023-12-02 17:07:36 +08:00
|
|
|
cache dnsCache
|
2023-11-08 19:29:26 +08:00
|
|
|
policy []dnsPolicy
|
2022-03-28 00:44:13 +08:00
|
|
|
proxyServer []dnsClient
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2022-11-12 13:18:36 +08:00
|
|
|
func (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) {
|
2022-04-23 00:27:22 +08:00
|
|
|
ch := make(chan []netip.Addr, 1)
|
2019-06-28 12:29:08 +08:00
|
|
|
go func() {
|
2019-06-29 00:58:59 +08:00
|
|
|
defer close(ch)
|
2022-08-13 13:07:35 +08:00
|
|
|
ip, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
2019-06-28 12:29:08 +08:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ch <- ip
|
|
|
|
}()
|
|
|
|
|
2022-11-12 20:43:48 +08:00
|
|
|
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
2019-06-28 12:29:08 +08:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-06-29 00:58:59 +08:00
|
|
|
ip, open := <-ch
|
|
|
|
if !open {
|
2022-04-23 00:27:22 +08:00
|
|
|
return nil, resolver.ErrIPNotFound
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ip, nil
|
|
|
|
}
|
|
|
|
|
2022-11-12 13:18:36 +08:00
|
|
|
func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error) {
|
2022-04-23 00:27:22 +08:00
|
|
|
ch := make(chan []netip.Addr, 1)
|
|
|
|
go func() {
|
|
|
|
defer close(ch)
|
2022-11-12 20:43:48 +08:00
|
|
|
ip, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
2022-04-23 00:27:22 +08:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ch <- ip
|
|
|
|
}()
|
|
|
|
|
2022-11-12 20:43:48 +08:00
|
|
|
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
2023-03-10 23:38:16 +08:00
|
|
|
var waitIPv6 *time.Timer
|
2023-04-11 10:29:55 +08:00
|
|
|
if r != nil && r.ipv6Timeout > 0 {
|
2023-03-10 23:38:16 +08:00
|
|
|
waitIPv6 = time.NewTimer(r.ipv6Timeout)
|
|
|
|
} else {
|
|
|
|
waitIPv6 = time.NewTimer(100 * time.Millisecond)
|
|
|
|
}
|
|
|
|
defer waitIPv6.Stop()
|
2022-05-21 00:34:00 +08:00
|
|
|
select {
|
|
|
|
case ipv6s, open := <-ch:
|
|
|
|
if !open && err != nil {
|
|
|
|
return nil, resolver.ErrIPNotFound
|
|
|
|
}
|
|
|
|
ips = append(ips, ipv6s...)
|
2023-03-10 23:38:16 +08:00
|
|
|
case <-waitIPv6.C:
|
2022-05-28 09:58:45 +08:00
|
|
|
// wait ipv6 result
|
2022-04-23 00:27:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ips, nil
|
|
|
|
}
|
|
|
|
|
2022-11-12 21:31:07 +08:00
|
|
|
// LookupIPv4 request with TypeA
|
|
|
|
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
|
|
|
|
return r.lookupIP(ctx, host, D.TypeA)
|
2022-04-23 00:27:22 +08:00
|
|
|
}
|
|
|
|
|
2022-11-12 21:31:07 +08:00
|
|
|
// LookupIPv6 request with TypeAAAA
|
|
|
|
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
|
|
|
|
return r.lookupIP(ctx, host, D.TypeAAAA)
|
2019-09-11 17:00:55 +08:00
|
|
|
}
|
2019-06-28 12:29:08 +08:00
|
|
|
|
2022-04-20 01:52:51 +08:00
|
|
|
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
2020-09-28 22:17:10 +08:00
|
|
|
for _, filter := range r.fallbackIPFilters {
|
2024-08-15 20:04:24 +08:00
|
|
|
if ok, _ := filter.Match(&C.Metadata{DstIP: ip}); ok {
|
2019-09-15 13:36:45 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-09-06 23:07:34 +08:00
|
|
|
// ExchangeContext a batch of dns request with context.Context, and it use cache
|
|
|
|
func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
2019-06-28 12:29:08 +08:00
|
|
|
if len(m.Question) == 0 {
|
|
|
|
return nil, errors.New("should have one question at least")
|
|
|
|
}
|
2022-11-12 18:29:19 +08:00
|
|
|
continueFetch := false
|
|
|
|
defer func() {
|
|
|
|
if continueFetch || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
|
|
|
go func() {
|
2022-11-16 17:53:52 +08:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
|
|
|
|
defer cancel()
|
2022-11-12 18:29:19 +08:00
|
|
|
_, _ = r.exchangeWithoutCache(ctx, m) // ignore result, just for putMsgToCache
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}()
|
2019-06-28 12:29:08 +08:00
|
|
|
|
|
|
|
q := m.Question[0]
|
2024-07-19 19:27:29 +08:00
|
|
|
domain := msgToDomain(m)
|
|
|
|
_, qTypeStr := msgToQtype(m)
|
2023-12-02 17:07:36 +08:00
|
|
|
cacheM, expireTime, hit := r.cache.GetWithExpire(q.String())
|
2020-05-07 15:10:14 +08:00
|
|
|
if hit {
|
2024-07-19 19:27:29 +08:00
|
|
|
ips := msgToIP(cacheM)
|
|
|
|
log.Debugln("[DNS] cache hit %s --> %s %s, expire at %s", domain, ips, qTypeStr, expireTime.Format("2006-01-02 15:04:05"))
|
2020-05-07 15:10:14 +08:00
|
|
|
now := time.Now()
|
2022-04-20 01:52:51 +08:00
|
|
|
msg = cacheM.Copy()
|
2020-05-07 15:10:14 +08:00
|
|
|
if expireTime.Before(now) {
|
|
|
|
setMsgTTL(msg, uint32(1)) // Continue fetch
|
2022-11-12 18:29:19 +08:00
|
|
|
continueFetch = true
|
2020-05-07 15:10:14 +08:00
|
|
|
} else {
|
2023-06-06 09:45:05 +08:00
|
|
|
// updating TTL by subtracting common delta time from each DNS record
|
|
|
|
updateMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
|
2020-05-07 15:10:14 +08:00
|
|
|
}
|
2019-06-28 12:29:08 +08:00
|
|
|
return
|
|
|
|
}
|
2021-09-06 23:07:34 +08:00
|
|
|
return r.exchangeWithoutCache(ctx, m)
|
2020-05-07 15:10:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// ExchangeWithoutCache a batch of dns request, and it do NOT GET from cache
|
2021-09-06 23:07:34 +08:00
|
|
|
func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
2020-05-07 15:10:14 +08:00
|
|
|
q := m.Question[0]
|
|
|
|
|
2022-11-19 08:51:00 +08:00
|
|
|
retryNum := 0
|
|
|
|
retryMax := 3
|
|
|
|
fn := func() (result any, err error) {
|
2022-11-18 18:02:46 +08:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight
|
|
|
|
defer cancel()
|
2023-06-11 20:58:51 +08:00
|
|
|
cache := false
|
2022-11-18 18:02:46 +08:00
|
|
|
|
2020-07-20 21:16:36 +08:00
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
2022-11-19 08:51:00 +08:00
|
|
|
result = retryNum
|
|
|
|
retryNum++
|
2020-07-20 21:16:36 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
msg := result.(*D.Msg)
|
|
|
|
|
2023-06-11 20:58:51 +08:00
|
|
|
if cache {
|
2023-10-16 09:21:06 +08:00
|
|
|
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
|
|
|
|
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
|
|
|
|
return rr.Header().Rrtype != D.TypeOPT
|
|
|
|
})
|
2023-12-02 17:07:36 +08:00
|
|
|
putMsgToCache(r.cache, q.String(), q, msg)
|
2023-06-11 20:58:51 +08:00
|
|
|
}
|
2020-07-20 21:16:36 +08:00
|
|
|
}()
|
|
|
|
|
2020-07-17 17:34:40 +08:00
|
|
|
isIPReq := isIPRequest(q)
|
|
|
|
if isIPReq {
|
2023-09-02 20:56:42 +08:00
|
|
|
cache = true
|
2021-09-06 23:07:34 +08:00
|
|
|
return r.ipExchange(ctx, m)
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2021-05-19 11:17:35 +08:00
|
|
|
if matched := r.matchPolicy(m); len(matched) != 0 {
|
2023-10-25 18:07:45 +08:00
|
|
|
result, cache, err = batchExchange(ctx, matched, m)
|
2023-06-11 20:58:51 +08:00
|
|
|
return
|
2021-05-19 11:17:35 +08:00
|
|
|
}
|
2023-10-25 18:07:45 +08:00
|
|
|
result, cache, err = batchExchange(ctx, r.main, m)
|
2023-06-11 20:58:51 +08:00
|
|
|
return
|
2022-11-19 08:51:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
ch := r.group.DoChan(q.String(), fn)
|
2019-07-14 19:29:58 +08:00
|
|
|
|
2022-11-18 18:02:46 +08:00
|
|
|
var result singleflight.Result
|
|
|
|
|
|
|
|
select {
|
|
|
|
case result = <-ch:
|
|
|
|
break
|
|
|
|
case <-ctx.Done():
|
|
|
|
select {
|
2022-11-19 08:51:00 +08:00
|
|
|
case result = <-ch: // maybe ctxDone and chFinish in same time, get DoChan's result as much as possible
|
2022-11-18 18:02:46 +08:00
|
|
|
break
|
2022-11-19 08:51:00 +08:00
|
|
|
default:
|
|
|
|
go func() { // start a retrying monitor in background
|
|
|
|
result := <-ch
|
|
|
|
ret, err, shared := result.Val, result.Err, result.Shared
|
|
|
|
if err != nil && !shared && ret.(int) < retryMax { // retry
|
|
|
|
r.group.DoChan(q.String(), fn)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return nil, ctx.Err()
|
2022-11-18 18:02:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret, err, shared := result.Val, result.Err, result.Shared
|
2022-11-19 08:51:00 +08:00
|
|
|
if err != nil && !shared && ret.(int) < retryMax { // retry
|
|
|
|
r.group.DoChan(q.String(), fn)
|
|
|
|
}
|
2022-11-18 18:02:46 +08:00
|
|
|
|
2019-07-14 19:29:58 +08:00
|
|
|
if err == nil {
|
|
|
|
msg = ret.(*D.Msg)
|
2020-03-24 10:13:53 +08:00
|
|
|
if shared {
|
|
|
|
msg = msg.Copy()
|
|
|
|
}
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-05-19 11:17:35 +08:00
|
|
|
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
|
|
|
if r.policy == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-15 13:16:45 +08:00
|
|
|
domain := msgToDomain(m)
|
2021-05-19 11:17:35 +08:00
|
|
|
if domain == "" {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-11-08 19:29:26 +08:00
|
|
|
for _, policy := range r.policy {
|
|
|
|
if dnsClients := policy.Match(domain); len(dnsClients) > 0 {
|
|
|
|
return dnsClients
|
2023-04-01 11:53:39 +08:00
|
|
|
}
|
|
|
|
}
|
2023-01-21 14:40:36 +08:00
|
|
|
return nil
|
2021-05-19 11:17:35 +08:00
|
|
|
}
|
|
|
|
|
2020-09-28 22:17:10 +08:00
|
|
|
func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
|
|
|
|
if r.fallback == nil || len(r.fallbackDomainFilters) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-05-15 13:16:45 +08:00
|
|
|
domain := msgToDomain(m)
|
2020-09-28 22:17:10 +08:00
|
|
|
|
|
|
|
if domain == "" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, df := range r.fallbackDomainFilters {
|
2024-08-15 20:04:24 +08:00
|
|
|
if ok, _ := df.Match(&C.Metadata{Host: domain}); ok {
|
2020-09-28 22:17:10 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-09-06 23:07:34 +08:00
|
|
|
func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
2021-05-19 11:17:35 +08:00
|
|
|
if matched := r.matchPolicy(m); len(matched) != 0 {
|
2021-09-06 23:07:34 +08:00
|
|
|
res := <-r.asyncExchange(ctx, matched, m)
|
2021-05-19 11:17:35 +08:00
|
|
|
return res.Msg, res.Error
|
|
|
|
}
|
|
|
|
|
2020-09-28 22:17:10 +08:00
|
|
|
onlyFallback := r.shouldOnlyQueryFallback(m)
|
|
|
|
|
|
|
|
if onlyFallback {
|
2021-09-06 23:07:34 +08:00
|
|
|
res := <-r.asyncExchange(ctx, r.fallback, m)
|
2020-09-28 22:17:10 +08:00
|
|
|
return res.Msg, res.Error
|
|
|
|
}
|
|
|
|
|
2021-09-06 23:07:34 +08:00
|
|
|
msgCh := r.asyncExchange(ctx, r.main, m)
|
2020-09-28 22:17:10 +08:00
|
|
|
|
2021-11-17 16:03:47 +08:00
|
|
|
if r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available
|
2019-06-28 12:29:08 +08:00
|
|
|
res := <-msgCh
|
|
|
|
msg, err = res.Msg, res.Error
|
|
|
|
return
|
|
|
|
}
|
2020-09-28 22:17:10 +08:00
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
res := <-msgCh
|
|
|
|
if res.Error == nil {
|
2021-01-23 14:49:46 +08:00
|
|
|
if ips := msgToIP(res.Msg); len(ips) != 0 {
|
2023-09-02 20:56:42 +08:00
|
|
|
shouldNotFallback := lo.EveryBy(ips, func(ip netip.Addr) bool {
|
|
|
|
return !r.shouldIPFallback(ip)
|
|
|
|
})
|
|
|
|
if shouldNotFallback {
|
2022-03-17 17:41:02 +08:00
|
|
|
msg, err = res.Msg, res.Error // no need to wait for fallback result
|
|
|
|
return
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-17 16:03:47 +08:00
|
|
|
res = <-r.asyncExchange(ctx, r.fallback, m)
|
2019-06-28 12:29:08 +08:00
|
|
|
msg, err = res.Msg, res.Error
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:43:48 +08:00
|
|
|
func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {
|
2022-04-23 00:27:22 +08:00
|
|
|
ip, err := netip.ParseAddr(host)
|
2022-04-20 01:52:51 +08:00
|
|
|
if err == nil {
|
2024-03-22 00:33:38 +08:00
|
|
|
isIPv4 := ip.Is4() || ip.Is4In6()
|
2019-09-27 15:26:07 +08:00
|
|
|
if dnsType == D.TypeAAAA && !isIPv4 {
|
2022-04-23 00:27:22 +08:00
|
|
|
return []netip.Addr{ip}, nil
|
2019-09-27 15:26:07 +08:00
|
|
|
} else if dnsType == D.TypeA && isIPv4 {
|
2022-04-23 00:27:22 +08:00
|
|
|
return []netip.Addr{ip}, nil
|
2020-02-17 20:11:46 +08:00
|
|
|
} else {
|
2022-04-23 00:27:22 +08:00
|
|
|
return []netip.Addr{}, resolver.ErrIPVersion
|
2019-09-11 17:00:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
query := &D.Msg{}
|
|
|
|
query.SetQuestion(D.Fqdn(host), dnsType)
|
|
|
|
|
2022-11-12 13:18:36 +08:00
|
|
|
msg, err := r.ExchangeContext(ctx, query)
|
2019-06-28 12:29:08 +08:00
|
|
|
if err != nil {
|
2022-04-23 00:27:22 +08:00
|
|
|
return []netip.Addr{}, err
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2022-04-23 00:27:22 +08:00
|
|
|
ips = msgToIP(msg)
|
2020-02-14 16:36:20 +08:00
|
|
|
ipLength := len(ips)
|
|
|
|
if ipLength == 0 {
|
2022-04-23 00:27:22 +08:00
|
|
|
return []netip.Addr{}, resolver.ErrIPNotFound
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-06 23:07:34 +08:00
|
|
|
func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D.Msg) <-chan *result {
|
2020-03-13 00:11:54 +08:00
|
|
|
ch := make(chan *result, 1)
|
2019-06-28 12:29:08 +08:00
|
|
|
go func() {
|
2023-10-25 18:07:45 +08:00
|
|
|
res, _, err := batchExchange(ctx, client, msg)
|
2019-06-28 12:29:08 +08:00
|
|
|
ch <- &result{Msg: res, Error: err}
|
|
|
|
}()
|
|
|
|
return ch
|
|
|
|
}
|
|
|
|
|
2023-04-11 14:10:57 +08:00
|
|
|
// Invalid return this resolver can or can't be used
|
|
|
|
func (r *Resolver) Invalid() bool {
|
|
|
|
if r == nil {
|
|
|
|
return false
|
|
|
|
}
|
2022-03-28 00:44:13 +08:00
|
|
|
return len(r.main) > 0
|
|
|
|
}
|
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
type NameServer struct {
|
2021-11-17 16:03:47 +08:00
|
|
|
Net string
|
|
|
|
Addr string
|
2023-10-26 09:07:49 +08:00
|
|
|
Interface string
|
2023-04-11 10:29:55 +08:00
|
|
|
ProxyAdapter C.ProxyAdapter
|
|
|
|
ProxyName string
|
2022-07-21 14:03:49 +08:00
|
|
|
Params map[string]string
|
2022-11-12 11:14:51 +08:00
|
|
|
PreferH3 bool
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2023-11-08 20:19:48 +08:00
|
|
|
func (ns NameServer) Equal(ns2 NameServer) bool {
|
|
|
|
defer func() {
|
|
|
|
// C.ProxyAdapter compare maybe panic, just ignore
|
|
|
|
recover()
|
|
|
|
}()
|
|
|
|
if ns.Net == ns2.Net &&
|
|
|
|
ns.Addr == ns2.Addr &&
|
|
|
|
ns.Interface == ns2.Interface &&
|
|
|
|
ns.ProxyAdapter == ns2.ProxyAdapter &&
|
|
|
|
ns.ProxyName == ns2.ProxyName &&
|
|
|
|
maps.Equal(ns.Params, ns2.Params) &&
|
|
|
|
ns.PreferH3 == ns2.PreferH3 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-08-15 20:04:24 +08:00
|
|
|
type Policy struct {
|
|
|
|
Domain string
|
|
|
|
Rule C.Rule
|
|
|
|
NameServers []NameServer
|
2019-09-15 13:36:45 +08:00
|
|
|
}
|
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
type Config struct {
|
2024-08-15 20:04:24 +08:00
|
|
|
Main, Fallback []NameServer
|
|
|
|
Default []NameServer
|
|
|
|
ProxyServer []NameServer
|
|
|
|
IPv6 bool
|
|
|
|
IPv6Timeout uint
|
|
|
|
EnhancedMode C.DNSMode
|
|
|
|
FallbackIPFilter []C.Rule
|
|
|
|
FallbackDomainFilter []C.Rule
|
|
|
|
Pool *fakeip.Pool
|
|
|
|
Hosts *trie.DomainTrie[resolver.HostValue]
|
|
|
|
Policy []Policy
|
|
|
|
Tunnel provider.Tunnel
|
|
|
|
CacheAlgorithm string
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
|
|
|
|
2020-09-17 10:48:42 +08:00
|
|
|
func NewResolver(config Config) *Resolver {
|
2023-12-02 17:07:36 +08:00
|
|
|
var cache dnsCache
|
|
|
|
if config.CacheAlgorithm == "lru" {
|
|
|
|
cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
|
|
|
|
} else {
|
|
|
|
cache = arc.New(arc.WithSize[string, *D.Msg](4096))
|
|
|
|
}
|
2020-02-15 21:42:46 +08:00
|
|
|
defaultResolver := &Resolver{
|
2023-03-10 23:38:16 +08:00
|
|
|
main: transform(config.Default, nil),
|
2023-12-02 17:07:36 +08:00
|
|
|
cache: cache,
|
2023-03-10 23:38:16 +08:00
|
|
|
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
|
2020-02-15 21:42:46 +08:00
|
|
|
}
|
|
|
|
|
2023-11-08 20:19:48 +08:00
|
|
|
var nameServerCache []struct {
|
|
|
|
NameServer
|
|
|
|
dnsClient
|
|
|
|
}
|
|
|
|
cacheTransform := func(nameserver []NameServer) (result []dnsClient) {
|
|
|
|
LOOP:
|
|
|
|
for _, ns := range nameserver {
|
|
|
|
for _, nsc := range nameServerCache {
|
|
|
|
if nsc.NameServer.Equal(ns) {
|
|
|
|
result = append(result, nsc.dnsClient)
|
|
|
|
continue LOOP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// not in cache
|
|
|
|
dc := transform([]NameServer{ns}, defaultResolver)
|
|
|
|
if len(dc) > 0 {
|
|
|
|
dc := dc[0]
|
|
|
|
nameServerCache = append(nameServerCache, struct {
|
|
|
|
NameServer
|
|
|
|
dnsClient
|
|
|
|
}{NameServer: ns, dnsClient: dc})
|
|
|
|
result = append(result, dc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-12-02 17:07:36 +08:00
|
|
|
if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" {
|
|
|
|
cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
|
|
|
|
} else {
|
|
|
|
cache = arc.New(arc.WithSize[string, *D.Msg](4096))
|
|
|
|
}
|
2019-06-28 12:29:08 +08:00
|
|
|
r := &Resolver{
|
2023-03-10 23:38:16 +08:00
|
|
|
ipv6: config.IPv6,
|
2023-11-08 20:19:48 +08:00
|
|
|
main: cacheTransform(config.Main),
|
2023-12-02 17:07:36 +08:00
|
|
|
cache: cache,
|
2023-03-10 23:38:16 +08:00
|
|
|
hosts: config.Hosts,
|
|
|
|
ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
2019-09-15 13:36:45 +08:00
|
|
|
|
2019-06-28 12:29:08 +08:00
|
|
|
if len(config.Fallback) != 0 {
|
2023-11-08 20:19:48 +08:00
|
|
|
r.fallback = cacheTransform(config.Fallback)
|
2019-06-28 12:29:08 +08:00
|
|
|
}
|
2019-09-15 13:36:45 +08:00
|
|
|
|
2022-03-28 00:44:13 +08:00
|
|
|
if len(config.ProxyServer) != 0 {
|
2023-11-08 20:19:48 +08:00
|
|
|
r.proxyServer = cacheTransform(config.ProxyServer)
|
2022-03-28 00:44:13 +08:00
|
|
|
}
|
|
|
|
|
2024-08-15 20:04:24 +08:00
|
|
|
if len(config.Policy) != 0 {
|
2023-11-08 19:29:26 +08:00
|
|
|
r.policy = make([]dnsPolicy, 0)
|
|
|
|
|
|
|
|
var triePolicy *trie.DomainTrie[[]dnsClient]
|
2023-12-08 09:26:24 +08:00
|
|
|
insertPolicy := func(policy dnsPolicy) {
|
2023-11-08 19:29:26 +08:00
|
|
|
if triePolicy != nil {
|
|
|
|
triePolicy.Optimize()
|
|
|
|
r.policy = append(r.policy, domainTriePolicy{triePolicy})
|
|
|
|
triePolicy = nil
|
|
|
|
}
|
2023-12-08 09:26:24 +08:00
|
|
|
if policy != nil {
|
|
|
|
r.policy = append(r.policy, policy)
|
|
|
|
}
|
2023-11-08 19:29:26 +08:00
|
|
|
}
|
2023-11-08 20:19:48 +08:00
|
|
|
|
2024-08-15 20:04:24 +08:00
|
|
|
for _, policy := range config.Policy {
|
|
|
|
if policy.Rule != nil {
|
|
|
|
insertPolicy(domainRulePolicy{rule: policy.Rule, dnsClients: cacheTransform(policy.NameServers)})
|
|
|
|
} else {
|
|
|
|
if triePolicy == nil {
|
|
|
|
triePolicy = trie.New[[]dnsClient]()
|
2023-01-21 14:40:36 +08:00
|
|
|
}
|
2024-08-15 20:04:24 +08:00
|
|
|
_ = triePolicy.Insert(policy.Domain, cacheTransform(policy.NameServers))
|
2023-01-21 14:40:36 +08:00
|
|
|
}
|
2021-05-19 11:17:35 +08:00
|
|
|
}
|
2023-12-08 09:26:24 +08:00
|
|
|
insertPolicy(nil)
|
2023-04-01 11:53:39 +08:00
|
|
|
}
|
2024-08-15 20:04:24 +08:00
|
|
|
r.fallbackIPFilters = config.FallbackIPFilter
|
|
|
|
r.fallbackDomainFilters = config.FallbackDomainFilter
|
2021-11-17 16:03:47 +08:00
|
|
|
|
|
|
|
return r
|
|
|
|
}
|
2019-09-15 13:36:45 +08:00
|
|
|
|
2022-03-28 00:44:13 +08:00
|
|
|
func NewProxyServerHostResolver(old *Resolver) *Resolver {
|
2021-11-17 16:03:47 +08:00
|
|
|
r := &Resolver{
|
2023-03-10 23:38:16 +08:00
|
|
|
ipv6: old.ipv6,
|
|
|
|
main: old.proxyServer,
|
2023-12-02 17:07:36 +08:00
|
|
|
cache: old.cache,
|
2023-03-10 23:38:16 +08:00
|
|
|
hosts: old.hosts,
|
|
|
|
ipv6Timeout: old.ipv6Timeout,
|
2021-11-17 16:03:47 +08:00
|
|
|
}
|
2019-06-28 12:29:08 +08:00
|
|
|
return r
|
|
|
|
}
|
2023-04-11 10:29:55 +08:00
|
|
|
|
|
|
|
var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go
|