mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-12-18 13:14:36 +03:00
Patch: if A-ttl is not expired but AAAA-ttl is expired, we should only send AAAA-query and vice versa
1. if A-ttl is not expired but AAAA-ttl is expired, we should only send AAAA-query and vice versa 2. `sendQuery` send each query in new goroutine so there is no need to run it in new goroutine.
This commit is contained in:
@@ -102,20 +102,8 @@ func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|||||||
switch req.reqType {
|
switch req.reqType {
|
||||||
case dnsmessage.TypeA:
|
case dnsmessage.TypeA:
|
||||||
c.pub.Publish(req.domain+"4", nil)
|
c.pub.Publish(req.domain+"4", nil)
|
||||||
if !c.disableCache {
|
|
||||||
_, _, err := rec.AAAA.getIPs()
|
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
|
||||||
c.pub.Publish(req.domain+"6", nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case dnsmessage.TypeAAAA:
|
case dnsmessage.TypeAAAA:
|
||||||
c.pub.Publish(req.domain+"6", nil)
|
c.pub.Publish(req.domain+"6", nil)
|
||||||
if !c.disableCache {
|
|
||||||
_, _, err := rec.A.getIPs()
|
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
|
||||||
c.pub.Publish(req.domain+"4", nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
@@ -124,13 +112,13 @@ func (c *CacheController) updateIP(req *dnsRequest, ipRec *IPRecord) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, int32, error) {
|
func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, int32, bool, bool, error) {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
record, found := c.ips[domain]
|
record, found := c.ips[domain]
|
||||||
c.RUnlock()
|
c.RUnlock()
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return nil, 0, errRecordNotFound
|
return nil, 0, true, true, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
@@ -139,43 +127,55 @@ func (c *CacheController) findIPsForDomain(domain string, option dns_feature.IPO
|
|||||||
|
|
||||||
mergeReq := option.IPv4Enable && option.IPv6Enable
|
mergeReq := option.IPv4Enable && option.IPv6Enable
|
||||||
|
|
||||||
|
isARecordExpired := true
|
||||||
if option.IPv4Enable {
|
if option.IPv4Enable {
|
||||||
ips, ttl, err := record.A.getIPs()
|
ips, ttl, err := record.A.getIPs()
|
||||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
if ttl > 0 {
|
||||||
return ips, ttl, err
|
isARecordExpired = false
|
||||||
|
}
|
||||||
|
if !mergeReq {
|
||||||
|
return ips, ttl, isARecordExpired, true, err
|
||||||
}
|
}
|
||||||
if ttl < rTTL {
|
if ttl < rTTL {
|
||||||
rTTL = ttl
|
rTTL = ttl
|
||||||
}
|
}
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
allIPs = append(allIPs, ips...)
|
allIPs = append(allIPs, ips...)
|
||||||
} else {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAAAARecordExpired := true
|
||||||
if option.IPv6Enable {
|
if option.IPv6Enable {
|
||||||
ips, ttl, err := record.AAAA.getIPs()
|
ips, ttl, err := record.AAAA.getIPs()
|
||||||
if !mergeReq || go_errors.Is(err, errRecordNotFound) {
|
if ttl > 0 {
|
||||||
return ips, ttl, err
|
isAAAARecordExpired = false
|
||||||
|
}
|
||||||
|
if !mergeReq {
|
||||||
|
return ips, ttl, true, isAAAARecordExpired, err
|
||||||
}
|
}
|
||||||
if ttl < rTTL {
|
if ttl < rTTL {
|
||||||
rTTL = ttl
|
rTTL = ttl
|
||||||
}
|
}
|
||||||
if len(ips) > 0 {
|
if len(ips) > 0 {
|
||||||
allIPs = append(allIPs, ips...)
|
allIPs = append(allIPs, ips...)
|
||||||
} else {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if go_errors.Is(errs[0], errRecordNotFound) || go_errors.Is(errs[1], errRecordNotFound) {
|
||||||
|
return nil, 0, isARecordExpired, isAAAARecordExpired, errRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(allIPs) > 0 {
|
if len(allIPs) > 0 {
|
||||||
return allIPs, rTTL, nil
|
return allIPs, rTTL, isARecordExpired, isAAAARecordExpired, nil
|
||||||
}
|
}
|
||||||
if go_errors.Is(errs[0], errs[1]) {
|
if go_errors.Is(errs[0], errs[1]) {
|
||||||
return nil, rTTL, errs[0]
|
return nil, rTTL, isARecordExpired, isAAAARecordExpired, errs[0]
|
||||||
}
|
}
|
||||||
return nil, rTTL, errors.Combine(errs...)
|
return nil, rTTL, isARecordExpired, isAAAARecordExpired, errors.Combine(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
|
func (c *CacheController) registerSubscribers(domain string, option dns_feature.IPOption) (sub4 *pubsub.Subscriber, sub6 *pubsub.Subscriber) {
|
||||||
|
|||||||
@@ -229,10 +229,22 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||||
defer closeSubscribers(sub4, sub6)
|
defer closeSubscribers(sub4, sub6)
|
||||||
|
|
||||||
|
queryOption := option
|
||||||
|
|
||||||
if s.cacheController.disableCache {
|
if s.cacheController.disableCache {
|
||||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||||
} else {
|
} else {
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, isARecordExpired, isAAAARecordExpired, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
|
if sub4 != nil && !isARecordExpired {
|
||||||
|
sub4.Close()
|
||||||
|
sub4 = nil
|
||||||
|
queryOption.IPv4Enable = false
|
||||||
|
}
|
||||||
|
if sub6 != nil && !isAAAARecordExpired {
|
||||||
|
sub6.Close()
|
||||||
|
sub6 = nil
|
||||||
|
queryOption.IPv6Enable = false
|
||||||
|
}
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
if !go_errors.Is(err, errRecordNotFound) {
|
||||||
if ttl > 0 {
|
if ttl > 0 {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||||
@@ -241,14 +253,14 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
}
|
}
|
||||||
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
||||||
go s.sendQuery(ctx, nil, fqdn, option)
|
s.sendQuery(ctx, nil, fqdn, queryOption)
|
||||||
return ips, 1, err
|
return ips, 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noResponseErrCh := make(chan error, 2)
|
noResponseErrCh := make(chan error, 2)
|
||||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
s.sendQuery(ctx, noResponseErrCh, fqdn, queryOption)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if sub4 != nil {
|
if sub4 != nil {
|
||||||
@@ -272,7 +284,7 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, _, _, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||||
var rTTL uint32
|
var rTTL uint32
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
|
|||||||
@@ -196,10 +196,22 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_
|
|||||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||||
defer closeSubscribers(sub4, sub6)
|
defer closeSubscribers(sub4, sub6)
|
||||||
|
|
||||||
|
queryOption := option
|
||||||
|
|
||||||
if s.cacheController.disableCache {
|
if s.cacheController.disableCache {
|
||||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||||
} else {
|
} else {
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, isARecordExpired, isAAAARecordExpired, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
|
if sub4 != nil && !isARecordExpired {
|
||||||
|
sub4.Close()
|
||||||
|
sub4 = nil
|
||||||
|
queryOption.IPv4Enable = false
|
||||||
|
}
|
||||||
|
if sub6 != nil && !isAAAARecordExpired {
|
||||||
|
sub6.Close()
|
||||||
|
sub6 = nil
|
||||||
|
queryOption.IPv6Enable = false
|
||||||
|
}
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
if !go_errors.Is(err, errRecordNotFound) {
|
||||||
if ttl > 0 {
|
if ttl > 0 {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||||
@@ -208,14 +220,14 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_
|
|||||||
}
|
}
|
||||||
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
||||||
go s.sendQuery(ctx, nil, fqdn, option)
|
s.sendQuery(ctx, nil, fqdn, queryOption)
|
||||||
return ips, 1, err
|
return ips, 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noResponseErrCh := make(chan error, 2)
|
noResponseErrCh := make(chan error, 2)
|
||||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
s.sendQuery(ctx, noResponseErrCh, fqdn, queryOption)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if sub4 != nil {
|
if sub4 != nil {
|
||||||
@@ -239,7 +251,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, _, _, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||||
var rTTL uint32
|
var rTTL uint32
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
|
|||||||
@@ -224,10 +224,22 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||||
defer closeSubscribers(sub4, sub6)
|
defer closeSubscribers(sub4, sub6)
|
||||||
|
|
||||||
|
queryOption := option
|
||||||
|
|
||||||
if s.cacheController.disableCache {
|
if s.cacheController.disableCache {
|
||||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||||
} else {
|
} else {
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, isARecordExpired, isAAAARecordExpired, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
|
if sub4 != nil && !isARecordExpired {
|
||||||
|
sub4.Close()
|
||||||
|
sub4 = nil
|
||||||
|
queryOption.IPv4Enable = false
|
||||||
|
}
|
||||||
|
if sub6 != nil && !isAAAARecordExpired {
|
||||||
|
sub6.Close()
|
||||||
|
sub6 = nil
|
||||||
|
queryOption.IPv6Enable = false
|
||||||
|
}
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
if !go_errors.Is(err, errRecordNotFound) {
|
||||||
if ttl > 0 {
|
if ttl > 0 {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||||
@@ -236,14 +248,14 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
}
|
}
|
||||||
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
||||||
go s.sendQuery(ctx, nil, fqdn, option)
|
s.sendQuery(ctx, nil, fqdn, queryOption)
|
||||||
return ips, 1, err
|
return ips, 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noResponseErrCh := make(chan error, 2)
|
noResponseErrCh := make(chan error, 2)
|
||||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
s.sendQuery(ctx, noResponseErrCh, fqdn, queryOption)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if sub4 != nil {
|
if sub4 != nil {
|
||||||
@@ -267,7 +279,7 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, option dns_f
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, _, _, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||||
var rTTL uint32
|
var rTTL uint32
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
|
|||||||
@@ -174,10 +174,22 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option d
|
|||||||
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
|
||||||
defer closeSubscribers(sub4, sub6)
|
defer closeSubscribers(sub4, sub6)
|
||||||
|
|
||||||
|
queryOption := option
|
||||||
|
|
||||||
if s.cacheController.disableCache {
|
if s.cacheController.disableCache {
|
||||||
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
|
||||||
} else {
|
} else {
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, isARecordExpired, isAAAARecordExpired, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
|
if sub4 != nil && !isARecordExpired {
|
||||||
|
sub4.Close()
|
||||||
|
sub4 = nil
|
||||||
|
queryOption.IPv4Enable = false
|
||||||
|
}
|
||||||
|
if sub6 != nil && !isAAAARecordExpired {
|
||||||
|
sub6.Close()
|
||||||
|
sub6 = nil
|
||||||
|
queryOption.IPv6Enable = false
|
||||||
|
}
|
||||||
if !go_errors.Is(err, errRecordNotFound) {
|
if !go_errors.Is(err, errRecordNotFound) {
|
||||||
if ttl > 0 {
|
if ttl > 0 {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
|
||||||
@@ -186,14 +198,14 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option d
|
|||||||
}
|
}
|
||||||
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
|
||||||
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
|
||||||
go s.sendQuery(ctx, nil, fqdn, option)
|
s.sendQuery(ctx, nil, fqdn, queryOption)
|
||||||
return ips, 1, err
|
return ips, 1, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noResponseErrCh := make(chan error, 2)
|
noResponseErrCh := make(chan error, 2)
|
||||||
s.sendQuery(ctx, noResponseErrCh, fqdn, option)
|
s.sendQuery(ctx, noResponseErrCh, fqdn, queryOption)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if sub4 != nil {
|
if sub4 != nil {
|
||||||
@@ -217,7 +229,7 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option d
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ips, ttl, err := s.cacheController.findIPsForDomain(fqdn, option)
|
ips, ttl, _, _, err := s.cacheController.findIPsForDomain(fqdn, option)
|
||||||
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
|
||||||
var rTTL uint32
|
var rTTL uint32
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user