更新Swaggers API

This commit is contained in:
Alex Yang
2025-12-16 20:38:29 +08:00
parent c16e147931
commit b67a0fad8e
15 changed files with 10138 additions and 247 deletions

View File

@@ -23,11 +23,11 @@ import (
)
// BlockedDomain 屏蔽域名统计
type BlockedDomain struct {
Domain string
Count int64
LastSeen time.Time
DNSSEC bool // 是否使用了DNSSEC
}
// ClientStats 客户端统计
@@ -57,6 +57,8 @@ type QueryLog struct {
BlockRule string // 屏蔽规则(如果被屏蔽)
BlockType string // 屏蔽类型(如果被屏蔽)
FromCache bool // 是否来自缓存
DNSSEC bool // 是否使用了DNSSEC
EDNS bool // 是否使用了EDNS
}
// StatsData 用于持久化的统计数据结构
@@ -111,6 +113,9 @@ type Server struct {
// DNS查询缓存
dnsCache *DNSCache // DNS响应缓存
// 域名DNSSEC状态映射表
domainDNSSECStatus map[string]bool // 域名到DNSSEC状态的映射
}
// Stats DNS服务器统计信息
@@ -125,6 +130,10 @@ type Stats struct {
QueryTypes map[string]int64 // 查询类型统计
SourceIPs map[string]bool // 活跃来源IP
CpuUsage float64 // CPU使用率(%)
DNSSECQueries int64 // DNSSEC查询总数
DNSSECSuccess int64 // DNSSEC验证成功数
DNSSECFailed int64 // DNSSEC验证失败数
DNSSECEnabled bool // 是否启用了DNSSEC
}
// NewServer 创建DNS服务器实例
@@ -141,6 +150,7 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
resolver: &dns.Client{
Net: "udp",
Timeout: time.Duration(config.Timeout) * time.Millisecond,
UDPSize: 4096, // 增加UDP缓冲区大小支持更大的DNSSEC响应
},
ctx: ctx,
cancel: cancel,
@@ -155,6 +165,10 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
QueryTypes: make(map[string]int64),
SourceIPs: make(map[string]bool),
CpuUsage: 0,
DNSSECQueries: 0,
DNSSECSuccess: 0,
DNSSECFailed: 0,
DNSSECEnabled: config.EnableDNSSEC,
},
blockedDomains: make(map[string]*BlockedDomain),
resolvedDomains: make(map[string]*BlockedDomain),
@@ -171,6 +185,8 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
ipGeolocationCacheTTL: 24 * time.Hour, // 缓存有效期24小时
// DNS查询缓存初始化
dnsCache: NewDNSCache(cacheTTL),
// 初始化域名DNSSEC状态映射表
domainDNSSECStatus: make(map[string]bool),
}
// 加载已保存的统计数据
@@ -327,8 +343,8 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
response.SetRcode(r, dns.RcodeRefused)
w.WriteMsg(response)
// 计算响应时间
responseTime := time.Since(startTime).Milliseconds()
// 缓存命中响应时间设为0ms
responseTime := int64(0)
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
@@ -337,15 +353,15 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true)
return
}
// 检查hosts文件是否有匹配
if ip, exists := s.shieldManager.GetHostsIP(domain); exists {
s.handleHostsResponse(w, r, ip)
// 计算响应时间
responseTime := time.Since(startTime).Milliseconds()
// 缓存命中响应时间设为0ms
responseTime := int64(0)
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
@@ -354,7 +370,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, false, true)
return
}
@@ -376,16 +392,58 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true)
return
}
// 检查缓存中是否有响应(增强版缓存查询
if cachedResponse, found := s.dnsCache.Get(r.Question[0].Name, qType); found {
// 检查缓存中是否有响应(优先查找带DNSSEC的缓存项
var cachedResponse *dns.Msg
var found bool
var cachedDNSSEC bool
// 1. 首先检查是否有普通缓存项
if tempResponse, tempFound := s.dnsCache.Get(r.Question[0].Name, qType); tempFound {
cachedResponse = tempResponse
found = tempFound
cachedDNSSEC = s.hasDNSSECRecords(tempResponse)
}
// 2. 如果启用了DNSSEC且没有找到带DNSSEC的缓存项
// 尝试从所有缓存中查找是否有其他响应包含DNSSEC记录
// 这里可以进一步优化比如在缓存中标记DNSSEC状态快速查找
if s.config.EnableDNSSEC && !cachedDNSSEC {
// 目前的缓存实现不支持按DNSSEC状态查找所以这里暂时跳过
// 后续可以考虑改进缓存实现添加DNSSEC状态标记
}
if found {
// 缓存命中,直接返回缓存的响应
cachedResponseCopy := cachedResponse.Copy() // 创建响应副本避免并发修改问题
cachedResponseCopy.Id = r.Id // 更新ID以匹配请求
cachedResponseCopy.Compress = true
// 如果客户端请求包含EDNS记录确保响应也包含EDNS
if opt := r.IsEdns0(); opt != nil {
// 检查响应是否已经包含EDNS记录
if respOpt := cachedResponseCopy.IsEdns0(); respOpt == nil {
// 添加EDNS记录使用客户端的UDP缓冲区大小
cachedResponseCopy.SetEdns0(opt.UDPSize(), s.config.EnableDNSSEC)
} else {
// 确保响应的UDP缓冲区大小不超过客户端请求的大小
if respOpt.UDPSize() > opt.UDPSize() {
// 移除现有的EDNS记录
for i := range cachedResponseCopy.Extra {
if cachedResponseCopy.Extra[i] == respOpt {
cachedResponseCopy.Extra = append(cachedResponseCopy.Extra[:i], cachedResponseCopy.Extra[i+1:]...)
break
}
}
// 添加新的EDNS记录使用客户端的UDP缓冲区大小
cachedResponseCopy.SetEdns0(opt.UDPSize(), s.config.EnableDNSSEC)
}
}
}
w.WriteMsg(cachedResponseCopy)
// 计算响应时间
@@ -397,21 +455,57 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
})
// 如果缓存响应包含DNSSEC记录更新DNSSEC查询计数
if cachedDNSSEC {
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
// 缓存响应视为DNSSEC成功
stats.DNSSECSuccess++
})
}
// 添加查询日志 - 标记为缓存
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true)
logger.Debug("从缓存返回DNS响应", "domain", domain, "type", queryType)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true)
logger.Debug("从缓存返回DNS响应", "domain", domain, "type", queryType, "dnssec", cachedDNSSEC)
return
}
// 缓存未命中转发到上游DNS服务器
response, _ := s.forwardDNSRequestWithCache(r, domain)
response, rtt := s.forwardDNSRequestWithCache(r, domain)
if response != nil {
// 如果客户端请求包含EDNS记录确保响应也包含EDNS
if opt := r.IsEdns0(); opt != nil {
// 检查响应是否已经包含EDNS记录
if respOpt := response.IsEdns0(); respOpt == nil {
// 添加EDNS记录使用客户端的UDP缓冲区大小
response.SetEdns0(opt.UDPSize(), s.config.EnableDNSSEC)
} else {
// 确保响应的UDP缓冲区大小不超过客户端请求的大小
if respOpt.UDPSize() > opt.UDPSize() {
// 移除现有的EDNS记录
for i := range response.Extra {
if response.Extra[i] == respOpt {
response.Extra = append(response.Extra[:i], response.Extra[i+1:]...)
break
}
}
// 添加新的EDNS记录使用客户端的UDP缓冲区大小
response.SetEdns0(opt.UDPSize(), s.config.EnableDNSSEC)
}
}
}
// 写入响应给客户端
w.WriteMsg(response)
}
// 计算响应时间
responseTime := time.Since(startTime).Milliseconds()
// 使用上游服务器的实际响应时间(转换为毫秒)
responseTime := int64(rtt.Milliseconds())
// 如果rtt为0查询失败则使用本地计算的时间
if responseTime == 0 {
responseTime = time.Since(startTime).Milliseconds()
}
s.updateStats(func(stats *Stats) {
stats.TotalResponseTime += responseTime
if stats.Queries > 0 {
@@ -419,6 +513,23 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
})
// 检查响应是否包含DNSSEC记录并验证结果
responseDNSSEC := false
if response != nil {
// 使用hasDNSSECRecords函数检查是否包含DNSSEC记录
responseDNSSEC = s.hasDNSSECRecords(response)
// 检查AD标志确认DNSSEC验证是否成功
if response.AuthenticatedData {
responseDNSSEC = true
}
// 更新域名的DNSSEC状态
if responseDNSSEC {
s.updateDomainDNSSECStatus(domain, true)
}
}
// 如果响应成功,缓存结果(增强版缓存存储)
if response != nil && response.Rcode == dns.RcodeSuccess {
// 创建响应副本以避免后续修改影响缓存
@@ -426,11 +537,11 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
// 设置合理的TTL不超过默认的30分钟
defaultCacheTTL := 30 * time.Minute
s.dnsCache.Set(r.Question[0].Name, qType, responseCopy, defaultCacheTTL)
logger.Debug("DNS响应已缓存", "domain", domain, "type", queryType, "ttl", defaultCacheTTL)
logger.Debug("DNS响应已缓存", "domain", domain, "type", queryType, "ttl", defaultCacheTTL, "dnssec", responseDNSSEC)
}
// 添加查询日志 - 标记为实时
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true)
}
// handleHostsResponse 处理hosts文件匹配的响应
@@ -537,24 +648,193 @@ func (s *Server) handleBlockedResponse(w dns.ResponseWriter, r *dns.Msg, domain
// 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
// 检查客户端请求是否包含EDNS记录
if opt := r.IsEdns0(); opt != nil {
// 保留客户端的UDP缓冲区大小
udpSize = opt.UDPSize()
// 移除现有的EDNS记录以便重新添加
for i := range r.Extra {
if r.Extra[i] == opt {
r.Extra = append(r.Extra[:i], r.Extra[i+1:]...)
break
}
}
}
// 添加EDNS记录设置适当的UDPSize和DO标志
r.SetEdns0(udpSize, doFlag)
// DNSSEC专用服务器列表从配置中获取
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.Rcode == dns.RcodeSuccess {
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = true
logger.Debug("DNS查询成功", "domain", domain, "rtt", rtt, "server", upstream)
// 检查是否包含DNSSEC记录
containsDNSSEC := s.hasDNSSECRecords(response)
// 记录解析域名统计
s.updateResolvedDomainStats(domain)
// 如果启用了DNSSEC且响应包含DNSSEC记录验证DNSSEC签名
if s.config.EnableDNSSEC && containsDNSSEC {
// 验证DNSSEC记录
signatureValid := s.verifyDNSSEC(response)
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
return response, rtt
// 设置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)
} 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
}
}
}
}
// 2. 当启用DNSSEC且没有找到带DNSSEC的响应时向DNSSEC专用服务器发送请求
if s.config.EnableDNSSEC && !hasDNSSECResponse {
logger.Debug("向DNSSEC专用服务器发送请求", "domain", domain)
// 增加DNSSEC查询计数
s.updateStats(func(stats *Stats) {
stats.DNSSECQueries++
})
for _, dnssecServer := range dnssecServers {
response, rtt, err := s.resolver.Exchange(r, dnssecServer)
if err == nil && response != nil {
// 设置递归可用标志
response.RecursionAvailable = 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)
}
// 注意如果DNSSEC专用服务器返回的响应不包含DNSSEC记录
// 我们不会覆盖之前从upstreamDNS获取的响应
// 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求
// 更新备选响应
if !hasBackup {
backupResponse = response
backupRtt = rtt
hasBackup = true
}
}
}
}
}
// 3. 返回最佳响应
if hasBestResponse {
// 检查最佳响应是否包含DNSSEC记录
bestHasDNSSEC := s.hasDNSSECRecords(bestResponse)
// 如果启用了DNSSEC且最佳响应不包含DNSSEC记录使用upstreamDNS的解析结果
if s.config.EnableDNSSEC && !bestHasDNSSEC {
logger.Debug("最佳响应不包含DNSSEC记录使用upstreamDNS的解析结果", "domain", domain)
}
// 记录解析域名统计
s.updateResolvedDomainStats(domain)
// 更新域名的DNSSEC状态
if bestHasDNSSEC {
s.updateDomainDNSSECStatus(domain, true)
}
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
return bestResponse, bestRtt
}
// 如果有备选响应,返回该响应
if hasBackup {
logger.Debug("使用备选响应,没有找到更好的结果", "domain", domain)
// 记录解析域名统计
s.updateResolvedDomainStats(domain)
s.updateStats(func(stats *Stats) {
stats.Allowed++
})
return backupResponse, backupRtt
}
// 所有上游服务器都失败,返回服务器失败错误
response := new(dns.Msg)
response.SetReply(r)
@@ -630,6 +910,164 @@ func (s *Server) updateClientStats(ip string) {
}
}
// hasDNSSECRecords 检查响应是否包含DNSSEC记录
func (s *Server) hasDNSSECRecords(response *dns.Msg) bool {
// 检查响应中是否包含DNSSEC相关记录DNSKEY、RRSIG、DS、NSEC、NSEC3等
for _, rr := range response.Answer {
if _, ok := rr.(*dns.DNSKEY); ok {
return true
}
if _, ok := rr.(*dns.RRSIG); ok {
return true
}
if _, ok := rr.(*dns.DS); ok {
return true
}
if _, ok := rr.(*dns.NSEC); ok {
return true
}
if _, ok := rr.(*dns.NSEC3); ok {
return true
}
}
for _, rr := range response.Ns {
if _, ok := rr.(*dns.DNSKEY); ok {
return true
}
if _, ok := rr.(*dns.RRSIG); ok {
return true
}
if _, ok := rr.(*dns.DS); ok {
return true
}
if _, ok := rr.(*dns.NSEC); ok {
return true
}
if _, ok := rr.(*dns.NSEC3); ok {
return true
}
}
for _, rr := range response.Extra {
if _, ok := rr.(*dns.DNSKEY); ok {
return true
}
if _, ok := rr.(*dns.RRSIG); ok {
return true
}
if _, ok := rr.(*dns.DS); ok {
return true
}
if _, ok := rr.(*dns.NSEC); ok {
return true
}
if _, ok := rr.(*dns.NSEC3); ok {
return true
}
}
return false
}
// verifyDNSSEC 验证DNSSEC签名
func (s *Server) verifyDNSSEC(response *dns.Msg) bool {
// 提取DNSKEY和RRSIG记录
dnskeys := make(map[uint16]*dns.DNSKEY) // KeyTag -> DNSKEY
rrsigs := make([]*dns.RRSIG, 0)
// 从响应中提取所有DNSKEY和RRSIG记录
for _, rr := range response.Answer {
if dnskey, ok := rr.(*dns.DNSKEY); ok {
tag := dnskey.KeyTag()
dnskeys[tag] = dnskey
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
rrsigs = append(rrsigs, rrsig)
}
}
for _, rr := range response.Ns {
if dnskey, ok := rr.(*dns.DNSKEY); ok {
tag := dnskey.KeyTag()
dnskeys[tag] = dnskey
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
rrsigs = append(rrsigs, rrsig)
}
}
for _, rr := range response.Extra {
if dnskey, ok := rr.(*dns.DNSKEY); ok {
tag := dnskey.KeyTag()
dnskeys[tag] = dnskey
} else if rrsig, ok := rr.(*dns.RRSIG); ok {
rrsigs = append(rrsigs, rrsig)
}
}
// 如果没有RRSIG记录验证失败
if len(rrsigs) == 0 {
return false
}
// 验证所有RRSIG记录
signatureValid := true
for _, rrsig := range rrsigs {
// 查找对应的DNSKEY
dnskey, exists := dnskeys[rrsig.KeyTag]
if !exists {
logger.Warn("DNSSEC验证失败找不到对应的DNSKEY", "key_tag", rrsig.KeyTag)
signatureValid = false
continue
}
// 收集需要验证的记录集
rrset := make([]dns.RR, 0)
for _, rr := range response.Answer {
if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
rrset = append(rrset, rr)
}
}
for _, rr := range response.Ns {
if rr.Header().Name == rrsig.Header().Name && rr.Header().Rrtype == rrsig.TypeCovered {
rrset = append(rrset, rr)
}
}
// 验证签名
if len(rrset) > 0 {
err := rrsig.Verify(dnskey, rrset)
if err != nil {
logger.Warn("DNSSEC签名验证失败", "error", err, "key_tag", rrsig.KeyTag)
signatureValid = false
} else {
logger.Debug("DNSSEC签名验证成功", "key_tag", rrsig.KeyTag)
}
}
}
return signatureValid
}
// updateDomainDNSSECStatus 更新域名的DNSSEC状态
func (s *Server) updateDomainDNSSECStatus(domain string, dnssec bool) {
// 确保域名是小写
domain = strings.ToLower(domain)
// 更新域名的DNSSEC状态
s.resolvedDomainsMutex.Lock()
defer s.resolvedDomainsMutex.Unlock()
// 更新resolvedDomains中的DNSSEC状态
if entry, exists := s.resolvedDomains[domain]; exists {
entry.DNSSEC = dnssec
} else {
s.resolvedDomains[domain] = &BlockedDomain{
Domain: domain,
Count: 1,
LastSeen: time.Now(),
DNSSEC: dnssec,
}
}
// 更新domainDNSSECStatus映射
s.domainDNSSECStatus[domain] = dnssec
}
// updateResolvedDomainStats 更新解析域名统计
func (s *Server) updateResolvedDomainStats(domain string) {
s.resolvedDomainsMutex.Lock()
@@ -643,6 +1081,7 @@ func (s *Server) updateResolvedDomainStats(domain string) {
Domain: domain,
Count: 1,
LastSeen: time.Now(),
DNSSEC: false,
}
}
}
@@ -655,7 +1094,7 @@ func (s *Server) updateStats(update func(*Stats)) {
}
// addQueryLog 添加查询日志
func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache bool) {
func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool) {
// 获取IP地理位置
location := s.getIpGeolocation(clientIP)
@@ -671,6 +1110,8 @@ func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime in
BlockRule: blockRule,
BlockType: blockType,
FromCache: fromCache,
DNSSEC: dnssec,
EDNS: edns,
}
// 添加到日志列表
@@ -720,6 +1161,10 @@ func (s *Server) GetStats() *Stats {
QueryTypes: queryTypesCopy,
SourceIPs: sourceIPsCopy,
CpuUsage: s.stats.CpuUsage,
DNSSECQueries: s.stats.DNSSECQueries,
DNSSECSuccess: s.stats.DNSSECSuccess,
DNSSECFailed: s.stats.DNSSECFailed,
DNSSECEnabled: s.stats.DNSSECEnabled,
}
}
@@ -1145,6 +1590,8 @@ func (s *Server) loadStatsData() {
s.statsMutex.Lock()
if statsData.Stats != nil {
s.stats = statsData.Stats
// 确保使用当前配置中的EnableDNSSEC值覆盖从文件加载的值
s.stats.DNSSECEnabled = s.config.EnableDNSSEC
}
s.statsMutex.Unlock()