Merge commit '2a8e1778ad1d7b507e432f659407d76dffcaacb8' into Alpha
This commit is contained in:
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||
"go.uber.org/atomic"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
@ -34,15 +35,19 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
|
||||
ip netip.Addr
|
||||
err error
|
||||
)
|
||||
if ip, err = netip.ParseAddr(c.host); err != nil {
|
||||
if c.r == nil {
|
||||
if c.r == nil {
|
||||
// a default ip dns
|
||||
if ip, err = netip.ParseAddr(c.host); err != nil {
|
||||
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
|
||||
} else {
|
||||
if ip, err = resolver.ResolveIPWithResolver(ctx, c.host, c.r); err != nil {
|
||||
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
|
||||
}
|
||||
c.host = ip.String()
|
||||
}
|
||||
} else {
|
||||
ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
|
||||
} else if len(ips) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host)
|
||||
}
|
||||
ip = ips[rand.Intn(len(ips))]
|
||||
}
|
||||
|
||||
network := "udp"
|
||||
|
19
dns/dhcp.go
19
dns/dhcp.go
@ -30,7 +30,7 @@ type dhcpClient struct {
|
||||
|
||||
ifaceAddr *netip.Prefix
|
||||
done chan struct{}
|
||||
resolver *Resolver
|
||||
clients []dnsClient
|
||||
err error
|
||||
}
|
||||
|
||||
@ -42,15 +42,15 @@ func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
|
||||
}
|
||||
|
||||
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
|
||||
res, err := d.resolve(ctx)
|
||||
clients, err := d.resolve(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.ExchangeContext(ctx, m)
|
||||
return batchExchange(ctx, clients, m)
|
||||
}
|
||||
|
||||
func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
|
||||
func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {
|
||||
d.lock.Lock()
|
||||
|
||||
invalidated, err := d.invalidate()
|
||||
@ -65,8 +65,9 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout)
|
||||
defer cancel()
|
||||
|
||||
var res *Resolver
|
||||
var res []dnsClient
|
||||
dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName)
|
||||
// dns never empty if err is nil
|
||||
if err == nil {
|
||||
nameserver := make([]NameServer, 0, len(dns))
|
||||
for _, item := range dns {
|
||||
@ -76,9 +77,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
|
||||
})
|
||||
}
|
||||
|
||||
res = NewResolver(Config{
|
||||
Main: nameserver,
|
||||
})
|
||||
res = transform(nameserver, nil)
|
||||
}
|
||||
|
||||
d.lock.Lock()
|
||||
@ -87,7 +86,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
|
||||
close(done)
|
||||
|
||||
d.done = nil
|
||||
d.resolver = res
|
||||
d.clients = res
|
||||
d.err = err
|
||||
}()
|
||||
}
|
||||
@ -97,7 +96,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
|
||||
for {
|
||||
d.lock.Lock()
|
||||
|
||||
res, err, done := d.resolver, d.err, d.done
|
||||
res, err, done := d.clients, d.err, d.done
|
||||
|
||||
d.lock.Unlock()
|
||||
|
||||
|
@ -109,7 +109,7 @@ func NewEnhancer(cfg Config) *ResolverEnhancer {
|
||||
|
||||
if cfg.EnhancedMode != C.DNSNormal {
|
||||
fakePool = cfg.Pool
|
||||
mapping = cache.NewLRUCache[netip.Addr, string](cache.WithSize[netip.Addr, string](4096), cache.WithStale[netip.Addr, string](true))
|
||||
mapping = cache.New[netip.Addr, string](cache.WithSize[netip.Addr, string](4096), cache.WithStale[netip.Addr, string](true))
|
||||
}
|
||||
|
||||
return &ResolverEnhancer{
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
"github.com/Dreamacro/clash/common/picker"
|
||||
"github.com/Dreamacro/clash/component/fakeip"
|
||||
"github.com/Dreamacro/clash/component/geodata/router"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
@ -48,14 +47,14 @@ func (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []
|
||||
ch := make(chan []netip.Addr, 1)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
ip, err := r.resolveIP(ctx, host, D.TypeAAAA)
|
||||
ip, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ch <- ip
|
||||
}()
|
||||
|
||||
ips, err = r.resolveIP(ctx, host, D.TypeA)
|
||||
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
@ -72,7 +71,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
|
||||
ch := make(chan []netip.Addr, 1)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
ip, err := r.resolveIP(ctx, host, D.TypeAAAA)
|
||||
ip, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -80,7 +79,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
|
||||
ch <- ip
|
||||
}()
|
||||
|
||||
ips, err = r.resolveIP(ctx, host, D.TypeA)
|
||||
ips, err = r.lookupIP(ctx, host, D.TypeA)
|
||||
|
||||
select {
|
||||
case ipv6s, open := <-ch:
|
||||
@ -95,39 +94,47 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) {
|
||||
return r.resolveIP(ctx, host, D.TypeA)
|
||||
}
|
||||
|
||||
func (r *Resolver) LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) {
|
||||
return r.resolveIP(ctx, host, D.TypeAAAA)
|
||||
}
|
||||
|
||||
// ResolveIP request with TypeA and TypeAAAA, priority return TypeA
|
||||
func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) {
|
||||
if ips, err := r.LookupIPPrimaryIPv4(ctx, host); err == nil {
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
} else {
|
||||
ips, err := r.LookupIPPrimaryIPv4(ctx, host)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
} else if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
||||
}
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
}
|
||||
|
||||
// LookupIPv4 request with TypeA
|
||||
func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
return r.lookupIP(ctx, host, D.TypeA)
|
||||
}
|
||||
|
||||
// ResolveIPv4 request with TypeA
|
||||
func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) {
|
||||
if ips, err := r.LookupIPv4(ctx, host); err == nil {
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
} else {
|
||||
ips, err := r.lookupIP(ctx, host, D.TypeA)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
} else if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
||||
}
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
}
|
||||
|
||||
// LookupIPv6 request with TypeAAAA
|
||||
func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
return r.lookupIP(ctx, host, D.TypeAAAA)
|
||||
}
|
||||
|
||||
// ResolveIPv6 request with TypeAAAA
|
||||
func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) {
|
||||
if ips, err := r.LookupIPv6(ctx, host); err == nil {
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
} else {
|
||||
ips, err := r.lookupIP(ctx, host, D.TypeAAAA)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
} else if len(ips) == 0 {
|
||||
return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host)
|
||||
}
|
||||
return ips[rand.Intn(len(ips))], nil
|
||||
}
|
||||
|
||||
func (r *Resolver) shouldIPFallback(ip netip.Addr) bool {
|
||||
@ -149,6 +156,16 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
|
||||
if len(m.Question) == 0 {
|
||||
return nil, errors.New("should have one question at least")
|
||||
}
|
||||
continueFetch := false
|
||||
defer func() {
|
||||
if continueFetch || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)
|
||||
defer cancel()
|
||||
go func() {
|
||||
_, _ = r.exchangeWithoutCache(ctx, m) // ignore result, just for putMsgToCache
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
q := m.Question[0]
|
||||
cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String())
|
||||
@ -157,7 +174,7 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
|
||||
msg = cacheM.Copy()
|
||||
if expireTime.Before(now) {
|
||||
setMsgTTL(msg, uint32(1)) // Continue fetch
|
||||
go r.exchangeWithoutCache(ctx, m)
|
||||
continueFetch = true
|
||||
} else {
|
||||
setMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))
|
||||
}
|
||||
@ -203,31 +220,10 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
|
||||
}
|
||||
|
||||
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
|
||||
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
|
||||
for _, client := range clients {
|
||||
r := client
|
||||
fast.Go(func() (*D.Msg, error) {
|
||||
m, err := r.ExchangeContext(ctx, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
|
||||
return nil, errors.New("server failure")
|
||||
}
|
||||
return m, nil
|
||||
})
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDNSTimeout)
|
||||
defer cancel()
|
||||
|
||||
elm := fast.Wait()
|
||||
if elm == nil {
|
||||
err := errors.New("all DNS requests failed")
|
||||
if fErr := fast.Error(); fErr != nil {
|
||||
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg = elm
|
||||
return
|
||||
return batchExchange(ctx, clients, m)
|
||||
}
|
||||
|
||||
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
||||
@ -305,7 +301,7 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Resolver) resolveIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {
|
||||
func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {
|
||||
ip, err := netip.ParseAddr(host)
|
||||
if err == nil {
|
||||
isIPv4 := ip.Is4()
|
||||
@ -381,13 +377,13 @@ type Config struct {
|
||||
func NewResolver(config Config) *Resolver {
|
||||
defaultResolver := &Resolver{
|
||||
main: transform(config.Default, nil),
|
||||
lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
lruCache: cache.New[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
}
|
||||
|
||||
r := &Resolver{
|
||||
ipv6: config.IPv6,
|
||||
main: transform(config.Main, defaultResolver),
|
||||
lruCache: cache.NewLRUCache[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
lruCache: cache.New[string, *D.Msg](cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)),
|
||||
hosts: config.Hosts,
|
||||
}
|
||||
|
||||
|
35
dns/util.go
35
dns/util.go
@ -3,6 +3,7 @@ package dns
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
@ -11,7 +12,9 @@ import (
|
||||
|
||||
"github.com/Dreamacro/clash/common/cache"
|
||||
"github.com/Dreamacro/clash/common/nnip"
|
||||
"github.com/Dreamacro/clash/common/picker"
|
||||
"github.com/Dreamacro/clash/component/dialer"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"github.com/Dreamacro/clash/tunnel"
|
||||
@ -71,8 +74,8 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
|
||||
case "quic":
|
||||
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil {
|
||||
ret = append(ret, doq)
|
||||
}else{
|
||||
log.Fatalln("DoQ format error: %v",err)
|
||||
} else {
|
||||
log.Fatalln("DoQ format error: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -208,3 +211,31 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d
|
||||
|
||||
return adapter.DialContext(ctx, metadata, opts...)
|
||||
}
|
||||
|
||||
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
|
||||
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
|
||||
for _, client := range clients {
|
||||
r := client
|
||||
fast.Go(func() (*D.Msg, error) {
|
||||
m, err := r.ExchangeContext(ctx, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
|
||||
return nil, errors.New("server failure")
|
||||
}
|
||||
return m, nil
|
||||
})
|
||||
}
|
||||
|
||||
elm := fast.Wait()
|
||||
if elm == nil {
|
||||
err := errors.New("all DNS requests failed")
|
||||
if fErr := fast.Error(); fErr != nil {
|
||||
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msg = elm
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user