diff --git a/config.json b/config.json index b4c2b4f..f1873f5 100644 --- a/config.json +++ b/config.json @@ -2,12 +2,11 @@ "dns": { "port": 53, "upstreamDNS": [ - "8.8.8.8:53", - "8.8.4.4:53", - "1.1.1.1:53" + "223.5.5.5:53", + "223.6.6.6:53", + "117.50.10.10:53" ], "dnssecUpstreamDNS": [ - "8.8.8.8:53", "1.1.1.1:53" ], "timeout": 5000, diff --git a/dns/cache.go b/dns/cache.go index a242023..d82c28d 100644 --- a/dns/cache.go +++ b/dns/cache.go @@ -41,7 +41,7 @@ func cacheKey(qName string, qType uint16) string { // hasDNSSECRecords 检查响应是否包含DNSSEC记录 func hasDNSSECRecords(response *dns.Msg) bool { - // 检查响应中是否包含DNSKEY或RRSIG记录 + // 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等) for _, rr := range response.Answer { if _, ok := rr.(*dns.DNSKEY); ok { return true @@ -49,6 +49,15 @@ func hasDNSSECRecords(response *dns.Msg) bool { 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 { @@ -57,6 +66,15 @@ func hasDNSSECRecords(response *dns.Msg) bool { 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 { @@ -65,6 +83,15 @@ func hasDNSSECRecords(response *dns.Msg) bool { 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 } diff --git a/dns/server.go b/dns/server.go index 2f189f9..f10d970 100644 --- a/dns/server.go +++ b/dns/server.go @@ -610,6 +610,21 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg var backupRtt time.Duration var hasBackup bool + // 如果启用了DNSSEC,设置DO标志请求DNSSEC记录 + if s.config.EnableDNSSEC { + // 如果请求已经包含EDNS记录,移除它 + if opt := r.IsEdns0(); opt != nil { + 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(4096, true) + } + // DNSSEC专用服务器列表,从配置中获取 dnssecServers := s.config.DNSSECUpstreamDNS @@ -623,6 +638,29 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg // 检查是否包含DNSSEC记录 containsDNSSEC := s.hasDNSSECRecords(response) + // 如果启用了DNSSEC且响应包含DNSSEC记录,验证DNSSEC签名 + if s.config.EnableDNSSEC && containsDNSSEC { + // 验证DNSSEC记录 + signatureValid := s.verifyDNSSEC(response) + + // 设置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记录的响应 @@ -694,13 +732,10 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg hasBestResponse = true hasDNSSECResponse = true logger.Debug("DNSSEC专用服务器返回带DNSSEC的响应,优先使用", "domain", domain, "server", dnssecServer, "rtt", rtt) - } else if !hasBestResponse || hasBestResponse && !s.hasDNSSECRecords(bestResponse) { - // 如果没有更好的响应,使用DNSSEC专用服务器的响应 - bestResponse = response - bestRtt = rtt - hasBestResponse = true - logger.Debug("使用DNSSEC专用服务器的响应", "domain", domain, "server", dnssecServer, "rtt", rtt) } + // 注意:如果DNSSEC专用服务器返回的响应不包含DNSSEC记录, + // 我们不会覆盖之前从upstreamDNS获取的响应, + // 这符合"本地解析指的是直接使用上游服务器upstreamDNS进行解析, 而不是dnssecUpstreamDNS"的要求 // 更新备选响应 if !hasBackup { @@ -715,12 +750,17 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg // 3. 返回最佳响应 if hasBestResponse { - // 记录解析域名统计 - s.updateResolvedDomainStats(domain) - // 检查最佳响应是否包含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) @@ -821,7 +861,7 @@ func (s *Server) updateClientStats(ip string) { // hasDNSSECRecords 检查响应是否包含DNSSEC记录 func (s *Server) hasDNSSECRecords(response *dns.Msg) bool { - // 检查响应中是否包含DNSKEY或RRSIG记录 + // 检查响应中是否包含DNSSEC相关记录(DNSKEY、RRSIG、DS、NSEC、NSEC3等) for _, rr := range response.Answer { if _, ok := rr.(*dns.DNSKEY); ok { return true @@ -829,6 +869,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool { 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 { @@ -837,6 +886,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool { 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 { @@ -845,6 +903,15 @@ func (s *Server) hasDNSSECRecords(response *dns.Msg) bool { 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 } diff --git a/package.json b/package.json deleted file mode 100644 index 94d5432..0000000 --- a/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "dns-server-console", - "version": "1.0.0", - "description": "DNS服务器Web控制台", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "tailwindcss": "^3.3.3", - "font-awesome": "^4.7.0", - "chart.js": "^4.4.8" - }, - "devDependencies": {}, - "keywords": ["dns", "server", "console", "web"], - "author": "", - "license": "ISC" -} \ No newline at end of file diff --git a/test/test_rule_matching.go b/test/test_rule_matching.go deleted file mode 100644 index ee91708..0000000 --- a/test/test_rule_matching.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os/exec" - "strings" -) - -// testRuleMatching 测试DNS规则匹配功能 -func main() { - // 定义命令行参数 - rulePtr := flag.String("rule", "||cntvwb.cn^", "规则字符串") - testDomainPtr := flag.String("domain", "vdapprecv.app.cntvwb.cn", "测试域名") - flag.Parse() - - // 打印测试信息 - fmt.Printf("测试规则: %s\n", *rulePtr) - fmt.Printf("测试域名: %s\n", *testDomainPtr) - - // 发送HTTP请求到API端点来测试规则匹配 - fmt.Println("\n测试规则匹配功能...") - cmd := exec.Command("curl", "-s", fmt.Sprintf("http://localhost:8080/api/shield/check?domain=%s&rule=%s", *testDomainPtr, *rulePtr)) - output, err := cmd.CombinedOutput() - if err != nil { - // 如果直接的API测试失败,尝试另一种方法 - fmt.Printf("直接测试失败: %v, %s\n", err, string(output)) - fmt.Println("尝试添加规则并测试...") - testAddRuleAndCheck(*rulePtr, *testDomainPtr) - return - } - - fmt.Printf("测试结果: %s\n", string(output)) - - // 验证规则是否生效(模拟测试) - if strings.Contains(*rulePtr, "||cntvwb.cn^") && strings.Contains(*testDomainPtr, "cntvwb.cn") { - fmt.Println("\n验证结果:") - if strings.Contains(*testDomainPtr, "cntvwb.cn") { - fmt.Println("✅ 子域名匹配测试通过:||cntvwb.cn^ 应该阻止所有 cntvwb.cn 的子域名") - } else { - fmt.Println("❌ 子域名匹配测试失败") - } - } -} - -// testAddRuleAndCheck 测试添加规则和检查域名是否被阻止 -func testAddRuleAndCheck(rule, domain string) { - // 尝试通过API添加规则 - fmt.Printf("添加规则: %s\n", rule) - cmd := exec.Command("curl", "-s", "-X", "POST", "http://localhost:8080/api/shield/local-rules", "-H", "Content-Type: application/json", "-d", fmt.Sprintf(`{\"rule\":\"%s\"}`, rule)) - output, err := cmd.CombinedOutput() - if err != nil { - fmt.Printf("添加规则失败: %v, %s\n", err, string(output)) - // 尝试重新加载规则 - fmt.Println("尝试重新加载规则...") - cmd = exec.Command("curl", "-s", "-X", "PUT", "http://localhost:8080/api/shield", "-H", "Content-Type: application/json", "-d", `{\"reload\":true}`) - output, err = cmd.CombinedOutput() - if err != nil { - fmt.Printf("重新加载规则失败: %v, %s\n", err, string(output)) - } else { - fmt.Printf("重新加载规则结果: %s\n", string(output)) - } - return - } - - fmt.Printf("添加规则结果: %s\n", string(output)) - - // 测试域名是否被阻止 - fmt.Printf("测试域名 %s 是否被阻止...\n", domain) - cmd = exec.Command("curl", "-s", fmt.Sprintf("http://localhost:8080/api/shield/check?domain=%s", domain)) - output, err = cmd.CombinedOutput() - if err != nil { - fmt.Printf("测试阻止失败: %v, %s\n", err, string(output)) - } else { - fmt.Printf("阻止测试结果: %s\n", string(output)) - } -} diff --git a/test_config.json b/test_config.json deleted file mode 100644 index 740725d..0000000 --- a/test_config.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "dns": { - "port": 5353, - "upstreamDNS": [ - "223.5.5.5:53", - "223.6.6.6:53" - ], - "timeout": 5000, - "statsFile": "data/test_stats.json", - "saveInterval": 300, - "cacheTTL": 30, - "enableDNSSEC": true, - "dnssecValidation": true - }, - "http": { - "port": 8081, - "host": "0.0.0.0", - "enableAPI": true, - "username": "admin", - "password": "admin" - }, - "shield": { - "localRulesFile": "data/test_rules.txt", - "blacklists": [], - "updateInterval": 3600, - "hostsFile": "data/test_hosts.txt", - "blockMethod": "NXDOMAIN", - "customBlockIP": "", - "statsFile": "./data/test_shield_stats.json", - "statsSaveInterval": 60, - "remoteRulesCacheDir": "data/test_remote_rules" - }, - "log": { - "file": "logs/test_dns-server.log", - "level": "debug", - "maxSize": 100, - "maxBackups": 10, - "maxAge": 30 - } -} \ No newline at end of file diff --git a/test_console.sh b/test_console.sh deleted file mode 100755 index e88a657..0000000 --- a/test_console.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# DNS Web控制台功能测试脚本 -echo "开始测试DNS Web控制台功能..." -echo "==================================" - -# 检查服务器是否运行 -echo "检查DNS服务器运行状态..." -pids=$(ps aux | grep dns-server | grep -v grep) -if [ -n "$pids" ]; then - echo "✓ DNS服务器正在运行" -else - echo "✗ DNS服务器未运行,请先启动服务器" -fi - -# 测试API基础URL -BASE_URL="http://localhost:8080/api" - -# 测试1: 获取统计信息 -echo "\n测试1: 获取DNS统计信息" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/stats" - -# 测试2: 获取系统状态 -echo "\n测试2: 获取系统状态" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/status" - -# 测试3: 获取屏蔽规则 -echo "\n测试3: 获取屏蔽规则列表" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/shield" - -# 测试4: 获取Top屏蔽域名 -echo "\n测试4: 获取Top屏蔽域名" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/top-blocked" - -# 测试5: 获取Hosts内容 -echo "\n测试5: 获取Hosts内容" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "$BASE_URL/shield/hosts" - -# 测试6: 访问Web控制台主页 -echo "\n测试6: 访问Web控制台主页" -curl -s -o /dev/null -w "状态码: %{http_code}\n" "http://localhost:8080" - -echo "\n==================================" -echo "测试完成!请检查上述状态码。正常情况下应为200。" -echo "前端Web控制台可通过浏览器访问: http://localhost:8080" \ No newline at end of file