支持DNS上游服务器的并行,最快,均衡查询模式
This commit is contained in:
497
dns/server.go
497
dns/server.go
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user