1.1.1修复

This commit is contained in:
Alex Yang
2025-12-19 12:44:57 +08:00
parent b576996ede
commit 1f3f5708a3
72 changed files with 3476 additions and 83 deletions

View File

@@ -59,6 +59,8 @@ type QueryLog struct {
FromCache bool // 是否来自缓存
DNSSEC bool // 是否使用了DNSSEC
EDNS bool // 是否使用了EDNS
DNSServer string // 使用的DNS服务器
DNSSECServer string // 使用的DNSSEC专用服务器
}
// StatsData 用于持久化的统计数据结构
@@ -230,14 +232,14 @@ func (s *Server) Start() error {
s.startTime = time.Now()
s.server = &dns.Server{
Addr: fmt.Sprintf(":%d", s.config.Port),
Addr: fmt.Sprintf("0.0.0.0:%d", s.config.Port),
Net: "udp",
Handler: dns.HandlerFunc(s.handleDNSRequest),
}
// 保存TCP服务器实例以便在Stop方法中关闭
s.tcpServer = &dns.Server{
Addr: fmt.Sprintf(":%d", s.config.Port),
Addr: fmt.Sprintf("0.0.0.0:%d", s.config.Port),
Net: "tcp",
Handler: dns.HandlerFunc(s.handleDNSRequest),
}
@@ -368,7 +370,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "")
return
}
@@ -385,7 +387,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, false, true)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, false, true, "缓存", "无")
return
}
@@ -407,7 +409,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true, "无", "无")
return
}
@@ -480,7 +482,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
// 添加查询日志 - 标记为缓存
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true, "缓存", "无")
logger.Debug("从缓存返回DNS响应", "domain", domain, "type", queryType, "dnssec", cachedDNSSEC)
return
}
@@ -489,10 +491,12 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
var response *dns.Msg
var rtt time.Duration
var queryAttempts []string
var dnsServer string
var dnssecServer string
// 1. 首先尝试直接查询原始域名
queryAttempts = append(queryAttempts, domain)
response, rtt = s.forwardDNSRequestWithCache(r, domain)
response, rtt, dnsServer, dnssecServer = s.forwardDNSRequestWithCache(r, domain)
// 2. 如果直接查询失败且配置了prefixDomain尝试添加前缀
if (response == nil || response.Rcode != dns.RcodeSuccess) && len(s.config.PrefixDomain) > 0 {
@@ -517,8 +521,8 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
Qclass: originalQuestion.Qclass,
}
// 转发请求
response, rtt = s.forwardDNSRequestWithCache(newReq, fullDomain)
// 查询带有前缀的域名
response, rtt, dnsServer, dnssecServer = s.forwardDNSRequestWithCache(newReq, fullDomain)
if response != nil && response.Rcode == dns.RcodeSuccess {
logger.Debug("使用prefixDomain查询成功", "fullDomain", fullDomain, "originalDomain", domain)
break // 找到成功的响应,退出循环
@@ -595,7 +599,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
// 添加查询日志 - 标记为实时
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true, dnsServer, dnssecServer)
}
// handleHostsResponse 处理hosts文件匹配的响应
@@ -708,7 +712,7 @@ type serverResponse struct {
}
// forwardDNSRequestWithCache 转发DNS请求到上游服务器并返回响应
func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration) {
func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration, string, string) {
// 始终支持EDNS
var udpSize uint16 = 4096
var doFlag bool = s.config.EnableDNSSEC
@@ -746,9 +750,16 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}
}
// 2. 如果没有匹配的域名特定配置使用默认的上游DNS服务器
// 2. 如果没有匹配的域名特定配置
if !domainMatched {
selectedUpstreamDNS = s.config.UpstreamDNS
// 如果启用了DNSSEC且有配置DNSSEC专用服务器则使用DNSSEC专用服务器
if s.config.EnableDNSSEC && len(s.config.DNSSECUpstreamDNS) > 0 {
selectedUpstreamDNS = s.config.DNSSECUpstreamDNS
logger.Debug("使用DNSSEC专用服务器", "servers", selectedUpstreamDNS)
} else {
// 否则使用默认的上游DNS服务器
selectedUpstreamDNS = s.config.UpstreamDNS
}
}
// 1. 首先尝试所有配置的上游DNS服务器
@@ -759,6 +770,8 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
var backupResponse *dns.Msg
var backupRtt time.Duration
var hasBackup bool
var usedDNSServer string
var usedDNSSECServer string
// 根据查询模式处理请求
switch s.config.QueryMode {
@@ -832,21 +845,47 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}
}
// 如果响应成功根据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)
// 如果响应成功或为NXDOMAIN根据DNSSEC状态选择最佳响应
if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError {
// 检查当前使用的服务器是否是DNSSEC专用服务器
for _, dnssecServer := range dnssecServers {
if dnssecServer == resp.server {
usedDNSSECServer = resp.server
break
}
}
if resp.response.Rcode == dns.RcodeSuccess {
// 处理成功响应
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = true
usedDNSServer = resp.server
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
} else if resp.response.Rcode == dns.RcodeNameError {
// 处理NXDOMAIN响应
// 如果还没有最佳响应或者最佳响应也是NXDOMAIN优先选择更快的NXDOMAIN响应
if !hasBestResponse || bestResponse.Rcode == dns.RcodeNameError {
// 如果还没有最佳响应,或者当前响应更快,更新最佳响应
if !hasBestResponse || resp.rtt < bestRtt {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
logger.Debug("找到NXDOMAIN最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
}
}
// 保存为备选响应
if !hasBackup {
@@ -900,21 +939,46 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
// 如果响应成功或为NXDOMAIN根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError {
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
usedDNSServer = selectedServer
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == selectedServer {
usedDNSSECServer = selectedServer
break
}
}
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt)
} else {
// 没有带DNSSEC的响应时保存成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
usedDNSServer = selectedServer
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == selectedServer {
usedDNSSECServer = selectedServer
break
}
}
logger.Debug("找到最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt)
}
} else if response.Rcode == dns.RcodeNameError {
// 处理NXDOMAIN响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt)
} else {
// 没有带DNSSEC的响应时保存成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt)
usedDNSServer = selectedServer
logger.Debug("找到NXDOMAIN响应", "domain", domain, "server", selectedServer, "rtt", rtt)
}
// 保存为备选响应
if !hasBackup {
@@ -968,21 +1032,46 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}
}
// 如果响应成功根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
// 如果响应成功或为NXDOMAIN根据DNSSEC状态选择最佳响应
if response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError {
if response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
usedDNSServer = fastestServer
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == fastestServer {
usedDNSSECServer = fastestServer
break
}
}
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
} else {
// 没有带DNSSEC的响应时保存成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
usedDNSServer = fastestServer
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == fastestServer {
usedDNSSECServer = fastestServer
break
}
}
logger.Debug("找到最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
}
} else if response.Rcode == dns.RcodeNameError {
// 处理NXDOMAIN响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
hasDNSSECResponse = true
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
} else {
// 没有带DNSSEC的响应时保存成功响应
bestResponse = response
bestRtt = rtt
hasBestResponse = true
logger.Debug("找到最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt)
usedDNSServer = fastestServer
logger.Debug("找到NXDOMAIN响应", "domain", domain, "server", fastestServer, "rtt", rtt)
}
// 保存为备选响应
if !hasBackup {
@@ -1050,21 +1139,52 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}
}
// 如果响应成功根据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)
// 如果响应成功或为NXDOMAIN根据DNSSEC状态选择最佳响应
if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError {
if resp.response.Rcode == dns.RcodeSuccess {
// 优先选择带有DNSSEC记录的响应
if containsDNSSEC {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = true
usedDNSServer = resp.server
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == resp.server {
usedDNSSECServer = resp.server
break
}
}
logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if !hasBestResponse {
// 没有带DNSSEC的响应时保存第一个成功响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
// 如果当前使用的服务器是DNSSEC专用服务器同时设置usedDNSSECServer
for _, dnssecServer := range dnssecServers {
if dnssecServer == resp.server {
usedDNSSECServer = resp.server
break
}
}
logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
} else if resp.response.Rcode == dns.RcodeNameError {
// 处理NXDOMAIN响应
// 如果还没有最佳响应或者最佳响应也是NXDOMAIN优先选择更快的NXDOMAIN响应
if !hasBestResponse || bestResponse.Rcode == dns.RcodeNameError {
// 如果还没有最佳响应,或者当前响应更快,更新最佳响应
if !hasBestResponse || resp.rtt < bestRtt {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
logger.Debug("找到NXDOMAIN最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
}
}
}
// 保存为备选响应
if !hasBackup {
@@ -1137,6 +1257,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
containsDNSSEC := s.hasDNSSECRecords(resp.response)
if resp.response.Rcode == dns.RcodeSuccess {
// 无论响应是否包含DNSSEC记录只要使用了DNSSEC专用服务器就设置usedDNSSECServer
usedDNSSECServer = resp.server
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(resp.response)
@@ -1197,6 +1320,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
containsDNSSEC := s.hasDNSSECRecords(response)
if response.Rcode == dns.RcodeSuccess {
// 无论响应是否包含DNSSEC记录只要使用了DNSSEC专用服务器就设置usedDNSSECServer
usedDNSSECServer = selectedServer
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
@@ -1257,6 +1383,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
containsDNSSEC := s.hasDNSSECRecords(response)
if response.Rcode == dns.RcodeSuccess {
// 无论响应是否包含DNSSEC记录只要使用了DNSSEC专用服务器就设置usedDNSSECServer
usedDNSSECServer = fastestServer
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
@@ -1315,6 +1444,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
containsDNSSEC := s.hasDNSSECRecords(response)
if response.Rcode == dns.RcodeSuccess {
// 无论响应是否包含DNSSEC记录只要使用了DNSSEC专用服务器就设置usedDNSSECServer
usedDNSSECServer = dnssecServer
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
@@ -1366,9 +1498,43 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
// 检查最佳响应是否包含DNSSEC记录
bestHasDNSSEC := s.hasDNSSECRecords(bestResponse)
// 如果启用了DNSSEC且最佳响应不包含DNSSEC记录使用upstreamDNS的解析结果
// 如果启用了DNSSEC且最佳响应不包含DNSSEC记录尝试使用本地解析
if s.config.EnableDNSSEC && !bestHasDNSSEC {
logger.Debug("最佳响应不包含DNSSEC记录使用upstreamDNS的解析结果", "domain", domain)
logger.Debug("最佳响应不包含DNSSEC记录尝试使用本地解析", "domain", domain)
if ip, exists := s.shieldManager.GetHostsIP(domain); exists {
// 本地解析成功,构建响应
localResponse := new(dns.Msg)
localResponse.SetReply(r)
localResponse.RecursionAvailable = true
localResponse.AuthenticatedData = false
localResponse.Rcode = dns.RcodeSuccess
if len(r.Question) > 0 {
q := r.Question[0]
answer := new(dns.A)
answer.Hdr = dns.RR_Header{
Name: q.Name,
Rrtype: q.Qtype,
Class: q.Qclass,
Ttl: 300,
}
answer.A = net.ParseIP(ip)
localResponse.Answer = append(localResponse.Answer, answer)
}
// 记录解析域名统计
s.updateResolvedDomainStats(domain)
// 更新域名的DNSSEC状态为false
s.updateDomainDNSSECStatus(domain, false)
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
logger.Debug("使用本地解析结果", "domain", domain, "ip", ip)
return localResponse, 0, "", ""
}
}
// 记录解析域名统计
@@ -1377,12 +1543,14 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
// 更新域名的DNSSEC状态
if bestHasDNSSEC {
s.updateDomainDNSSECStatus(domain, true)
} else {
s.updateDomainDNSSECStatus(domain, false)
}
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
return bestResponse, bestRtt
return bestResponse, bestRtt, usedDNSServer, usedDNSSECServer
}
// 如果有备选响应,返回该响应
@@ -1390,11 +1558,11 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
logger.Debug("使用备选响应,没有找到更好的结果", "domain", domain)
// 记录解析域名统计
s.updateResolvedDomainStats(domain)
// 更新统计信息
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
return backupResponse, backupRtt
return backupResponse, backupRtt, "", ""
}
// 所有上游服务器都失败,返回服务器失败错误
@@ -1407,12 +1575,12 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
s.updateStats(func(stats *Stats) {
stats.Errors++
})
return response, 0
return response, 0, "", ""
}
// forwardDNSRequest 转发DNS请求到上游服务器
func (s *Server) forwardDNSRequest(w dns.ResponseWriter, r *dns.Msg, domain string) {
response, _ := s.forwardDNSRequestWithCache(r, domain)
response, _, _, _ := s.forwardDNSRequestWithCache(r, domain)
w.WriteMsg(response)
}
@@ -1844,7 +2012,7 @@ func (s *Server) updateStats(update func(*Stats)) {
}
// addQueryLog 添加查询日志
func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool) {
func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool, dnsServer, dnssecServer string) {
// 获取IP地理位置
location := s.getIpGeolocation(clientIP)
@@ -1862,6 +2030,8 @@ func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime in
FromCache: fromCache,
DNSSEC: dnssec,
EDNS: edns,
DNSServer: dnsServer,
DNSSECServer: dnssecServer,
}
// 添加到日志列表