diff --git a/config/config.go b/config/config.go index 59b406e..0e39336 100644 --- a/config/config.go +++ b/config/config.go @@ -5,11 +5,13 @@ import ( "io/ioutil" ) -// DNSConfig DNS服务器配置 +// DNSConfig DNS配置 type DNSConfig struct { - Port int `json:"port"` - UpstreamDNS []string `json:"upstreamDNS"` - Timeout int `json:"timeout"` + Port int `json:"port"` + UpstreamDNS []string `json:"upstreamDNS"` + Timeout int `json:"timeout"` + StatsFile string `json:"statsFile"` // 统计数据持久化文件 + SaveInterval int `json:"saveInterval"` // 数据保存间隔(秒) } // HTTPConfig HTTP控制台配置 @@ -21,12 +23,14 @@ type HTTPConfig struct { // ShieldConfig 屏蔽规则配置 type ShieldConfig struct { - LocalRulesFile string `json:"localRulesFile"` - RemoteRules []string `json:"remoteRules"` - UpdateInterval int `json:"updateInterval"` - HostsFile string `json:"hostsFile"` - BlockMethod string `json:"blockMethod"` // 屏蔽方法: "NXDOMAIN", "refused", "emptyIP", "customIP" - CustomBlockIP string `json:"customBlockIP"` // 自定义屏蔽IP,当BlockMethod为"customIP"时使用 + LocalRulesFile string `json:"localRulesFile"` + RemoteRules []string `json:"remoteRules"` + UpdateInterval int `json:"updateInterval"` + HostsFile string `json:"hostsFile"` + BlockMethod string `json:"blockMethod"` // 屏蔽方法: "NXDOMAIN", "refused", "emptyIP", "customIP" + CustomBlockIP string `json:"customBlockIP"` // 自定义屏蔽IP,当BlockMethod为"customIP"时使用 + StatsFile string `json:"statsFile"` // 计数数据持久化文件 + StatsSaveInterval int `json:"statsSaveInterval"` // 计数数据保存间隔(秒) } // LogConfig 日志配置 @@ -64,7 +68,13 @@ func LoadConfig(path string) (*Config, error) { config.DNS.Port = 53 } if len(config.DNS.UpstreamDNS) == 0 { - config.DNS.UpstreamDNS = []string{"8.8.8.8:53", "1.1.1.1:53"} + config.DNS.UpstreamDNS = []string{"223.5.5.5:53", "223.6.6.6:53"} + } + if config.DNS.StatsFile == "" { + config.DNS.StatsFile = "./data/stats.json" // 默认统计数据文件路径 + } + if config.DNS.SaveInterval == 0 { + config.DNS.SaveInterval = 300 // 默认5分钟保存一次 } if config.HTTP.Port == 0 { config.HTTP.Port = 8080 @@ -78,6 +88,12 @@ func LoadConfig(path string) (*Config, error) { if config.Shield.BlockMethod == "" { config.Shield.BlockMethod = "NXDOMAIN" // 默认屏蔽方法为NXDOMAIN } + if config.Shield.StatsFile == "" { + config.Shield.StatsFile = "./data/shield_stats.json" // 默认Shield统计数据文件路径 + } + if config.Shield.StatsSaveInterval == 0 { + config.Shield.StatsSaveInterval = 300 // 默认5分钟保存一次 + } if config.Log.Level == "" { config.Log.Level = "info" } diff --git a/data/shield_stats.json b/data/shield_stats.json new file mode 100644 index 0000000..e69de29 diff --git a/data/stats.json b/data/stats.json new file mode 100644 index 0000000..82e2882 --- /dev/null +++ b/data/stats.json @@ -0,0 +1,42 @@ +{ + "stats": { + "Queries": 33, + "Blocked": 24, + "Allowed": 19, + "Errors": 2, + "LastQuery": "2025-11-23T19:06:10.694259822+08:00" + }, + "blockedDomains": { + "ad.qq.com": { + "Domain": "ad.qq.com", + "Count": 6, + "LastSeen": "2025-11-23T19:06:10.694532789+08:00" + }, + "ad.qq.com.amazehome.xyz": { + "Domain": "ad.qq.com.amazehome.xyz", + "Count": 6, + "LastSeen": "2025-11-23T19:06:10.691565251+08:00" + } + }, + "resolvedDomains": { + "ad.qq.com": { + "Domain": "ad.qq.com", + "Count": 10, + "LastSeen": "2025-11-23T19:05:54.429002187+08:00" + }, + "ad.qq.com.amazehome.xyz": { + "Domain": "ad.qq.com.amazehome.xyz", + "Count": 8, + "LastSeen": "2025-11-23T19:05:54.356216418+08:00" + }, + "apd-pcdnwxstat.teg.tencent-cloud.net": { + "Domain": "apd-pcdnwxstat.teg.tencent-cloud.net", + "Count": 1, + "LastSeen": "2025-11-23T19:05:56.842653777+08:00" + } + }, + "hourlyStats": { + "2025-11-23-19": 12 + }, + "lastSaved": "2025-11-23T19:06:21.194988376+08:00" +} \ No newline at end of file diff --git a/dns-server b/dns-server index bf5214f..e75185b 100755 Binary files a/dns-server and b/dns-server differ diff --git a/dns-server.log b/dns-server.log index 18babf7..12d5de4 100644 --- a/dns-server.log +++ b/dns-server.log @@ -230,3 +230,152 @@ time="2025-11-23T18:30:48+08:00" level=debug msg="接收到DNS查询" client="10 time="2025-11-23T18:30:48+08:00" level=error msg="DNS查询失败" domain=ads.qq.com time="2025-11-23T18:31:49+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:8360" domain=self.events.data.microsoft.com type=65 time="2025-11-23T18:31:49+08:00" level=debug msg="DNS查询成功" domain=self.events.data.microsoft.com rtt=14.34768ms server="223.5.5.5:53" +time="2025-11-23T18:35:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:34681" domain=apd-pcdnwxstat.teg.tencent-cloud.net type=1 +time="2025-11-23T18:35:09+08:00" level=debug msg="DNS查询成功" domain=apd-pcdnwxstat.teg.tencent-cloud.net rtt=5.914665ms server="223.5.5.5:53" +time="2025-11-23T18:37:26+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:8652" domain=apd-pcdnwxstat.teg.tencent-cloud.net type=1 +time="2025-11-23T18:37:26+08:00" level=debug msg="DNS查询成功" domain=apd-pcdnwxstat.teg.tencent-cloud.net rtt=5.903025ms server="223.5.5.5:53" +time="2025-11-23T18:39:07+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:15083" domain=metrics1-drcn.dt.dbankcloud.cn type=1 +time="2025-11-23T18:39:07+08:00" level=debug msg="DNS查询成功" domain=metrics1-drcn.dt.dbankcloud.cn rtt=5.213232ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:61317" domain=appdl-1-drcn.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:4656" domain=appdl-drcn.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appdl-1-drcn.dbankcdn.com rtt=4.647968ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appdl-drcn.dbankcdn.com rtt=4.474298ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:47439" domain=appoptimize-drcn.dbankcdn.cn type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:50747" domain=appimg-drcn.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appoptimize-drcn.dbankcdn.cn rtt=4.028323ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appimg-drcn.dbankcdn.com rtt=4.806171ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:58085" domain=store-drcn.hispace.dbankcloud.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=store-drcn.hispace.dbankcloud.com rtt=4.298717ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:65072" domain=contentcenter-drcn.dbankcdn.cn type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:9115" domain=api-drcn.theme.dbankcloud.cn type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=contentcenter-drcn.dbankcdn.cn rtt=5.394543ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=api-drcn.theme.dbankcloud.cn rtt=5.24299ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:58544" domain=magazine-drcn.theme.dbankcloud.cn type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:22253" domain=magazine-drcn.theme.dbankcloud.cn type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=magazine-drcn.theme.dbankcloud.cn rtt=5.682249ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=magazine-drcn.theme.dbankcloud.cn rtt=5.599705ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:32221" domain=agpicnsp-drcn.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:3445" domain=appimg.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=agpicnsp-drcn.dbankcdn.com rtt=5.96018ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appimg.dbankcdn.com rtt=13.633739ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:16444" domain=f00b1b869deb32d2ad60ba514bb876ea.b.hon.cc.cdnhwc8.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=f00b1b869deb32d2ad60ba514bb876ea.b.hon.cc.cdnhwc8.com rtt=15.020117ms server="223.5.5.5:53" +time="2025-11-23T18:39:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:43083" domain=appdl-2-drcn.dbankcdn.com type=1 +time="2025-11-23T18:39:09+08:00" level=debug msg="DNS查询成功" domain=appdl-2-drcn.dbankcdn.com rtt=4.087138ms server="223.5.5.5:53" +time="2025-11-23T18:39:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:41949" domain=grs.dbankcloud.com type=1 +time="2025-11-23T18:39:10+08:00" level=debug msg="DNS查询成功" domain=grs.dbankcloud.com rtt=6.434814ms server="223.5.5.5:53" +time="2025-11-23T18:39:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:18043" domain=sdkserver.op.hicloud.com type=1 +time="2025-11-23T18:39:10+08:00" level=debug msg="DNS查询成功" domain=sdkserver.op.hicloud.com rtt=6.663746ms server="223.5.5.5:53" +time="2025-11-23T18:39:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:27296" domain=events.op.hicloud.com type=1 +time="2025-11-23T18:39:10+08:00" level=debug msg="DNS查询成功" domain=events.op.hicloud.com rtt=4.076785ms server="223.5.5.5:53" +time="2025-11-23T18:39:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:20705" domain=acd.op.hicloud.com type=1 +time="2025-11-23T18:39:10+08:00" level=debug msg="DNS查询成功" domain=acd.op.hicloud.com rtt=5.188271ms server="223.5.5.5:53" +time="2025-11-23T18:39:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:11580" domain=api.cloud.huawei.com type=1 +time="2025-11-23T18:39:11+08:00" level=debug msg="DNS查询成功" domain=api.cloud.huawei.com rtt=5.414894ms server="223.5.5.5:53" +time="2025-11-23T18:39:11+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:16274" domain=sdkserver-drcn.op.dbankcloud.cn type=1 +time="2025-11-23T18:39:11+08:00" level=debug msg="DNS查询成功" domain=sdkserver-drcn.op.dbankcloud.cn rtt=5.745651ms server="223.5.5.5:53" +time="2025-11-23T18:39:12+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:54852" domain=tsms-drcn.security.dbankcloud.cn type=1 +time="2025-11-23T18:39:12+08:00" level=debug msg="DNS查询成功" domain=tsms-drcn.security.dbankcloud.cn rtt=5.889368ms server="223.5.5.5:53" +time="2025-11-23T18:39:18+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:56128" domain=store-drcn.hispace.dbankcloud.com type=1 +time="2025-11-23T18:39:18+08:00" level=debug msg="DNS查询成功" domain=store-drcn.hispace.dbankcloud.com rtt=5.222492ms server="223.5.5.5:53" +time="2025-11-23T18:41:22+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:6990" domain=dldir1v6.qq.com type=1 +time="2025-11-23T18:41:22+08:00" level=debug msg="DNS查询成功" domain=dldir1v6.qq.com rtt=5.968557ms server="223.5.5.5:53" +time="2025-11-23T18:48:02+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:20065" domain=dns.weixin.qq.com.cn type=1 +time="2025-11-23T18:48:02+08:00" level=debug msg="DNS查询成功" domain=dns.weixin.qq.com.cn rtt=5.24456ms server="223.5.5.5:53" +time="2025-11-23T18:52:01+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:2060" domain=apd-pcdnwxlogin.teg.tencent-cloud.net type=1 +time="2025-11-23T18:52:01+08:00" level=debug msg="DNS查询成功" domain=apd-pcdnwxlogin.teg.tencent-cloud.net rtt=6.190907ms server="223.5.5.5:53" +time="2025-11-23T18:55:03+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:12293" domain=apd-pcdnwxnat.teg.tencent-cloud.net type=1 +time="2025-11-23T18:55:03+08:00" level=debug msg="DNS查询成功" domain=apd-pcdnwxnat.teg.tencent-cloud.net rtt=5.919306ms server="223.5.5.5:53" +time="2025-11-23T18:55:34+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:27926" domain=aeventlog.beacon.qq.com type=1 +time="2025-11-23T18:55:34+08:00" level=debug msg="DNS查询成功" domain=aeventlog.beacon.qq.com rtt=6.692955ms server="223.5.5.5:53" +time="2025-11-23T18:57:28+08:00" level=info msg="正在关闭服务..." +time="2025-11-23T18:57:28+08:00" level=info msg="DNS服务器已停止" +time="2025-11-23T18:57:28+08:00" level=error msg="HTTP控制台服务器启动失败" error="http: Server closed" +time="2025-11-23T18:57:28+08:00" level=info msg="HTTP控制台服务器已停止" +time="2025-11-23T18:57:28+08:00" level=info msg="所有服务已关闭" +time="2025-11-23T18:57:28+08:00" level=warning msg="日志系统已关闭" +time="2025-11-23T19:05:40+08:00" level=error msg="获取远程规则失败" error="远程服务器返回错误状态码: 404" url="https://example.com/rules.txt" +time="2025-11-23T19:05:40+08:00" level=info msg="规则加载完成,域名规则: 2, 排除规则: 0, 正则规则: 2, hosts规则: 3" +time="2025-11-23T19:05:40+08:00" level=info msg="DNS服务器已启动,监听端口: 53" +time="2025-11-23T19:05:40+08:00" level=info msg="HTTP控制台已启动,监听端口: 8080" +time="2025-11-23T19:05:40+08:00" level=info msg="HTTP控制台服务器启动,监听地址: 0.0.0.0:8080" +time="2025-11-23T19:05:40+08:00" level=info msg="DNS TCP服务器启动,监听端口: 53" +time="2025-11-23T19:05:40+08:00" level=info msg="规则自动更新已启动" interval=3600 +time="2025-11-23T19:05:40+08:00" level=info msg="启动Shield计数数据自动保存功能" file=./data/shield_stats.json interval=300 +time="2025-11-23T19:05:40+08:00" level=info msg="DNS UDP服务器启动,监听端口: 53" +time="2025-11-23T19:05:40+08:00" level=info msg="Shield计数数据保存成功" +time="2025-11-23T19:05:51+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:63504" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=68.496416ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:63505" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=22.855544ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:63506" domain=ad.qq.com type=1 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=23.728363ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62552" domain=ad.qq.com type=28 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=27.123116ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62553" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=5.560802ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62554" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=4.628616ms server="223.6.6.6:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62555" domain=ad.qq.com type=1 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=32.441875ms server="223.5.5.5:53" +time="2025-11-23T19:05:52+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62556" domain=ad.qq.com type=28 +time="2025-11-23T19:05:52+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=16.630593ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62557" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=37.27035ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62558" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:05:53+08:00" level=error msg="DNS查询失败" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62559" domain=ad.qq.com type=1 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=14.2654ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:62560" domain=ad.qq.com type=28 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=6.551795ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52135" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=31.31022ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52136" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=30.611352ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52137" domain=ad.qq.com type=1 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=43.032729ms server="223.5.5.5:53" +time="2025-11-23T19:05:53+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52138" domain=ad.qq.com type=28 +time="2025-11-23T19:05:53+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=27.754923ms server="223.5.5.5:53" +time="2025-11-23T19:05:54+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52139" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:05:54+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com.amazehome.xyz rtt=5.00309ms server="223.5.5.5:53" +time="2025-11-23T19:05:54+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52140" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:05:54+08:00" level=error msg="DNS查询失败" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:05:54+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52141" domain=ad.qq.com type=1 +time="2025-11-23T19:05:54+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=18.018889ms server="223.5.5.5:53" +time="2025-11-23T19:05:54+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:52142" domain=ad.qq.com type=28 +time="2025-11-23T19:05:54+08:00" level=debug msg="DNS查询成功" domain=ad.qq.com rtt=32.759047ms server="223.5.5.5:53" +time="2025-11-23T19:05:56+08:00" level=debug msg="接收到DNS查询" client="10.35.10.11:32332" domain=apd-pcdnwxstat.teg.tencent-cloud.net type=1 +time="2025-11-23T19:05:56+08:00" level=debug msg="DNS查询成功" domain=apd-pcdnwxstat.teg.tencent-cloud.net rtt=6.574939ms server="223.5.5.5:53" +time="2025-11-23T19:06:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49579" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:06:09+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49579" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49580" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:06:09+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49580" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49581" domain=ad.qq.com type=1 +time="2025-11-23T19:06:09+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49581" domain=ad.qq.com +time="2025-11-23T19:06:09+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49582" domain=ad.qq.com type=28 +time="2025-11-23T19:06:09+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49582" domain=ad.qq.com +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49583" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49583" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49584" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49584" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49585" domain=ad.qq.com type=1 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49585" domain=ad.qq.com +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:49586" domain=ad.qq.com type=28 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:49586" domain=ad.qq.com +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:64329" domain=ad.qq.com.amazehome.xyz type=1 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:64329" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:64330" domain=ad.qq.com.amazehome.xyz type=28 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:64330" domain=ad.qq.com.amazehome.xyz +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:64331" domain=ad.qq.com type=1 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:64331" domain=ad.qq.com +time="2025-11-23T19:06:10+08:00" level=debug msg="接收到DNS查询" client="10.35.10.78:64332" domain=ad.qq.com type=28 +time="2025-11-23T19:06:10+08:00" level=info msg="域名被屏蔽" client="10.35.10.78:64332" domain=ad.qq.com +time="2025-11-23T19:06:21+08:00" level=info msg="正在关闭服务..." +time="2025-11-23T19:06:21+08:00" level=info msg="统计数据保存成功" +time="2025-11-23T19:06:21+08:00" level=info msg="DNS服务器已停止" +time="2025-11-23T19:06:21+08:00" level=error msg="HTTP控制台服务器启动失败" error="http: Server closed" +time="2025-11-23T19:06:21+08:00" level=info msg="HTTP控制台服务器已停止" +time="2025-11-23T19:06:21+08:00" level=info msg="Shield计数数据保存成功" +time="2025-11-23T19:06:21+08:00" level=info msg="规则自动更新已停止" +time="2025-11-23T19:06:21+08:00" level=info msg="所有服务已关闭" +time="2025-11-23T19:06:21+08:00" level=warning msg="日志系统已关闭" diff --git a/dns/server.go b/dns/server.go index c3c38a6..4d981a3 100644 --- a/dns/server.go +++ b/dns/server.go @@ -2,8 +2,12 @@ package dns import ( "context" + "encoding/json" "fmt" + "io/ioutil" "net" + "os" + "path/filepath" "sort" "sync" "time" @@ -23,6 +27,15 @@ type BlockedDomain struct { LastSeen time.Time } +// StatsData 用于持久化的统计数据结构 +type StatsData struct { + Stats *Stats `json:"stats"` + BlockedDomains map[string]*BlockedDomain `json:"blockedDomains"` + ResolvedDomains map[string]*BlockedDomain `json:"resolvedDomains"` + HourlyStats map[string]int64 `json:"hourlyStats"` + LastSaved time.Time `json:"lastSaved"` +} + // Server DNS服务器 type Server struct { config *config.DNSConfig @@ -40,6 +53,8 @@ type Server struct { resolvedDomains map[string]*BlockedDomain // 用于记录解析的域名 hourlyStatsMutex sync.RWMutex hourlyStats map[string]int64 // 按小时统计屏蔽数量 + saveTicker *time.Ticker // 用于定时保存数据 + saveDone chan struct{} // 用于通知保存协程停止 } // Stats DNS服务器统计信息 @@ -54,7 +69,7 @@ type Stats struct { // NewServer 创建DNS服务器实例 func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shieldManager *shield.ShieldManager) *Server { ctx, cancel := context.WithCancel(context.Background()) - return &Server{ + server := &Server{ config: config, shieldConfig: shieldConfig, shieldManager: shieldManager, @@ -73,7 +88,14 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie blockedDomains: make(map[string]*BlockedDomain), resolvedDomains: make(map[string]*BlockedDomain), hourlyStats: make(map[string]int64), + saveDone: make(chan struct{}), } + + // 加载已保存的统计数据 + server.loadStatsData() + + return server + } // Start 启动DNS服务器 @@ -116,10 +138,17 @@ func (s *Server) Start() error { // Stop 停止DNS服务器 func (s *Server) Stop() { + // 发送停止信号给保存协程 + close(s.saveDone) + + // 最后保存一次数据 + s.saveStatsData() + + // 停止服务器 + s.cancel() if s.server != nil { s.server.Shutdown() } - s.cancel() logger.Info("DNS服务器已停止") } @@ -452,3 +481,135 @@ func (s *Server) GetHourlyStats() map[string]int64 { } return result } + +// loadStatsData 从文件加载统计数据 +func (s *Server) loadStatsData() { + if s.config.StatsFile == "" { + return + } + + // 检查文件是否存在 + data, err := ioutil.ReadFile(s.config.StatsFile) + if err != nil { + if !os.IsNotExist(err) { + logger.Error("读取统计数据文件失败", "error", err) + } + return + } + + var statsData StatsData + err = json.Unmarshal(data, &statsData) + if err != nil { + logger.Error("解析统计数据失败", "error", err) + return + } + + // 恢复统计数据 + s.statsMutex.Lock() + if statsData.Stats != nil { + s.stats = statsData.Stats + } + s.statsMutex.Unlock() + + s.blockedDomainsMutex.Lock() + if statsData.BlockedDomains != nil { + s.blockedDomains = statsData.BlockedDomains + } + s.blockedDomainsMutex.Unlock() + + s.resolvedDomainsMutex.Lock() + if statsData.ResolvedDomains != nil { + s.resolvedDomains = statsData.ResolvedDomains + } + s.resolvedDomainsMutex.Unlock() + + s.hourlyStatsMutex.Lock() + if statsData.HourlyStats != nil { + s.hourlyStats = statsData.HourlyStats + } + s.hourlyStatsMutex.Unlock() + + logger.Info("统计数据加载成功") +} + +// saveStatsData 保存统计数据到文件 +func (s *Server) saveStatsData() { + if s.config.StatsFile == "" { + return + } + + // 创建数据目录 + statsDir := filepath.Dir(s.config.StatsFile) + err := os.MkdirAll(statsDir, 0755) + if err != nil { + logger.Error("创建统计数据目录失败", "error", err) + return + } + + // 收集所有统计数据 + statsData := &StatsData{ + Stats: s.GetStats(), + LastSaved: time.Now(), + } + + // 复制域名数据 + s.blockedDomainsMutex.RLock() + statsData.BlockedDomains = make(map[string]*BlockedDomain) + for k, v := range s.blockedDomains { + statsData.BlockedDomains[k] = v + } + s.blockedDomainsMutex.RUnlock() + + s.resolvedDomainsMutex.RLock() + statsData.ResolvedDomains = make(map[string]*BlockedDomain) + for k, v := range s.resolvedDomains { + statsData.ResolvedDomains[k] = v + } + s.resolvedDomainsMutex.RUnlock() + + s.hourlyStatsMutex.RLock() + statsData.HourlyStats = make(map[string]int64) + for k, v := range s.hourlyStats { + statsData.HourlyStats[k] = v + } + s.hourlyStatsMutex.RUnlock() + + // 序列化数据 + jsonData, err := json.MarshalIndent(statsData, "", " ") + if err != nil { + logger.Error("序列化统计数据失败", "error", err) + return + } + + // 写入文件 + err = ioutil.WriteFile(s.config.StatsFile, jsonData, 0644) + if err != nil { + logger.Error("保存统计数据到文件失败", "error", err) + return + } + + logger.Info("统计数据保存成功") +} + +// startAutoSave 启动自动保存功能 +func (s *Server) startAutoSave() { + if s.config.StatsFile == "" || s.config.SaveInterval <= 0 { + return + } + + // 设置定时器 + s.saveTicker = time.NewTicker(time.Duration(s.config.SaveInterval) * time.Second) + defer s.saveTicker.Stop() + + logger.Info("启动统计数据自动保存功能", "interval", s.config.SaveInterval, "file", s.config.StatsFile) + + // 定期保存数据 + for { + select { + case <-s.saveTicker.C: + s.saveStatsData() + case <-s.saveDone: + return + } + } +} diff --git a/rules.txt b/rules.txt index 4865f49..bd67aa8 100644 --- a/rules.txt +++ b/rules.txt @@ -1,3 +1,5 @@ ||hm.baidu.com +||baidu.com /.*tracking.*/ -/adjust.net/ \ No newline at end of file +/adjust.net/ +/ad./ \ No newline at end of file diff --git a/shield/manager.go b/shield/manager.go index 63bdaa0..a8afd07 100644 --- a/shield/manager.go +++ b/shield/manager.go @@ -3,10 +3,12 @@ package shield import ( "bufio" "context" + "encoding/json" "fmt" "io/ioutil" "net/http" "os" + "path/filepath" "regexp" "sort" "strings" @@ -23,6 +25,13 @@ type regexRule struct { original string } +// ShieldStatsData 用于持久化的Shield统计数据 +type ShieldStatsData struct { + BlockedDomainsCount map[string]int `json:"blockedDomainsCount"` + ResolvedDomainsCount map[string]int `json:"resolvedDomainsCount"` + LastSaved time.Time `json:"lastSaved"` +} + // ShieldManager 屏蔽管理器 type ShieldManager struct { config *config.ShieldConfig @@ -42,7 +51,7 @@ type ShieldManager struct { // NewShieldManager 创建屏蔽管理器实例 func NewShieldManager(config *config.ShieldConfig) *ShieldManager { ctx, cancel := context.WithCancel(context.Background()) - return &ShieldManager{ + manager := &ShieldManager{ config: config, domainRules: make(map[string]bool), domainExceptions: make(map[string]bool), @@ -54,6 +63,11 @@ func NewShieldManager(config *config.ShieldConfig) *ShieldManager { updateCtx: ctx, updateCancel: cancel, } + + // 加载已保存的计数数据 + manager.loadStatsData() + + return manager } // LoadRules 加载屏蔽规则 @@ -651,6 +665,9 @@ func (m *ShieldManager) StartAutoUpdate() { ticker := time.NewTicker(time.Duration(m.config.UpdateInterval) * time.Second) defer ticker.Stop() + // 启动自动保存计数数据 + go m.startAutoSaveStats() + for { select { case <-ticker.C: @@ -661,16 +678,27 @@ func (m *ShieldManager) StartAutoUpdate() { logger.Info("自动更新规则成功") } case <-m.updateCtx.Done(): + // 保存计数数据 + m.saveStatsData() m.updateRunning = false return } } }() + + logger.Info("规则自动更新已启动", "interval", m.config.UpdateInterval) + + // 如果是首次启动,先保存一次数据确保目录存在 + go m.saveStatsData() } // StopAutoUpdate 停止自动更新 func (m *ShieldManager) StopAutoUpdate() { + m.updateRunning = false m.updateCancel() + // 保存计数数据 + m.saveStatsData() + logger.Info("规则自动更新已停止") } // saveRulesToFile 保存规则到文件 @@ -781,7 +809,112 @@ func (m *ShieldManager) GetStats() map[string]interface{} { } } -// GetRules 获取所有规则的详细列表 +// loadStatsData 从文件加载计数数据 +func (m *ShieldManager) loadStatsData() { + if m.config.StatsFile == "" { + return + } + + // 检查文件是否存在 + data, err := ioutil.ReadFile(m.config.StatsFile) + if err != nil { + if !os.IsNotExist(err) { + logger.Error("读取Shield计数数据文件失败", "error", err) + } + return + } + + var statsData ShieldStatsData + err = json.Unmarshal(data, &statsData) + if err != nil { + logger.Error("解析Shield计数数据失败", "error", err) + return + } + + // 恢复计数数据 + m.rulesMutex.Lock() + if statsData.BlockedDomainsCount != nil { + m.blockedDomainsCount = statsData.BlockedDomainsCount + } + if statsData.ResolvedDomainsCount != nil { + m.resolvedDomainsCount = statsData.ResolvedDomainsCount + } + m.rulesMutex.Unlock() + + logger.Info("Shield计数数据加载成功") +} + +// saveStatsData 保存计数数据到文件 +func (m *ShieldManager) saveStatsData() { + if m.config.StatsFile == "" { + return + } + + // 创建数据目录 + statsDir := filepath.Dir(m.config.StatsFile) + err := os.MkdirAll(statsDir, 0755) + if err != nil { + logger.Error("创建Shield统计数据目录失败", "error", err) + return + } + + // 收集计数数据 + m.rulesMutex.RLock() + statsData := &ShieldStatsData{ + BlockedDomainsCount: make(map[string]int), + ResolvedDomainsCount: make(map[string]int), + LastSaved: time.Now(), + } + + // 复制数据 + for k, v := range m.blockedDomainsCount { + statsData.BlockedDomainsCount[k] = v + } + for k, v := range m.resolvedDomainsCount { + statsData.ResolvedDomainsCount[k] = v + } + m.rulesMutex.RUnlock() + + // 序列化数据 + jsonData, err := json.MarshalIndent(statsData, "", " ") + if err != nil { + logger.Error("序列化Shield计数数据失败", "error", err) + return + } + + // 写入文件 + err = ioutil.WriteFile(m.config.StatsFile, jsonData, 0644) + if err != nil { + logger.Error("保存Shield计数数据到文件失败", "error", err) + return + } + + logger.Info("Shield计数数据保存成功") +} + +// startAutoSaveStats 启动计数数据自动保存功能 +func (m *ShieldManager) startAutoSaveStats() { + if m.config.StatsFile == "" || m.config.StatsSaveInterval <= 0 { + return + } + + ticker := time.NewTicker(time.Duration(m.config.StatsSaveInterval) * time.Second) + defer ticker.Stop() + + logger.Info("启动Shield计数数据自动保存功能", "interval", m.config.StatsSaveInterval, "file", m.config.StatsFile) + + // 定期保存数据 + for { + select { + case <-ticker.C: + m.saveStatsData() + case <-m.updateCtx.Done(): + return + } + } +} + +// GetRules 获取所有规则 func (m *ShieldManager) GetRules() map[string]interface{} { m.rulesMutex.RLock() defer m.rulesMutex.RUnlock()