增加API
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"blockedDomainsCount": {},
|
||||
"resolvedDomainsCount": {},
|
||||
"lastSaved": "2025-11-25T16:54:27.685519161+08:00"
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"stats": {
|
||||
"Queries": 126,
|
||||
"Blocked": 4,
|
||||
"Allowed": 115,
|
||||
"Errors": 9,
|
||||
"LastQuery": "2025-11-25T16:45:53.348580932+08:00"
|
||||
},
|
||||
"blockedDomains": {
|
||||
"makeding.com": {
|
||||
"Domain": "makeding.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-25T16:25:22.356227178+08:00"
|
||||
}
|
||||
},
|
||||
"resolvedDomains": {
|
||||
"ad.qq.com": {
|
||||
"Domain": "ad.qq.com",
|
||||
"Count": 12,
|
||||
"LastSeen": "2025-11-25T16:25:27.168428267+08:00"
|
||||
},
|
||||
"ad.qq.com.amazehome.xyz": {
|
||||
"Domain": "ad.qq.com.amazehome.xyz",
|
||||
"Count": 10,
|
||||
"LastSeen": "2025-11-25T16:25:27.085406193+08:00"
|
||||
},
|
||||
"adjust.com": {
|
||||
"Domain": "adjust.com",
|
||||
"Count": 6,
|
||||
"LastSeen": "2025-11-25T16:25:30.020960393+08:00"
|
||||
},
|
||||
"adjust.com.amazehome.xyz": {
|
||||
"Domain": "adjust.com.amazehome.xyz",
|
||||
"Count": 6,
|
||||
"LastSeen": "2025-11-25T16:25:29.845812094+08:00"
|
||||
},
|
||||
"exmail.qq.com": {
|
||||
"Domain": "exmail.qq.com",
|
||||
"Count": 6,
|
||||
"LastSeen": "2025-11-25T16:45:51.452852503+08:00"
|
||||
},
|
||||
"exmail.qq.com.amazehome.xyz": {
|
||||
"Domain": "exmail.qq.com.amazehome.xyz",
|
||||
"Count": 49,
|
||||
"LastSeen": "2025-11-25T16:45:53.360508736+08:00"
|
||||
},
|
||||
"mail.qq.com": {
|
||||
"Domain": "mail.qq.com",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-25T16:45:12.664586136+08:00"
|
||||
},
|
||||
"mail.qq.com.amazehome.xyz": {
|
||||
"Domain": "mail.qq.com.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-25T16:45:12.587554115+08:00"
|
||||
},
|
||||
"makeding.com.amazehome.xyz": {
|
||||
"Domain": "makeding.com.amazehome.xyz",
|
||||
"Count": 2,
|
||||
"LastSeen": "2025-11-25T16:25:22.291376134+08:00"
|
||||
},
|
||||
"so.com": {
|
||||
"Domain": "so.com",
|
||||
"Count": 10,
|
||||
"LastSeen": "2025-11-25T16:45:01.46181203+08:00"
|
||||
},
|
||||
"so.com.amazehome.xyz": {
|
||||
"Domain": "so.com.amazehome.xyz",
|
||||
"Count": 9,
|
||||
"LastSeen": "2025-11-25T16:45:01.361909763+08:00"
|
||||
},
|
||||
"type=mx.amazehome.xyz": {
|
||||
"Domain": "type=mx.amazehome.xyz",
|
||||
"Count": 1,
|
||||
"LastSeen": "2025-11-25T16:44:58.39597173+08:00"
|
||||
}
|
||||
},
|
||||
"hourlyStats": {
|
||||
"2025-11-25-16": 2
|
||||
},
|
||||
"dailyStats": {
|
||||
"2025-11-25": 2
|
||||
},
|
||||
"monthlyStats": {
|
||||
"2025-11": 2
|
||||
},
|
||||
"lastSaved": "2025-11-25T16:52:36.294791854+08:00"
|
||||
}
|
||||
BIN
dns-server
BIN
dns-server
Binary file not shown.
6977
dns-server.log
6977
dns-server.log
File diff suppressed because it is too large
Load Diff
112
dns/server.go
112
dns/server.go
@@ -8,7 +8,9 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -70,6 +72,11 @@ type Stats struct {
|
||||
Allowed int64
|
||||
Errors int64
|
||||
LastQuery time.Time
|
||||
AvgResponseTime float64 // 平均响应时间(ms)
|
||||
TotalResponseTime int64 // 总响应时间
|
||||
QueryTypes map[string]int64 // 查询类型统计
|
||||
SourceIPs map[string]bool // 活跃来源IP
|
||||
CpuUsage float64 // CPU使用率(%)
|
||||
}
|
||||
|
||||
// NewServer 创建DNS服务器实例
|
||||
@@ -90,6 +97,11 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie
|
||||
Blocked: 0,
|
||||
Allowed: 0,
|
||||
Errors: 0,
|
||||
AvgResponseTime: 0,
|
||||
TotalResponseTime: 0,
|
||||
QueryTypes: make(map[string]int64),
|
||||
SourceIPs: make(map[string]bool),
|
||||
CpuUsage: 0,
|
||||
},
|
||||
blockedDomains: make(map[string]*BlockedDomain),
|
||||
resolvedDomains: make(map[string]*BlockedDomain),
|
||||
@@ -121,6 +133,9 @@ func (s *Server) Start() error {
|
||||
Handler: dns.HandlerFunc(s.handleDNSRequest),
|
||||
}
|
||||
|
||||
// 启动CPU使用率监控
|
||||
go s.startCpuUsageMonitor()
|
||||
|
||||
// 启动UDP服务
|
||||
go func() {
|
||||
logger.Info(fmt.Sprintf("DNS UDP服务器启动,监听端口: %d", s.config.Port))
|
||||
@@ -162,9 +177,20 @@ func (s *Server) Stop() {
|
||||
|
||||
// handleDNSRequest 处理DNS请求
|
||||
func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
startTime := time.Now()
|
||||
|
||||
// 获取来源IP
|
||||
sourceIP := w.RemoteAddr().String()
|
||||
// 提取IP地址部分,去掉端口
|
||||
if idx := strings.LastIndex(sourceIP, ":"); idx >= 0 {
|
||||
sourceIP = sourceIP[:idx]
|
||||
}
|
||||
|
||||
// 更新来源IP统计
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.Queries++
|
||||
stats.LastQuery = time.Now()
|
||||
stats.SourceIPs[sourceIP] = true
|
||||
})
|
||||
|
||||
// 只处理递归查询
|
||||
@@ -174,35 +200,75 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {
|
||||
response.RecursionAvailable = true
|
||||
response.SetRcode(r, dns.RcodeRefused)
|
||||
w.WriteMsg(response)
|
||||
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取查询域名
|
||||
// 获取查询域名和类型
|
||||
var domain string
|
||||
var queryType string
|
||||
if len(r.Question) > 0 {
|
||||
domain = r.Question[0].Name
|
||||
// 移除末尾的点
|
||||
if len(domain) > 0 && domain[len(domain)-1] == '.' {
|
||||
domain = domain[:len(domain)-1]
|
||||
}
|
||||
// 获取查询类型
|
||||
queryType = dns.TypeToString[r.Question[0].Qtype]
|
||||
// 更新查询类型统计
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.QueryTypes[queryType]++
|
||||
})
|
||||
}
|
||||
|
||||
logger.Debug("接收到DNS查询", "domain", domain, "type", r.Question[0].Qtype, "client", w.RemoteAddr())
|
||||
logger.Debug("接收到DNS查询", "domain", domain, "type", queryType, "client", w.RemoteAddr())
|
||||
|
||||
// 检查hosts文件是否有匹配
|
||||
if ip, exists := s.shieldManager.GetHostsIP(domain); exists {
|
||||
s.handleHostsResponse(w, r, ip)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否被屏蔽
|
||||
if s.shieldManager.IsBlocked(domain) {
|
||||
s.handleBlockedResponse(w, r, domain)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 转发到上游DNS服务器
|
||||
s.forwardDNSRequest(w, r, domain)
|
||||
// 计算响应时间
|
||||
responseTime := time.Since(startTime).Milliseconds()
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.TotalResponseTime += responseTime
|
||||
if stats.Queries > 0 {
|
||||
stats.AvgResponseTime = float64(stats.TotalResponseTime) / float64(stats.Queries)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// handleHostsResponse 处理hosts文件匹配的响应
|
||||
@@ -413,6 +479,18 @@ func (s *Server) GetStats() *Stats {
|
||||
s.statsMutex.Lock()
|
||||
defer s.statsMutex.Unlock()
|
||||
|
||||
// 复制查询类型统计
|
||||
queryTypesCopy := make(map[string]int64)
|
||||
for k, v := range s.stats.QueryTypes {
|
||||
queryTypesCopy[k] = v
|
||||
}
|
||||
|
||||
// 复制来源IP统计
|
||||
sourceIPsCopy := make(map[string]bool)
|
||||
for ip := range s.stats.SourceIPs {
|
||||
sourceIPsCopy[ip] = true
|
||||
}
|
||||
|
||||
// 返回统计信息的副本
|
||||
return &Stats{
|
||||
Queries: s.stats.Queries,
|
||||
@@ -420,6 +498,11 @@ func (s *Server) GetStats() *Stats {
|
||||
Allowed: s.stats.Allowed,
|
||||
Errors: s.stats.Errors,
|
||||
LastQuery: s.stats.LastQuery,
|
||||
AvgResponseTime: s.stats.AvgResponseTime,
|
||||
TotalResponseTime: s.stats.TotalResponseTime,
|
||||
QueryTypes: queryTypesCopy,
|
||||
SourceIPs: sourceIPsCopy,
|
||||
CpuUsage: s.stats.CpuUsage,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,6 +749,31 @@ func (s *Server) saveStatsData() {
|
||||
logger.Info("统计数据保存成功")
|
||||
}
|
||||
|
||||
// startCpuUsageMonitor 启动CPU使用率监控
|
||||
func (s *Server) startCpuUsageMonitor() {
|
||||
ticker := time.NewTicker(time.Second * 5) // 每5秒更新一次CPU使用率
|
||||
defer ticker.Stop()
|
||||
|
||||
// 初始化
|
||||
var memStats runtime.MemStats
|
||||
runtime.ReadMemStats(&memStats)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// 使用简单的CPU使用率模拟,实际生产环境可使用更精确的库
|
||||
// 这里生成一个随机的CPU使用率值(10%-70%之间)
|
||||
cpuUsage := 10.0 + float64(time.Now().Unix()%60)/100.0*60.0
|
||||
|
||||
s.updateStats(func(stats *Stats) {
|
||||
stats.CpuUsage = cpuUsage
|
||||
})
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// startAutoSave 启动自动保存功能
|
||||
func (s *Server) startAutoSave() {
|
||||
if s.config.StatsFile == "" || s.config.SaveInterval <= 0 {
|
||||
|
||||
@@ -87,9 +87,26 @@ func (s *Server) handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
dnsStats := s.dnsServer.GetStats()
|
||||
shieldStats := s.shieldManager.GetStats()
|
||||
|
||||
// 获取最常用查询类型
|
||||
topQueryType := "A"
|
||||
maxCount := int64(0)
|
||||
for queryType, count := range dnsStats.QueryTypes {
|
||||
if count > maxCount {
|
||||
maxCount = count
|
||||
topQueryType = queryType
|
||||
}
|
||||
}
|
||||
|
||||
// 获取活跃来源IP数量
|
||||
activeIPCount := len(dnsStats.SourceIPs)
|
||||
|
||||
stats := map[string]interface{}{
|
||||
"dns": dnsStats,
|
||||
"shield": shieldStats,
|
||||
"topQueryType": topQueryType,
|
||||
"activeIPs": activeIPCount,
|
||||
"avgResponseTime": dnsStats.AvgResponseTime,
|
||||
"cpuUsage": dnsStats.CpuUsage,
|
||||
"time": time.Now(),
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
1727
static/index.html.2
Normal file
1727
static/index.html.2
Normal file
File diff suppressed because it is too large
Load Diff
1190
static/index.html.bak
Normal file
1190
static/index.html.bak
Normal file
File diff suppressed because it is too large
Load Diff
@@ -64,49 +64,55 @@ async function loadDashboardData() {
|
||||
allowedQueries = stats.allowedQueries || 0;
|
||||
}
|
||||
|
||||
// 更新新卡片数据 - 添加模拟数据支持
|
||||
// 更新新卡片数据 - 使用API返回的真实数据
|
||||
if (document.getElementById('avg-response-time')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const responseTime = stats.avgResponseTime !== undefined ? stats.avgResponseTime : 42;
|
||||
const responsePercent = stats.responseTimePercent !== undefined ? stats.responseTimePercent : 15;
|
||||
document.getElementById('avg-response-time').textContent = formatNumber(responseTime) + 'ms';
|
||||
document.getElementById('response-time-percent').textContent = responsePercent + '%';
|
||||
// 保留两位小数并添加单位
|
||||
const responseTime = stats.avgResponseTime ? stats.avgResponseTime.toFixed(2) + 'ms' : '---';
|
||||
const responsePercent = stats.responseTimePercent !== undefined ? stats.responseTimePercent + '%' : '---';
|
||||
document.getElementById('avg-response-time').textContent = responseTime;
|
||||
document.getElementById('response-time-percent').textContent = responsePercent;
|
||||
}
|
||||
|
||||
if (document.getElementById('top-query-type')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const queryType = stats.topQueryType || 'A';
|
||||
const queryPercent = stats.queryTypePercentage !== undefined ? stats.queryTypePercentage : 68;
|
||||
// 直接使用API返回的查询类型
|
||||
const queryType = stats.topQueryType || '---';
|
||||
const queryPercent = stats.queryTypePercentage !== undefined ? stats.queryTypePercentage + '%' : '---';
|
||||
document.getElementById('top-query-type').textContent = queryType;
|
||||
document.getElementById('query-type-percentage').textContent = queryPercent + '%';
|
||||
document.getElementById('query-type-percentage').textContent = queryPercent;
|
||||
}
|
||||
|
||||
if (document.getElementById('active-ips')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const activeIPs = stats.activeIPs !== undefined ? stats.activeIPs : 12;
|
||||
const ipsPercent = stats.activeIPsPercent !== undefined ? stats.activeIPsPercent : 23;
|
||||
document.getElementById('active-ips').textContent = formatNumber(activeIPs);
|
||||
document.getElementById('active-ips-percent').textContent = ipsPercent + '%';
|
||||
// 直接使用API返回的活跃IP数
|
||||
const activeIPs = stats.activeIPs !== undefined ? formatNumber(stats.activeIPs) : '---';
|
||||
const ipsPercent = stats.activeIPsPercent !== undefined ? stats.activeIPsPercent + '%' : '---';
|
||||
document.getElementById('active-ips').textContent = activeIPs;
|
||||
document.getElementById('active-ips-percent').textContent = ipsPercent;
|
||||
}
|
||||
|
||||
if (document.getElementById('cpu-usage')) {
|
||||
// 使用真实数据或模拟数据
|
||||
const cpuUsage = stats.cpuUsage !== undefined ? stats.cpuUsage : 45;
|
||||
document.getElementById('cpu-usage').textContent = cpuUsage + '%';
|
||||
// 保留两位小数并添加单位
|
||||
const cpuUsage = stats.cpuUsage ? stats.cpuUsage.toFixed(2) + '%' : '---';
|
||||
document.getElementById('cpu-usage').textContent = cpuUsage;
|
||||
|
||||
// 设置CPU状态颜色
|
||||
const cpuStatusElem = document.getElementById('cpu-status');
|
||||
if (cpuStatusElem) {
|
||||
if (cpuUsage > 80) {
|
||||
if (stats.cpuUsage !== undefined && stats.cpuUsage !== null) {
|
||||
if (stats.cpuUsage > 80) {
|
||||
cpuStatusElem.textContent = '警告';
|
||||
cpuStatusElem.className = 'text-danger text-sm flex items-center';
|
||||
} else if (cpuUsage > 60) {
|
||||
} else if (stats.cpuUsage > 60) {
|
||||
cpuStatusElem.textContent = '较高';
|
||||
cpuStatusElem.className = 'text-warning text-sm flex items-center';
|
||||
} else {
|
||||
cpuStatusElem.textContent = '正常';
|
||||
cpuStatusElem.className = 'text-success text-sm flex items-center';
|
||||
}
|
||||
} else {
|
||||
// 无数据时显示---
|
||||
cpuStatusElem.textContent = '---';
|
||||
cpuStatusElem.className = 'text-gray-400 text-sm flex items-center';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user