128 lines
2.6 KiB
Go
128 lines
2.6 KiB
Go
package resolver
|
|
|
|
import "strings"
|
|
|
|
func RunTimeoutQuarantineRecheck(
|
|
domains []string,
|
|
now int,
|
|
limit int,
|
|
workers int,
|
|
domainCache *DomainCacheState,
|
|
cacheSourceForHost func(string) DomainCacheSource,
|
|
resolveHost func(string) ([]string, DNSMetrics),
|
|
) ResolverTimeoutRecheckStats {
|
|
stats := ResolverTimeoutRecheckStats{}
|
|
if limit <= 0 || now <= 0 || domainCache == nil || resolveHost == nil {
|
|
return stats
|
|
}
|
|
if workers < 1 {
|
|
workers = 1
|
|
}
|
|
if workers > 200 {
|
|
workers = 200
|
|
}
|
|
|
|
resolveSource := cacheSourceForHost
|
|
if resolveSource == nil {
|
|
resolveSource = func(string) DomainCacheSource { return DomainCacheSourceDirect }
|
|
}
|
|
|
|
seen := map[string]struct{}{}
|
|
capHint := len(domains)
|
|
if capHint > limit {
|
|
capHint = limit
|
|
}
|
|
candidates := make([]string, 0, capHint)
|
|
for _, raw := range domains {
|
|
host := strings.TrimSpace(strings.ToLower(raw))
|
|
if host == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[host]; ok {
|
|
continue
|
|
}
|
|
seen[host] = struct{}{}
|
|
|
|
source := resolveSource(host)
|
|
if _, _, ok := domainCache.GetQuarantine(host, source, now); !ok {
|
|
continue
|
|
}
|
|
kind, ok := domainCache.GetLastErrorKind(host, source)
|
|
if !ok || kind != DNSErrorTimeout {
|
|
continue
|
|
}
|
|
candidates = append(candidates, host)
|
|
if len(candidates) >= limit {
|
|
break
|
|
}
|
|
}
|
|
if len(candidates) == 0 {
|
|
return stats
|
|
}
|
|
|
|
recoveredIPSet := map[string]struct{}{}
|
|
|
|
type result struct {
|
|
host string
|
|
source DomainCacheSource
|
|
ips []string
|
|
dns DNSMetrics
|
|
}
|
|
|
|
jobs := make(chan string, len(candidates))
|
|
results := make(chan result, len(candidates))
|
|
|
|
for i := 0; i < workers; i++ {
|
|
go func() {
|
|
for host := range jobs {
|
|
src := resolveSource(host)
|
|
ips, dnsStats := resolveHost(host)
|
|
results <- result{host: host, source: src, ips: ips, dns: dnsStats}
|
|
}
|
|
}()
|
|
}
|
|
|
|
for _, host := range candidates {
|
|
jobs <- host
|
|
}
|
|
close(jobs)
|
|
|
|
for i := 0; i < len(candidates); i++ {
|
|
r := <-results
|
|
stats.Checked++
|
|
if len(r.ips) > 0 {
|
|
for _, ip := range r.ips {
|
|
ip = strings.TrimSpace(ip)
|
|
if ip == "" {
|
|
continue
|
|
}
|
|
recoveredIPSet[ip] = struct{}{}
|
|
}
|
|
domainCache.Set(r.host, r.source, r.ips, now)
|
|
stats.Recovered++
|
|
continue
|
|
}
|
|
if r.dns.TotalErrors() > 0 {
|
|
domainCache.SetErrorWithStats(r.host, r.source, r.dns, now)
|
|
}
|
|
kind, ok := ClassifyHostErrorKind(r.dns)
|
|
if !ok {
|
|
stats.NoSignal++
|
|
continue
|
|
}
|
|
switch kind {
|
|
case DNSErrorTimeout:
|
|
stats.StillTimeout++
|
|
case DNSErrorNXDomain:
|
|
stats.NowNXDomain++
|
|
case DNSErrorTemporary:
|
|
stats.NowTemporary++
|
|
default:
|
|
stats.NowOther++
|
|
}
|
|
}
|
|
|
|
stats.RecoveredIPs = len(recoveredIPSet)
|
|
return stats
|
|
}
|