diff --git a/CHANGELOG.md b/CHANGELOG.md index f2bf328..fee0a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ 所有对本项目的显著更改都将记录在此文件中。 +## [1.1.4] - 2025-12-21 + +### 修复 +- 修复规则优先级问题:确保自定义规则优先于远程规则 +- 修复添加自定义规则后需要重启服务器的问题:通过在添加或删除规则后清空DNS缓存实现 + ## [1.1.3] - 2025-12-19 ### 移除 diff --git a/README.md b/README.md index 4090699..701b6e6 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ - 支持域名规则和正则表达式规则 - 支持规则例外 - 支持远程规则列表 -- 支持本地规则管理 +- 支持自定义规则管理 ### 3. 查询日志记录和统计 - 实时记录DNS查询日志 @@ -116,7 +116,7 @@ http://localhost:8080 1. 登录Web控制台 2. 点击左侧菜单中的"屏蔽管理" -3. 在"本地规则管理"中添加或删除规则 +3. 在"自定义规则管理"中添加或删除规则 4. 在"远程黑名单管理"中添加或删除远程规则列表 ### 查看查询日志 diff --git a/config.json b/config.json index 328ade1..cb705b5 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "dns": { "port": 53, "upstreamDNS": [ - "10.35.10.200:53" + "223.5.5.5:53" ], "dnssecUpstreamDNS": [ "117.50.10.10:53", @@ -18,10 +18,16 @@ "enableDNSSEC": true, "queryMode": "loadbalance", "domainSpecificDNS": { - "amazehome.cn": [ + "addr.arpa": [ "10.35.10.200:53" ], - "addr.arpa": [ + "akadns": [ + "4.2.2.1:53" + ], + "akamai": [ + "4.2.2.1:53" + ], + "amazehome.cn": [ "10.35.10.200:53" ], "amazehome.xyz": [ @@ -29,14 +35,7 @@ ], "microsoft.com": [ "4.2.2.1:53" - ], - "akamai": [ - "4.2.2.1:53" - ], - "akadns": [ - "4.2.2.1:53" ] - }, "noDNSSECDomains": [ "amazehome.cn", @@ -59,7 +58,7 @@ "name": "AdGuard DNS filter", "url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/filter.txt", "enabled": true, - "lastUpdateTime": "2025-11-28T16:13:03.564Z" + "lastUpdateTime": "2025-12-21T10:46:36.629Z" }, { "name": "Adaway Default Blocklist", @@ -92,7 +91,8 @@ { "name": "Hate \u0026 Junk", "url": "http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/hate-and-junk-extended.txt", - "enabled": true + "enabled": true, + "lastUpdateTime": "2025-12-21T10:46:43.522Z" }, { "name": "My Gitlab Hosts", @@ -152,4 +152,4 @@ "maxBackups": 10, "maxAge": 30 } -} +} \ No newline at end of file diff --git a/dns/server.go b/dns/server.go index ed70e1b..431e64f 100644 --- a/dns/server.go +++ b/dns/server.go @@ -123,7 +123,7 @@ type Server struct { ipGeolocationCacheTTL time.Duration // 缓存有效期 // DNS查询缓存 - dnsCache *DNSCache // DNS响应缓存 + DnsCache *DNSCache // DNS响应缓存 // 域名DNSSEC状态映射表 domainDNSSECStatus map[string]bool // 域名到DNSSEC状态的映射 @@ -199,7 +199,7 @@ func NewServer(config *config.DNSConfig, shieldConfig *config.ShieldConfig, shie ipGeolocationCache: make(map[string]*IPGeolocation), ipGeolocationCacheTTL: 24 * time.Hour, // 缓存有效期24小时 // DNS查询缓存初始化 - dnsCache: NewDNSCache(cacheTTL), + DnsCache: NewDNSCache(cacheTTL), // 初始化域名DNSSEC状态映射表 domainDNSSECStatus: make(map[string]bool), // 初始化服务器状态跟踪 @@ -419,7 +419,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { var cachedDNSSEC bool // 1. 首先检查是否有普通缓存项 - if tempResponse, tempFound := s.dnsCache.Get(r.Question[0].Name, qType); tempFound { + if tempResponse, tempFound := s.DnsCache.Get(r.Question[0].Name, qType); tempFound { cachedResponse = tempResponse found = tempFound cachedDNSSEC = s.hasDNSSECRecords(tempResponse) @@ -562,7 +562,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { responseCopy := response.Copy() // 设置合理的TTL,不超过默认的30分钟 defaultCacheTTL := 30 * time.Minute - s.dnsCache.Set(r.Question[0].Name, qType, responseCopy, defaultCacheTTL) + s.DnsCache.Set(r.Question[0].Name, qType, responseCopy, defaultCacheTTL) logger.Debug("DNS响应已缓存", "domain", domain, "type", queryType, "ttl", defaultCacheTTL, "dnssec", responseDNSSEC) } diff --git a/http/server.go b/http/server.go index 669e0d0..d1f0dc9 100644 --- a/http/server.go +++ b/http/server.go @@ -832,6 +832,9 @@ func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { return } + // 清空DNS缓存,使新规则立即生效 + s.dnsServer.DnsCache.Clear() + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) return case http.MethodDelete: @@ -850,6 +853,9 @@ func (s *Server) handleShield(w http.ResponseWriter, r *http.Request) { return } + // 清空DNS缓存,使规则变更立即生效 + s.dnsServer.DnsCache.Clear() + json.NewEncoder(w).Encode(map[string]string{"status": "success"}) return case http.MethodPut: diff --git a/main.go b/main.go index eb249f9..20bfef7 100644 --- a/main.go +++ b/main.go @@ -121,10 +121,10 @@ func createRequiredFiles(cfg *config.Config) error { } } - // 创建本地规则文件 + // 创建自定义规则文件 if _, err := os.Stat(cfg.Shield.LocalRulesFile); os.IsNotExist(err) { - if err := os.WriteFile(cfg.Shield.LocalRulesFile, []byte("# 本地规则文件\n# 格式:域名\n# 例如:example.com\n"), 0644); err != nil { - return fmt.Errorf("创建本地规则文件失败: %w", err) + if err := os.WriteFile(cfg.Shield.LocalRulesFile, []byte("# 自定义规则文件\n# 格式:域名\n# 例如:example.com\n"), 0644); err != nil { + return fmt.Errorf("创建自定义规则文件失败: %w", err) } } diff --git a/shield/manager.go b/shield/manager.go index 18c0463..44eae44 100644 --- a/shield/manager.go +++ b/shield/manager.go @@ -30,7 +30,7 @@ type ShieldStatsData struct { type regexRule struct { pattern *regexp.Regexp original string - isLocal bool // 是否为本地规则 + isLocal bool // 是否为自定义规则 source string // 规则来源 } @@ -39,8 +39,8 @@ type ShieldManager struct { config *config.ShieldConfig domainRules map[string]bool domainExceptions map[string]bool - domainRulesIsLocal map[string]bool // 标记域名规则是否为本地规则 - domainExceptionsIsLocal map[string]bool // 标记域名排除规则是否为本地规则 + domainRulesIsLocal map[string]bool // 标记域名规则是否为自定义规则 + domainExceptionsIsLocal map[string]bool // 标记域名排除规则是否为自定义规则 domainRulesSource map[string]string // 标记域名规则来源 domainExceptionsSource map[string]string // 标记域名排除规则来源 domainRulesOriginal map[string]string // 存储域名规则的原始字符串 @@ -54,7 +54,7 @@ type ShieldManager struct { updateCtx context.Context updateCancel context.CancelFunc updateRunning bool - localRulesCount int // 本地规则数量 + localRulesCount int // 自定义规则数量 remoteRulesCount int // 远程规则数量 } @@ -109,9 +109,9 @@ func (m *ShieldManager) LoadRules() error { m.remoteRulesCount = 0 // 保留计数数据,不随规则重新加载而清空 - // 加载本地规则文件 + // 加载自定义规则文件 if err := m.loadLocalRules(); err != nil { - logger.Error("加载本地规则失败", "error", err) + logger.Error("加载自定义规则失败", "error", err) // 继续执行,不返回错误 } @@ -132,7 +132,7 @@ func (m *ShieldManager) LoadRules() error { return nil } -// loadLocalRules 加载本地规则文件 +// loadLocalRules 加载自定义规则文件 func (m *ShieldManager) loadLocalRules() error { if m.config.LocalRulesFile == "" { return nil @@ -144,7 +144,7 @@ func (m *ShieldManager) loadLocalRules() error { } defer file.Close() - // 记录加载前的规则数量,用于计算本地规则数量 + // 记录加载前的规则数量,用于计算自定义规则数量 beforeDomainRules := len(m.domainRules) beforeRegexRules := len(m.regexRules) @@ -154,10 +154,10 @@ func (m *ShieldManager) loadLocalRules() error { if line == "" || strings.HasPrefix(line, "#") { continue } - m.parseRule(line, true, "本地规则") // 本地规则,isLocal=true,来源为"本地规则" + m.parseRule(line, true, "自定义规则") // 自定义规则,isLocal=true,来源为"自定义规则" } - // 更新本地规则计数 + // 更新自定义规则计数 m.localRulesCount = (len(m.domainRules) - beforeDomainRules) + (len(m.regexRules) - beforeRegexRules) return scanner.Err() @@ -445,10 +445,16 @@ func (m *ShieldManager) parseRuleOptions(optionsStr string) map[string]string { // addDomainRule 添加域名规则,支持是否为阻止规则 func (m *ShieldManager) addDomainRule(domain string, block bool, isLocal bool, source string, original string) { if block { - // 如果是远程规则,检查是否已经存在本地规则,如果存在则不覆盖 + // 如果是远程规则,检查是否已经存在自定义规则(阻止或排除),如果存在则不覆盖 if !isLocal { + // 检查是否存在自定义阻止规则 if _, exists := m.domainRulesIsLocal[domain]; exists && m.domainRulesIsLocal[domain] { - // 已经存在本地规则,不覆盖 + // 已经存在自定义规则,不覆盖 + return + } + // 检查是否存在自定义排除规则 + if _, exists := m.domainExceptionsIsLocal[domain]; exists && m.domainExceptionsIsLocal[domain] { + // 已经存在自定义规则,不覆盖 return } } @@ -458,10 +464,16 @@ func (m *ShieldManager) addDomainRule(domain string, block bool, isLocal bool, s m.domainRulesOriginal[domain] = original } else { // 添加到排除规则 - // 如果是远程规则,检查是否已经存在本地规则,如果存在则不覆盖 + // 如果是远程规则,检查是否已经存在自定义规则(阻止或排除),如果存在则不覆盖 if !isLocal { + // 检查是否存在自定义阻止规则 + if _, exists := m.domainRulesIsLocal[domain]; exists && m.domainRulesIsLocal[domain] { + // 已经存在自定义规则,不覆盖 + return + } + // 检查是否存在自定义排除规则 if _, exists := m.domainExceptionsIsLocal[domain]; exists && m.domainExceptionsIsLocal[domain] { - // 已经存在本地规则,不覆盖 + // 已经存在自定义规则,不覆盖 return } } @@ -481,11 +493,19 @@ func (m *ShieldManager) addRegexRule(re *regexp.Regexp, original string, block b source: source, } if block { - // 如果是远程规则,检查是否已经存在相同的本地规则,如果存在则不添加 + // 如果是远程规则,检查是否已经存在任何自定义规则,如果存在则不添加 if !isLocal { + // 检查是否存在自定义阻止规则 for _, existingRule := range m.regexRules { - if existingRule.original == original && existingRule.isLocal { - // 已经存在相同的本地规则,不添加 + if existingRule.pattern.String() == re.String() && existingRule.isLocal { + // 已经存在相同的自定义阻止规则,不添加 + return + } + } + // 检查是否存在自定义排除规则 + for _, existingRule := range m.regexExceptions { + if existingRule.pattern.String() == re.String() && existingRule.isLocal { + // 已经存在相同的自定义排除规则,不添加 return } } @@ -493,11 +513,19 @@ func (m *ShieldManager) addRegexRule(re *regexp.Regexp, original string, block b m.regexRules = append(m.regexRules, rule) } else { // 添加到排除规则 - // 如果是远程规则,检查是否已经存在相同的本地规则,如果存在则不添加 + // 如果是远程规则,检查是否已经存在任何自定义规则,如果存在则不添加 if !isLocal { + // 检查是否存在自定义阻止规则 + for _, existingRule := range m.regexRules { + if existingRule.pattern.String() == re.String() && existingRule.isLocal { + // 已经存在相同的自定义阻止规则,不添加 + return + } + } + // 检查是否存在自定义排除规则 for _, existingRule := range m.regexExceptions { - if existingRule.original == original && existingRule.isLocal { - // 已经存在相同的本地规则,不添加 + if existingRule.pattern.String() == re.String() && existingRule.isLocal { + // 已经存在相同的自定义排除规则,不添加 return } } @@ -537,8 +565,17 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf result["hostsIP"] = hostsIP // 检查排除规则(优先级最高) - // 检查域名排除规则 - if m.domainExceptions[domain] { + // 1. 先检查本地域名排除规则 + if m.domainExceptions[domain] && m.domainExceptionsIsLocal[domain] { + result["excluded"] = true + result["excludeRule"] = m.domainExceptionsOriginal[domain] + result["excludeRuleType"] = "exact_domain" + result["blocksource"] = m.domainExceptionsSource[domain] + return result + } + + // 2. 再检查远程域名排除规则 + if m.domainExceptions[domain] && !m.domainExceptionsIsLocal[domain] { result["excluded"] = true result["excludeRule"] = m.domainExceptionsOriginal[domain] result["excludeRuleType"] = "exact_domain" @@ -548,9 +585,11 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf // 检查子域名排除规则 parts := strings.Split(domain, ".") + + // 3. 先检查本地子域名排除规则 for i := 0; i < len(parts)-1; i++ { subdomain := strings.Join(parts[i:], ".") - if m.domainExceptions[subdomain] { + if m.domainExceptions[subdomain] && m.domainExceptionsIsLocal[subdomain] { result["excluded"] = true result["excludeRule"] = m.domainExceptionsOriginal[subdomain] result["excludeRuleType"] = "subdomain" @@ -559,9 +598,32 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf } } - // 检查正则表达式排除规则 + // 4. 再检查远程子域名排除规则 + for i := 0; i < len(parts)-1; i++ { + subdomain := strings.Join(parts[i:], ".") + if m.domainExceptions[subdomain] && !m.domainExceptionsIsLocal[subdomain] { + result["excluded"] = true + result["excludeRule"] = m.domainExceptionsOriginal[subdomain] + result["excludeRuleType"] = "subdomain" + result["blocksource"] = m.domainExceptionsSource[subdomain] + return result + } + } + + // 5. 先检查本地正则表达式排除规则 for _, re := range m.regexExceptions { - if re.pattern.MatchString(domain) { + if re.isLocal && re.pattern.MatchString(domain) { + result["excluded"] = true + result["excludeRule"] = re.original + result["excludeRuleType"] = "regex" + result["blocksource"] = re.source + return result + } + } + + // 6. 再检查远程正则表达式排除规则 + for _, re := range m.regexExceptions { + if !re.isLocal && re.pattern.MatchString(domain) { result["excluded"] = true result["excludeRule"] = re.original result["excludeRuleType"] = "regex" @@ -571,8 +633,17 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf } // 检查阻止规则 - 先检查精确域名匹配,再检查子域名匹配 - // 检查精确域名匹配 - if m.domainRules[domain] { + // 7. 先检查本地域名阻止规则 + if m.domainRules[domain] && m.domainRulesIsLocal[domain] { + result["blocked"] = true + result["blockRule"] = m.domainRulesOriginal[domain] + result["blockRuleType"] = "exact_domain" + result["blocksource"] = m.domainRulesSource[domain] + return result + } + + // 8. 再检查远程域名阻止规则 + if m.domainRules[domain] && !m.domainRulesIsLocal[domain] { result["blocked"] = true result["blockRule"] = m.domainRulesOriginal[domain] result["blockRuleType"] = "exact_domain" @@ -582,9 +653,11 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf // 检查子域名匹配(AdGuardHome风格) // 从最长的子域名开始匹配,确保优先级正确 + + // 9. 先检查本地子域名阻止规则 for i := 0; i < len(parts)-1; i++ { subdomain := strings.Join(parts[i:], ".") - if m.domainRules[subdomain] { + if m.domainRules[subdomain] && m.domainRulesIsLocal[subdomain] { result["blocked"] = true result["blockRule"] = m.domainRulesOriginal[subdomain] result["blockRuleType"] = "subdomain" @@ -593,9 +666,32 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf } } - // 检查正则表达式匹配 + // 10. 再检查远程子域名阻止规则 + for i := 0; i < len(parts)-1; i++ { + subdomain := strings.Join(parts[i:], ".") + if m.domainRules[subdomain] && !m.domainRulesIsLocal[subdomain] { + result["blocked"] = true + result["blockRule"] = m.domainRulesOriginal[subdomain] + result["blockRuleType"] = "subdomain" + result["blocksource"] = m.domainRulesSource[subdomain] + return result + } + } + + // 11. 先检查本地正则表达式阻止规则 for _, re := range m.regexRules { - if re.pattern.MatchString(domain) { + if re.isLocal && re.pattern.MatchString(domain) { + result["blocked"] = true + result["blockRule"] = re.original + result["blockRuleType"] = "regex" + result["blocksource"] = re.source + return result + } + } + + // 12. 再检查远程正则表达式阻止规则 + for _, re := range m.regexRules { + if !re.isLocal && re.pattern.MatchString(domain) { result["blocked"] = true result["blockRule"] = re.original result["blockRuleType"] = "regex" @@ -722,13 +818,13 @@ func (m *ShieldManager) GetHostsIP(domain string) (string, bool) { return ip, exists } -// AddRule 添加屏蔽规则,用户添加的规则是本地规则 +// AddRule 添加屏蔽规则,用户添加的规则是自定义规则 func (m *ShieldManager) AddRule(rule string) error { m.rulesMutex.Lock() defer m.rulesMutex.Unlock() - // 解析并添加规则到内存,isLocal=true表示本地规则,来源为"本地规则" - m.parseRule(rule, true, "本地规则") + // 解析并添加规则到内存,isLocal=true表示自定义规则,来源为"自定义规则" + m.parseRule(rule, true, "自定义规则") // 持久化保存规则到文件 if m.config.LocalRulesFile != "" { @@ -957,7 +1053,7 @@ func (m *ShieldManager) StopAutoUpdate() { logger.Info("规则自动更新已停止") } -// saveRulesToFile 保存规则到文件,只保存本地规则 +// saveRulesToFile 保存规则到文件,只保存自定义规则 func (m *ShieldManager) saveRulesToFile() error { var rules []string @@ -1293,12 +1389,12 @@ func (m *ShieldManager) GetHostsCount() int { return len(m.hostsMap) } -// GetLocalRules 获取仅本地规则 +// GetLocalRules 获取仅自定义规则 func (m *ShieldManager) GetLocalRules() map[string]interface{} { m.rulesMutex.RLock() defer m.rulesMutex.RUnlock() - // 转换map和slice为字符串列表,只包含本地规则 + // 转换map和slice为字符串列表,只包含自定义规则 domainRulesList := make([]string, 0) for domain, isLocal := range m.domainRulesIsLocal { if isLocal && m.domainRules[domain] { @@ -1329,7 +1425,7 @@ func (m *ShieldManager) GetLocalRules() map[string]interface{} { } } - // 计算本地规则数量 + // 计算自定义规则数量 localDomainRulesCount := 0 for _, isLocal := range m.domainRulesIsLocal { if isLocal { diff --git a/static/api/js/index.js b/static/api/js/index.js index ea876cc..94fa52e 100644 --- a/static/api/js/index.js +++ b/static/api/js/index.js @@ -1138,12 +1138,12 @@ }, "/shield/localrules": { "get": { - "summary": "获取本地规则", - "description": "获取Shield的本地规则列表。", + "summary": "获取自定义规则", + "description": "获取Shield的自定义规则列表。" "tags": ["shield"], "responses": { "200": { - "description": "成功获取本地规则", + "description": "成功获取自定义规则", "content": { "application/json": { "schema": { @@ -1166,8 +1166,8 @@ } }, "post": { - "summary": "添加本地规则", - "description": "添加新的本地规则。", + "summary": "添加自定义规则", + "description": "添加新的自定义规则。", "tags": ["shield"], "requestBody": { "required": true, @@ -1205,8 +1205,8 @@ } }, "delete": { - "summary": "删除本地规则", - "description": "删除指定ID的本地规则。", + "summary": "删除自定义规则", + "description": "删除指定ID的自定义规则。", "tags": ["shield"], "parameters": [ { @@ -1500,7 +1500,7 @@ "blocked": true, "blockRule": "example.com", "blockRuleType": "exact_domain", - "blocksource": "本地规则", + "blocksource": "自定义规则", "excluded": false, "excludeRule": "", "excludeRuleType": "", diff --git a/static/index.html b/static/index.html index 363371e..f36953f 100644 --- a/static/index.html +++ b/static/index.html @@ -630,9 +630,9 @@ - +