日志详情界面更新,增加日志详情API。
This commit is contained in:
213
dns/server.go
213
dns/server.go
@@ -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,
|
||||
}
|
||||
|
||||
// 添加到日志列表
|
||||
|
||||
Reference in New Issue
Block a user