DNS: Support returning upstream TTL to clients (#4526)

Closes https://github.com/XTLS/Xray-core/issues/4527
This commit is contained in:
Meo597 2025-03-24 21:33:56 +08:00 committed by GitHub
parent 2d3210e4b8
commit 4afe2d0cff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 168 additions and 157 deletions

View file

@ -230,60 +230,62 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
}
}
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
s.RLock()
record, found := s.ips[domain]
s.RUnlock()
if !found {
return nil, errRecordNotFound
return nil, 0, errRecordNotFound
}
var err4 error
var err6 error
var ips []net.Address
var ip6 []net.Address
var ttl uint32
if option.IPv4Enable {
ips, err4 = record.A.getIPs()
ips, ttl, err4 = record.A.getIPs()
}
if option.IPv6Enable {
ip6, err6 = record.AAAA.getIPs()
ip6, ttl, err6 = record.AAAA.getIPs()
ips = append(ips, ip6...)
}
if len(ips) > 0 {
return toNetIP(ips)
netips, err := toNetIP(ips)
return netips, ttl, err
}
if err4 != nil {
return nil, err4
return nil, 0, err4
}
if err6 != nil {
return nil, err6
return nil, 0, err6
}
return nil, dns_feature.ErrEmptyResponse
return nil, 0, dns_feature.ErrEmptyResponse
}
// QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) {
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, uint32, error) {
fqdn := Fqdn(domain)
option = ResolveIpOptionOverride(s.queryStrategy, option)
if !option.IPv4Enable && !option.IPv6Enable {
return nil, dns_feature.ErrEmptyResponse
return nil, 0, dns_feature.ErrEmptyResponse
}
if disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name)
} else {
ips, err := s.findIPsForDomain(fqdn, option)
ips, ttl, err := s.findIPsForDomain(fqdn, option)
if err == nil || err == dns_feature.ErrEmptyResponse {
errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, err
return ips, ttl, err
}
}
@ -317,15 +319,15 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
ips, ttl, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
return ips, err
return ips, ttl, err
}
select {
case <-ctx.Done():
return nil, ctx.Err()
return nil, 0, ctx.Err()
case <-done:
}
}