日志详情界面更新,增加日志详情API。

This commit is contained in:
Alex Yang
2025-12-26 00:53:18 +08:00
parent 01f2152e46
commit 42c4d6cfeb
9 changed files with 2449 additions and 787 deletions

View File

@@ -22,6 +22,17 @@ import (
"github.com/miekg/dns"
)
// 确保DNS服务器地址包含端口号默认添加53端口
func normalizeDNSServerAddress(address string) string {
// 检查地址是否已经包含端口号
if _, _, err := net.SplitHostPort(address); err != nil {
// 如果没有端口号添加默认的53端口
return net.JoinHostPort(address, "53")
}
// 已经有端口号,直接返回
return address
}
// BlockedDomain 屏蔽域名统计
type BlockedDomain struct {
Domain string
@@ -54,21 +65,22 @@ type DNSAnswer struct {
// QueryLog 查询日志记录
type QueryLog struct {
Timestamp time.Time // 查询时间
ClientIP string // 客户端IP
Location string // IP地理位置国家 城市)
Domain string // 查询域名
QueryType string // 查询类型
ResponseTime int64 // 响应时间(ms)
Result string // 查询结果allowed, blocked, error
BlockRule string // 屏蔽规则(如果被屏蔽)
BlockType string // 屏蔽类型(如果被屏蔽)
FromCache bool // 是否来自缓存
DNSSEC bool // 是否使用了DNSSEC
EDNS bool // 是否使用了EDNS
DNSServer string // 使用的DNS服务器
DNSSECServer string // 使用的DNSSEC专用服务器
Answers []DNSAnswer `json:"answers"` // 解析记录
Timestamp time.Time `json:"timestamp"` // 查询时间
ClientIP string `json:"clientIP"` // 客户端IP
Location string `json:"location"` // IP地理位置国家 城市)
Domain string `json:"domain"` // 查询域名
QueryType string `json:"queryType"` // 查询类型
ResponseTime int64 `json:"responseTime"` // 响应时间(ms)
Result string `json:"result"` // 查询结果allowed, blocked, error
BlockRule string `json:"blockRule"` // 屏蔽规则(如果被屏蔽)
BlockType string `json:"blockType"` // 屏蔽类型(如果被屏蔽)
FromCache bool `json:"fromCache"` // 是否来自缓存
DNSSEC bool `json:"dnssec"` // 是否使用了DNSSEC
EDNS bool `json:"edns"` // 是否使用了EDNS
DNSServer string `json:"dnsServer"` // 使用的DNS服务器
DNSSECServer string `json:"dnssecServer"` // 使用的DNSSEC专用服务器
Answers []DNSAnswer `json:"answers"` // 解析记录
ResponseCode int `json:"responseCode"` // DNS响应代码
}
// StatsData 用于持久化的统计数据结构
@@ -375,7 +387,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "", nil)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "", nil, dns.RcodeNameError)
logger.Debug("IPv6解析已禁用拒绝AAAA记录查询", "domain", domain)
return
}
@@ -401,7 +413,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
})
// 添加查询日志
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "", nil)
s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "", nil, dns.RcodeRefused)
return
}
@@ -438,9 +450,16 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
})
// 添加查询日志
// 添加查询日志 - 被屏蔽域名
blockedAnswers := []DNSAnswer{}
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true, "无", "无", blockedAnswers)
// 根据屏蔽方法确定响应代码
blockedRcode := dns.RcodeNameError // 默认NXDOMAIN
if blockMethod := s.shieldConfig.BlockMethod; blockMethod == "refused" {
blockedRcode = dns.RcodeRefused
} else if blockMethod == "emptyIP" || blockMethod == "customIP" {
blockedRcode = dns.RcodeSuccess
}
s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true, "无", "无", blockedAnswers, blockedRcode)
return
}
@@ -525,7 +544,12 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
// 添加查询日志 - 标记为缓存
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true, "缓存", "无", cachedAnswers)
// 从缓存响应中获取响应代码
cacheRcode := dns.RcodeSuccess // 默认成功
if cachedResponse != nil {
cacheRcode = cachedResponse.Rcode
}
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true, "缓存", "无", cachedAnswers, cacheRcode)
logger.Debug("从缓存返回DNS响应", "domain", domain, "type", queryType, "dnssec", cachedDNSSEC)
return
}
@@ -622,7 +646,12 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
}
// 添加查询日志 - 标记为实时
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true, dnsServer, dnssecServer, responseAnswers)
// 从响应中获取响应代码
realRcode := dns.RcodeSuccess // 默认成功
if response != nil {
realRcode = response.Rcode
}
s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true, dnsServer, dnssecServer, responseAnswers, realRcode)
}
// handleHostsResponse 处理hosts文件匹配的响应
@@ -795,17 +824,19 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
// 如果启用了DNSSEC且有配置DNSSEC专用服务器并且域名不匹配NoDNSSECDomains则将DNSSEC专用服务器添加到列表中
if s.config.EnableDNSSEC && len(s.config.DNSSECUpstreamDNS) > 0 && !noDNSSEC {
// 合并DNSSEC专用服务器到上游服务器列表避免重复
// 合并DNSSEC专用服务器到上游服务器列表避免重复,并确保包含端口号
for _, dnssecServer := range s.config.DNSSECUpstreamDNS {
hasDuplicate := false
// 确保DNSSEC服务器地址包含端口号
normalizedDnssecServer := normalizeDNSServerAddress(dnssecServer)
for _, upstream := range finalUpstreamDNS {
if upstream == dnssecServer {
if upstream == normalizedDnssecServer {
hasDuplicate = true
break
}
}
if !hasDuplicate {
finalUpstreamDNS = append(finalUpstreamDNS, dnssecServer)
finalUpstreamDNS = append(finalUpstreamDNS, normalizedDnssecServer)
}
}
logger.Debug("合并DNSSEC专用服务器到上游服务器列表", "servers", finalUpstreamDNS)
@@ -843,8 +874,8 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
go func(server string) {
defer wg.Done()
// 发送请求并获取响应
response, rtt, err := s.resolver.Exchange(r, server)
// 发送请求并获取响应,确保服务器地址包含端口号
response, rtt, err := s.resolver.Exchange(r, normalizeDNSServerAddress(server))
select {
case responses <- serverResponse{response, rtt, server, err}:
@@ -899,72 +930,103 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
resp.response.AuthenticatedData = false
}
// 如果响应成功或为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
}
// 检查当前服务器是否是DNSSEC专用服务器
for _, dnssecServer := range dnssecServers {
if dnssecServer == resp.server {
usedDNSSECServer = resp.server
break
}
}
if resp.response.Rcode == dns.RcodeSuccess {
// 处理成功响应
// 检查当前服务器是否是用户配置的上游DNS服务器优先使用用户配置的服务器
isUserUpstream := false
for _, userServer := range s.config.UpstreamDNS {
if userServer == resp.server {
isUserUpstream = true
break
}
}
// 检查当前服务器是否是用户配置的上游DNS服务器
isUserUpstream := false
for _, userServer := range s.config.UpstreamDNS {
if userServer == resp.server {
isUserUpstream = true
break
}
}
// 优先选择用户配置的上游DNS服务器的响应
if isUserUpstream {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = containsDNSSEC
usedDNSServer = resp.server
logger.Debug("使用用户配置的上游服务器响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if containsDNSSEC {
// 如果不是用户配置的服务器优先选择带有DNSSEC记录的响应
// 处理响应,优先选择用户配置的DNS服务器
if resp.response.Rcode == dns.RcodeSuccess {
// 成功响应,优先使用
if isUserUpstream {
// 用户配置的主DNS服务器响应直接设置为最佳响应
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
hasDNSSECResponse = containsDNSSEC
usedDNSServer = resp.server
logger.Debug("使用用户配置的上游服务器响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else if containsDNSSEC {
// 非用户配置服务器但有DNSSEC记录
if !hasBestResponse || !isUserUpstream {
// 如果还没有最佳响应,或者当前最佳响应不是用户配置的服务器,则更新
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的响应时保存第一个成功响应
}
} else {
// 非用户配置服务器没有DNSSEC记录
if !hasBestResponse {
// 如果还没有最佳响应,设置为最佳响应
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)
}
}
} else if resp.response.Rcode == dns.RcodeNameError {
// NXDOMAIN响应
if !hasBestResponse || bestResponse.Rcode == dns.RcodeNameError {
// 如果还没有最佳响应,或者最佳响应也是NXDOMAIN
if isUserUpstream {
// 用户配置的服务器,直接使用
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
logger.Debug("使用用户配置的上游服务器NXDOMAIN响应", "domain", domain, "server", resp.server, "rtt", resp.rtt)
} else 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 resp.response != nil {
if !hasBackup {
// 第一次保存备选响应
backupResponse = resp.response
backupRtt = resp.rtt
hasBackup = true
} else {
// 后续响应,优先保存用户配置的服务器响应作为备选
if isUserUpstream {
backupResponse = resp.response
backupRtt = resp.rtt
}
}
}
// 即使响应不是成功或NXDOMAIN也保存为最佳响应如果还没有的话
// 确保总有一个响应返回给客户端
if !hasBestResponse {
bestResponse = resp.response
bestRtt = resp.rtt
hasBestResponse = true
usedDNSServer = resp.server
logger.Debug("使用非成功响应作为最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt, "rcode", resp.response.Rcode)
}
} else {
// 更新服务器统计信息(失败)
s.updateServerStats(resp.server, false, 0)
@@ -1011,7 +1073,7 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}, 1)
go func() {
response, rtt, err := s.resolver.Exchange(r, selectedServer)
response, rtt, err := s.resolver.Exchange(r, normalizeDNSServerAddress(selectedServer))
resultChan <- struct {
response *dns.Msg
rtt time.Duration
@@ -1137,7 +1199,7 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}, 1)
go func() {
resp, r, e := s.resolver.Exchange(r, fastestServer)
resp, r, e := s.resolver.Exchange(r, normalizeDNSServerAddress(fastestServer))
resultChan <- struct {
response *dns.Msg
rtt time.Duration
@@ -1259,7 +1321,7 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
defer wg.Done()
// 发送请求并获取响应
response, rtt, err := s.resolver.Exchange(r, server)
response, rtt, err := s.resolver.Exchange(r, normalizeDNSServerAddress(server))
select {
case responses <- serverResponse{response, rtt, server, err}:
@@ -1400,7 +1462,7 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}, 1)
go func() {
response, rtt, err := s.resolver.Exchange(r, selectedDnssecServer)
response, rtt, err := s.resolver.Exchange(r, normalizeDNSServerAddress(selectedDnssecServer))
resultChan <- struct {
response *dns.Msg
rtt time.Duration
@@ -1498,7 +1560,7 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg
}, 1)
go func() {
resp, r, e := s.resolver.Exchange(r, localServer)
resp, r, e := s.resolver.Exchange(r, normalizeDNSServerAddress(localServer))
resultChan <- struct {
response *dns.Msg
rtt time.Duration
@@ -2083,7 +2145,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, dnsServer, dnssecServer string, answers []DNSAnswer) {
func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool, dnsServer, dnssecServer string, answers []DNSAnswer, responseCode int) {
// 获取IP地理位置
location := s.getIpGeolocation(clientIP)
@@ -2104,6 +2166,7 @@ func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime in
DNSServer: dnsServer,
DNSSECServer: dnssecServer,
Answers: answers,
ResponseCode: responseCode,
}
// 添加到日志列表