116 lines
2.7 KiB
Go
116 lines
2.7 KiB
Go
package resolver
|
|
|
|
type ResolveBatchInput struct {
|
|
ToResolve []string
|
|
Workers int
|
|
Now int
|
|
StaleKeepSec int
|
|
}
|
|
|
|
type ResolveBatchResult struct {
|
|
DNSStats DNSMetrics
|
|
ResolvedNowDNS int
|
|
ResolvedNowStale int
|
|
UnresolvedAfterAttempts int
|
|
StaleHitsDelta int
|
|
}
|
|
|
|
func ExecuteResolveBatch(
|
|
in ResolveBatchInput,
|
|
resolved map[string][]string,
|
|
domainCache *DomainCacheState,
|
|
cacheSourceForHost func(string) DomainCacheSource,
|
|
resolveHost func(string) ([]string, DNSMetrics),
|
|
logf func(string, ...any),
|
|
) ResolveBatchResult {
|
|
out := ResolveBatchResult{}
|
|
if len(in.ToResolve) == 0 || resolveHost == nil || domainCache == nil {
|
|
return out
|
|
}
|
|
|
|
workers := in.Workers
|
|
if workers < 1 {
|
|
workers = 1
|
|
}
|
|
if workers > 500 {
|
|
workers = 500
|
|
}
|
|
resolveSource := cacheSourceForHost
|
|
if resolveSource == nil {
|
|
resolveSource = func(string) DomainCacheSource { return DomainCacheSourceDirect }
|
|
}
|
|
|
|
type result struct {
|
|
host string
|
|
ips []string
|
|
stats DNSMetrics
|
|
}
|
|
|
|
jobs := make(chan string, len(in.ToResolve))
|
|
results := make(chan result, len(in.ToResolve))
|
|
|
|
for i := 0; i < workers; i++ {
|
|
go func() {
|
|
for host := range jobs {
|
|
ips, stats := resolveHost(host)
|
|
results <- result{host: host, ips: ips, stats: stats}
|
|
}
|
|
}()
|
|
}
|
|
|
|
for _, host := range in.ToResolve {
|
|
jobs <- host
|
|
}
|
|
close(jobs)
|
|
|
|
for i := 0; i < len(in.ToResolve); i++ {
|
|
r := <-results
|
|
out.DNSStats.Merge(r.stats)
|
|
hostErrors := r.stats.TotalErrors()
|
|
if hostErrors > 0 && logf != nil {
|
|
logf("resolve errors for %s: total=%d nxdomain=%d timeout=%d temporary=%d other=%d", r.host, hostErrors, r.stats.NXDomain, r.stats.Timeout, r.stats.Temporary, r.stats.Other)
|
|
}
|
|
if len(r.ips) > 0 {
|
|
if resolved != nil {
|
|
resolved[r.host] = r.ips
|
|
}
|
|
out.ResolvedNowDNS++
|
|
source := resolveSource(r.host)
|
|
domainCache.Set(r.host, source, r.ips, in.Now)
|
|
if logf != nil {
|
|
logf("%s -> %v", r.host, r.ips)
|
|
}
|
|
continue
|
|
}
|
|
|
|
staleApplied := false
|
|
if hostErrors > 0 {
|
|
source := resolveSource(r.host)
|
|
domainCache.SetErrorWithStats(r.host, source, r.stats, in.Now)
|
|
if in.StaleKeepSec > 0 && ShouldUseStaleOnError(r.stats) {
|
|
if staleIPs, staleAge, ok := domainCache.GetStale(r.host, source, in.Now, in.StaleKeepSec); ok {
|
|
out.StaleHitsDelta++
|
|
out.ResolvedNowStale++
|
|
staleApplied = true
|
|
if resolved != nil {
|
|
resolved[r.host] = staleIPs
|
|
}
|
|
if logf != nil {
|
|
logf("cache stale-keep (error)[age=%ds]: %s -> %v", staleAge, r.host, staleIPs)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !staleApplied {
|
|
out.UnresolvedAfterAttempts++
|
|
}
|
|
if logf != nil && resolved != nil {
|
|
if _, ok := resolved[r.host]; !ok {
|
|
logf("%s: no IPs", r.host)
|
|
}
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|