diff --git a/adapter/dns.go b/adapter/dns.go index 8f065e2e82..822bbd3283 100644 --- a/adapter/dns.go +++ b/adapter/dns.go @@ -31,12 +31,13 @@ type DNSClient interface { } type DNSQueryOptions struct { - Transport DNSTransport - Strategy C.DomainStrategy - LookupStrategy C.DomainStrategy - DisableCache bool - RewriteTTL *uint32 - ClientSubnet netip.Prefix + Transport DNSTransport + Strategy C.DomainStrategy + LookupStrategy C.DomainStrategy + DisableCache bool + RewriteTTL *uint32 + ClientSubnet netip.Prefix + CnameFlattening bool } func DNSQueryOptionsFrom(ctx context.Context, options *option.DomainResolveOptions) (*DNSQueryOptions, error) { diff --git a/dns/client.go b/dns/client.go index 939ca48cbf..e7c820a157 100644 --- a/dns/client.go +++ b/dns/client.go @@ -44,6 +44,7 @@ type Client struct { cacheLock compatible.Map[dns.Question, chan struct{}] transportCache freelru.Cache[transportCacheKey, *dns.Msg] transportCacheLock compatible.Map[dns.Question, chan struct{}] + cnameFlattening bool } type ClientOptions struct { @@ -53,6 +54,7 @@ type ClientOptions struct { IndependentCache bool CacheCapacity uint32 ClientSubnet netip.Prefix + CnameFlattening bool RDRC func() adapter.RDRCStore Logger logger.ContextLogger } @@ -64,6 +66,7 @@ func NewClient(options ClientOptions) *Client { disableExpire: options.DisableExpire, independentCache: options.IndependentCache, clientSubnet: options.ClientSubnet, + cnameFlattening: options.CnameFlattening, initRDRCFunc: options.RDRC, logger: options.Logger, } @@ -193,41 +196,41 @@ func (c *Client) Exchange(ctx context.Context, transport adapter.DNSTransport, m return nil, err } } - /*if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA { - validResponse := response - loop: - for { - var ( - addresses int - queryCNAME string - ) - for _, rawRR := range validResponse.Answer { - switch rr := rawRR.(type) { - case *dns.A: - break loop - case *dns.AAAA: - break loop - case *dns.CNAME: - queryCNAME = rr.Target + if c.cnameFlattening || options.CnameFlattening { + if question.Qtype == dns.TypeA || question.Qtype == dns.TypeAAAA { + validResponse := response + loop: + for { + var queryCNAME string + for _, rawRR := range validResponse.Answer { + switch rr := rawRR.(type) { + case *dns.A: + break loop + case *dns.AAAA: + break loop + case *dns.CNAME: + queryCNAME = rr.Target + } + } + if queryCNAME == "" { + break + } + exMessage := *message + exMessage.Question = []dns.Question{{ + Name: queryCNAME, + Qtype: question.Qtype, + Qclass: question.Qclass, + }} + validResponse, err = c.Exchange(ctx, transport, &exMessage, options, responseChecker) + if err != nil { + return nil, err } } - if queryCNAME == "" { - break - } - exMessage := *message - exMessage.Question = []dns.Question{{ - Name: queryCNAME, - Qtype: question.Qtype, - }} - validResponse, err = c.Exchange(ctx, transport, &exMessage, options, responseChecker) - if err != nil { - return nil, err + if validResponse != response { + response.Answer = append(response.Answer, validResponse.Answer...) } } - if validResponse != response { - response.Answer = append(response.Answer, validResponse.Answer...) - } - }*/ + } disableCache = disableCache || (response.Rcode != dns.RcodeSuccess && response.Rcode != dns.RcodeNameError) if responseChecker != nil { var rejected bool diff --git a/dns/router.go b/dns/router.go index e82cab2907..04fcd2e481 100644 --- a/dns/router.go +++ b/dns/router.go @@ -13,7 +13,7 @@ import ( "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" R "github.com/sagernet/sing-box/route/rule" - "github.com/sagernet/sing-tun" + tun "github.com/sagernet/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" @@ -65,7 +65,8 @@ func NewRouter(ctx context.Context, logFactory log.Factory, options option.DNSOp } return cacheFile }, - Logger: router.logger, + CnameFlattening: options.DNSClientOptions.CnameFlattening, + Logger: router.logger, }) if options.ReverseMapping { router.dnsReverseMapping = common.Must1(freelru.NewSharded[netip.Addr, string](1024, maphash.NewHasher[netip.Addr]().Hash32)) diff --git a/option/dns.go b/option/dns.go index 4c1ac208bf..404173fed3 100644 --- a/option/dns.go +++ b/option/dns.go @@ -109,6 +109,7 @@ type DNSClientOptions struct { IndependentCache bool `json:"independent_cache,omitempty"` CacheCapacity uint32 `json:"cache_capacity,omitempty"` ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` + CnameFlattening bool `json:"cname_flattening,omitempty"` } type LegacyDNSFakeIPOptions struct {