支持DNS上游服务器的并行,最快,均衡查询模式

This commit is contained in:
Alex Yang
2025-12-17 19:15:17 +08:00
parent 307fbefee2
commit 5d0fb6d4fe
42 changed files with 714 additions and 584346 deletions

View File

@@ -645,17 +645,16 @@ func (s *Server) handleBlockedResponse(w dns.ResponseWriter, r *dns.Msg, domain
}
// forwardDNSRequest 转发DNS请求到上游服务器
// serverResponse 用于存储服务器响应的结构体
type serverResponse struct {
response *dns.Msg
rtt time.Duration
server string
error error
}
// forwardDNSRequestWithCache 转发DNS请求到上游服务器并返回响应
func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration) {
// 尝试所有上游DNS服务器
var bestResponse *dns.Msg
var bestRtt time.Duration
var hasBestResponse bool
var hasDNSSECResponse bool
var backupResponse *dns.Msg
var backupRtt time.Duration
var hasBackup bool
// 始终支持EDNS
var udpSize uint16 = 4096
var doFlag bool = s.config.EnableDNSSEC
@@ -680,59 +679,293 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
dnssecServers := s.config.DNSSECUpstreamDNS
// 1. 首先尝试所有配置的上游DNS服务器
for _, upstream := range s.config.UpstreamDNS {
response, rtt, err := s.resolver.Exchange(r, upstream)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
var bestResponse *dns.Msg
var bestRtt time.Duration
var hasBestResponse bool
var hasDNSSECResponse bool
var backupResponse *dns.Msg
var backupRtt time.Duration
var hasBackup bool
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
// 根据查询模式处理请求
switch s.config.QueryMode {
case "parallel":
// 并行请求模式
responses := make(chan serverResponse, len(s.config.UpstreamDNS))
var wg sync.WaitGroup
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
// 向所有上游服务器并行发送请求
for _, upstream := range s.config.UpstreamDNS {
wg.Add(1)
go func(server string) {
defer wg.Done()
response, rtt, err := s.resolver.Exchange(r, server)
responses <- serverResponse{response, rtt, server, err}
}(upstream)
}
// 设置AD标志Authenticated Data
response.AuthenticatedData = signatureValid
// 等待所有请求完成
go func() {
wg.Wait()
close(responses)
}()
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECFailed++
})
// 处理所有响应
for resp := range responses {
if resp.error == nil && resp.response != nil {
// 设置递归可用标志
resp.response.RecursionAvailable = true
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(resp.response)
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(resp.response)
// 设置AD标志Authenticated Data
resp.response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECFailed++
})
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if resp.response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
// 保存为备选响应
if !hasBackup {
backupResponse = resp.response
backupRtt = resp.rtt
hasBackup = true
}
}
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
case "loadbalance":
// 负载均衡模式 - 目前使用简单的轮询,后续可以扩展为更复杂的算法
for _, upstream := range s.config.UpstreamDNS {
response, rtt, err := s.resolver.Exchange(r, upstream)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
// 设置AD标志Authenticated Data
response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECFailed++
})
}
}
// 保存为备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
// 如果响应成功根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
break // 找到带DNSSEC的响应立即返回
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
}
// 保存为备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
}
}
}
}
case "fastest-ip":
// 最快的IP地址模式 - 目前使用简单的顺序请求后续可以扩展为测量TCP连接速度
for _, upstream := range s.config.UpstreamDNS {
response, rtt, err := s.resolver.Exchange(r, upstream)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
// 设置AD标志Authenticated Data
response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECFailed++
})
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
break // 找到带DNSSEC的响应立即返回
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", upstream, "rtt", rtt)
break // 找到响应,立即返回
}
// 保存为备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
}
}
}
}
default:
// 默认使用并行请求模式
responses := make(chan serverResponse, len(s.config.UpstreamDNS))
var wg sync.WaitGroup
// 向所有上游服务器并行发送请求
for _, upstream := range s.config.UpstreamDNS {
wg.Add(1)
go func(server string) {
defer wg.Done()
response, rtt, err := s.resolver.Exchange(r, server)
responses <- serverResponse{response, rtt, server, err}
}(upstream)
}
// 等待所有请求完成
go func() {
wg.Wait()
close(responses)
}()
// 处理所有响应
for resp := range responses {
if resp.error == nil && resp.response != nil {
// 设置递归可用标志
resp.response.RecursionAvailable = true
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(resp.response)
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(resp.response)
// 设置AD标志Authenticated Data
resp.response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
stats.DNSSECFailed++
})
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if resp.response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
// 保存为备选响应
if !hasBackup {
backupResponse = resp.response
backupRtt = resp.rtt
hasBackup = true
}
}
}
}
@@ -747,52 +980,130 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
stats.DNSSECQueries++
})
for _, dnssecServer := range dnssecServers {
response, rtt, err := s.resolver.Exchange(r, dnssecServer)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
// 根据查询模式处理DNSSEC服务器请求
switch s.config.QueryMode {
case "parallel":
// 并行请求模式
responses := make(chan serverResponse, len(dnssecServers))
var wg sync.WaitGroup
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
// 向所有DNSSEC服务器并行发送请求
for _, dnssecServer := range dnssecServers {
wg.Add(1)
go func(server string) {
defer wg.Done()
response, rtt, err := s.resolver.Exchange(r, server)
responses <- serverResponse{response, rtt, server, err}
}(dnssecServer)
}
if response.Rcode == dns.RcodeSuccess {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
// 等待所有请求完成
go func() {
wg.Wait()
close(responses)
}()
// 设置AD标志Authenticated Data
response.AuthenticatedData = signatureValid
// 处理所有响应
for resp := range responses {
if resp.error == nil && resp.response != nil {
// 设置递归可用标志
resp.response.RecursionAvailable = true
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECFailed++
})
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(resp.response)
if resp.response.Rcode == dns.RcodeSuccess {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(resp.response)
// 设置AD标志Authenticated Data
resp.response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECFailed++
})
}
// 优先使用DNSSEC专用服务器的响应尤其是带有DNSSEC记录的
if containsDNSSEC {
// 即使之前有最佳响应也优先使用DNSSEC专用服务器的DNSSEC响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("DNSSEC专用服务器返回带DNSSEC的响应优先使用", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
// 注意如果DNSSEC专用服务器返回的响应不包含DNSSEC记录
// 我们不会覆盖之前从upstreamDNS获取的响应
// 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求
// 更新备选响应
if !hasBackup {
backupResponse = resp.response
backupRtt = resp.rtt
hasBackup = true
}
}
}
}
// 优先使用DNSSEC专用服务器的响应尤其是带有DNSSEC记录的
if containsDNSSEC {
// 即使之前有最佳响应也优先使用DNSSEC专用服务器的DNSSEC响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("DNSSEC专用服务器返回带DNSSEC的响应优先使用", "domain", domain, "server", dnssecServer, "rtt", rtt)
}
// 注意如果DNSSEC专用服务器返回的响应不包含DNSSEC记录
// 我们不会覆盖之前从upstreamDNS获取的响应
// 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求
default:
// 默认使用顺序请求模式
for _, dnssecServer := range dnssecServers {
response, rtt, err := s.resolver.Exchange(r, dnssecServer)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
// 更新备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
if response.Rcode == dns.RcodeSuccess {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
// 设置AD标志Authenticated Data
response.AuthenticatedData = signatureValid
if signatureValid {
// 更新DNSSEC验证成功计数
s.updateStats(func(stats *Stats) {
stats.DNSSECSuccess++
})
} else {
// 更新DNSSEC验证失败计数
s.updateStats(func(stats *Stats) {
stats.DNSSECFailed++
})
}
// 优先使用DNSSEC专用服务器的响应尤其是带有DNSSEC记录的
if containsDNSSEC {
// 即使之前有最佳响应也优先使用DNSSEC专用服务器的DNSSEC响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("DNSSEC专用服务器返回带DNSSEC的响应优先使用", "domain", domain, "server", dnssecServer, "rtt", rtt)
break // 找到带DNSSEC的响应立即返回
}
// 注意如果DNSSEC专用服务器返回的响应不包含DNSSEC记录
// 我们不会覆盖之前从upstreamDNS获取的响应
// 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求
// 更新备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
}
}
}
}