实现日志功能
This commit is contained in:
@@ -19,12 +19,6 @@ import (
|
||||
"dns-server/logger"
|
||||
)
|
||||
|
||||
// regexRule 正则规则结构,包含编译后的表达式和原始字符串
|
||||
type regexRule struct {
|
||||
pattern *regexp.Regexp
|
||||
original string
|
||||
}
|
||||
|
||||
// ShieldStatsData 用于持久化的Shield统计数据
|
||||
type ShieldStatsData struct {
|
||||
BlockedDomainsCount map[string]int `json:"blockedDomainsCount"`
|
||||
@@ -32,45 +26,65 @@ type ShieldStatsData struct {
|
||||
LastSaved time.Time `json:"lastSaved"`
|
||||
}
|
||||
|
||||
// regexRule 正则规则结构,包含编译后的表达式和原始字符串
|
||||
type regexRule struct {
|
||||
pattern *regexp.Regexp
|
||||
original string
|
||||
isLocal bool // 是否为本地规则
|
||||
source string // 规则来源
|
||||
}
|
||||
|
||||
// ShieldManager 屏蔽管理器
|
||||
type ShieldManager struct {
|
||||
config *config.ShieldConfig
|
||||
domainRules map[string]bool
|
||||
domainExceptions map[string]bool
|
||||
regexRules []regexRule
|
||||
regexExceptions []regexRule
|
||||
hostsMap map[string]string
|
||||
blockedDomainsCount map[string]int
|
||||
resolvedDomainsCount map[string]int
|
||||
rulesMutex sync.RWMutex
|
||||
updateCtx context.Context
|
||||
updateCancel context.CancelFunc
|
||||
updateRunning bool
|
||||
localRulesCount int // 本地规则数量
|
||||
remoteRulesCount int // 远程规则数量
|
||||
config *config.ShieldConfig
|
||||
domainRules map[string]bool
|
||||
domainExceptions map[string]bool
|
||||
domainRulesIsLocal map[string]bool // 标记域名规则是否为本地规则
|
||||
domainExceptionsIsLocal map[string]bool // 标记域名排除规则是否为本地规则
|
||||
domainRulesSource map[string]string // 标记域名规则来源
|
||||
domainExceptionsSource map[string]string // 标记域名排除规则来源
|
||||
domainRulesOriginal map[string]string // 存储域名规则的原始字符串
|
||||
domainExceptionsOriginal map[string]string // 存储域名排除规则的原始字符串
|
||||
regexRules []regexRule
|
||||
regexExceptions []regexRule
|
||||
hostsMap map[string]string
|
||||
blockedDomainsCount map[string]int
|
||||
resolvedDomainsCount map[string]int
|
||||
rulesMutex sync.RWMutex
|
||||
updateCtx context.Context
|
||||
updateCancel context.CancelFunc
|
||||
updateRunning bool
|
||||
localRulesCount int // 本地规则数量
|
||||
remoteRulesCount int // 远程规则数量
|
||||
}
|
||||
|
||||
// NewShieldManager 创建屏蔽管理器实例
|
||||
func NewShieldManager(config *config.ShieldConfig) *ShieldManager {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
manager := &ShieldManager{
|
||||
config: config,
|
||||
domainRules: make(map[string]bool),
|
||||
domainExceptions: make(map[string]bool),
|
||||
regexRules: []regexRule{},
|
||||
regexExceptions: []regexRule{},
|
||||
hostsMap: make(map[string]string),
|
||||
blockedDomainsCount: make(map[string]int),
|
||||
resolvedDomainsCount: make(map[string]int),
|
||||
updateCtx: ctx,
|
||||
updateCancel: cancel,
|
||||
localRulesCount: 0,
|
||||
remoteRulesCount: 0,
|
||||
config: config,
|
||||
domainRules: make(map[string]bool),
|
||||
domainExceptions: make(map[string]bool),
|
||||
domainRulesIsLocal: make(map[string]bool),
|
||||
domainExceptionsIsLocal: make(map[string]bool),
|
||||
domainRulesSource: make(map[string]string),
|
||||
domainExceptionsSource: make(map[string]string),
|
||||
domainRulesOriginal: make(map[string]string),
|
||||
domainExceptionsOriginal: make(map[string]string),
|
||||
regexRules: []regexRule{},
|
||||
regexExceptions: []regexRule{},
|
||||
hostsMap: make(map[string]string),
|
||||
blockedDomainsCount: make(map[string]int),
|
||||
resolvedDomainsCount: make(map[string]int),
|
||||
updateCtx: ctx,
|
||||
updateCancel: cancel,
|
||||
localRulesCount: 0,
|
||||
remoteRulesCount: 0,
|
||||
}
|
||||
|
||||
|
||||
// 加载已保存的计数数据
|
||||
manager.loadStatsData()
|
||||
|
||||
|
||||
return manager
|
||||
}
|
||||
|
||||
@@ -82,6 +96,12 @@ func (m *ShieldManager) LoadRules() error {
|
||||
// 清空现有规则
|
||||
m.domainRules = make(map[string]bool)
|
||||
m.domainExceptions = make(map[string]bool)
|
||||
m.domainRulesIsLocal = make(map[string]bool)
|
||||
m.domainExceptionsIsLocal = make(map[string]bool)
|
||||
m.domainRulesSource = make(map[string]string)
|
||||
m.domainExceptionsSource = make(map[string]string)
|
||||
m.domainRulesOriginal = make(map[string]string)
|
||||
m.domainExceptionsOriginal = make(map[string]string)
|
||||
m.regexRules = []regexRule{}
|
||||
m.regexExceptions = []regexRule{}
|
||||
m.hostsMap = make(map[string]string)
|
||||
@@ -134,7 +154,7 @@ func (m *ShieldManager) loadLocalRules() error {
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
m.parseRule(line)
|
||||
m.parseRule(line, true, "本地规则") // 本地规则,isLocal=true,来源为"本地规则"
|
||||
}
|
||||
|
||||
// 更新本地规则计数
|
||||
@@ -187,11 +207,11 @@ func (m *ShieldManager) shouldUpdateCache(cacheFile string) bool {
|
||||
func (m *ShieldManager) fetchRemoteRules(url string) error {
|
||||
// 获取缓存文件路径
|
||||
cacheFile := m.getCacheFilePath(url)
|
||||
|
||||
|
||||
// 尝试从缓存加载
|
||||
hasLoadedFromCache := false
|
||||
if !m.shouldUpdateCache(cacheFile) {
|
||||
if err := m.loadCachedRules(cacheFile); err == nil {
|
||||
if err := m.loadCachedRules(cacheFile, url); err == nil {
|
||||
logger.Info("从缓存加载远程规则", "url", url)
|
||||
hasLoadedFromCache = true
|
||||
}
|
||||
@@ -236,14 +256,14 @@ func (m *ShieldManager) fetchRemoteRules(url string) error {
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
m.parseRule(line)
|
||||
m.parseRule(line, false, url) // 远程规则,isLocal=false,来源为URL
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadCachedRules 从缓存文件加载规则
|
||||
func (m *ShieldManager) loadCachedRules(filePath string) error {
|
||||
func (m *ShieldManager) loadCachedRules(filePath string, source string) error {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -265,7 +285,7 @@ func (m *ShieldManager) loadCachedRules(filePath string) error {
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
m.parseRule(line)
|
||||
m.parseRule(line, false, source) // 远程规则,isLocal=false,来源为URL
|
||||
}
|
||||
|
||||
// 更新远程规则计数
|
||||
@@ -318,7 +338,10 @@ func (m *ShieldManager) loadHosts() error {
|
||||
}
|
||||
|
||||
// parseRule 解析规则行
|
||||
func (m *ShieldManager) parseRule(line string) {
|
||||
func (m *ShieldManager) parseRule(line string, isLocal bool, source string) {
|
||||
// 保存原始规则用于后续使用
|
||||
originalLine := line
|
||||
|
||||
// 处理注释
|
||||
if strings.HasPrefix(line, "!") || strings.HasPrefix(line, "#") || line == "" {
|
||||
return
|
||||
@@ -343,12 +366,12 @@ func (m *ShieldManager) parseRule(line string) {
|
||||
case strings.HasPrefix(line, "||") && strings.HasSuffix(line, "^"):
|
||||
// AdGuardHome域名规则格式: ||example.com^
|
||||
domain := strings.TrimSuffix(strings.TrimPrefix(line, "||"), "^")
|
||||
m.addDomainRule(domain, !isException)
|
||||
m.addDomainRule(domain, !isException, isLocal, source, originalLine)
|
||||
|
||||
case strings.HasPrefix(line, "||"):
|
||||
// 精确域名匹配规则
|
||||
domain := strings.TrimPrefix(line, "||")
|
||||
m.addDomainRule(domain, !isException)
|
||||
m.addDomainRule(domain, !isException, isLocal, source, originalLine)
|
||||
|
||||
case strings.HasPrefix(line, "*"):
|
||||
// 通配符规则,转换为正则表达式
|
||||
@@ -356,15 +379,17 @@ func (m *ShieldManager) parseRule(line string) {
|
||||
pattern = "^" + pattern + "$"
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
// 保存原始规则字符串
|
||||
m.addRegexRule(re, line, !isException)
|
||||
m.addRegexRule(re, originalLine, !isException, isLocal, source)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(line, "/") && strings.HasSuffix(line, "/"):
|
||||
// 正则表达式规则
|
||||
// 正则表达式匹配规则:/regex/ 格式,不区分大小写
|
||||
pattern := strings.TrimPrefix(strings.TrimSuffix(line, "/"), "/")
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
// 编译为不区分大小写的正则表达式,确保能匹配域名中任意位置
|
||||
// 对于像 /domain/ 这样的规则,应该匹配包含 domain 字符串的任何域名
|
||||
if re, err := regexp.Compile("(?i).*" + regexp.QuoteMeta(pattern) + ".*"); err == nil {
|
||||
// 保存原始规则字符串
|
||||
m.addRegexRule(re, line, !isException)
|
||||
m.addRegexRule(re, originalLine, !isException, isLocal, source)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(line, "|") && strings.HasSuffix(line, "|"):
|
||||
@@ -373,7 +398,7 @@ func (m *ShieldManager) parseRule(line string) {
|
||||
// 将URL模式转换为正则表达式
|
||||
pattern := "^" + regexp.QuoteMeta(urlPattern) + "$"
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
m.addRegexRule(re, line, !isException)
|
||||
m.addRegexRule(re, originalLine, !isException, isLocal, source)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(line, "|"):
|
||||
@@ -381,7 +406,7 @@ func (m *ShieldManager) parseRule(line string) {
|
||||
urlPattern := strings.TrimPrefix(line, "|")
|
||||
pattern := "^" + regexp.QuoteMeta(urlPattern)
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
m.addRegexRule(re, line, !isException)
|
||||
m.addRegexRule(re, originalLine, !isException, isLocal, source)
|
||||
}
|
||||
|
||||
case strings.HasSuffix(line, "|"):
|
||||
@@ -389,12 +414,12 @@ func (m *ShieldManager) parseRule(line string) {
|
||||
urlPattern := strings.TrimSuffix(line, "|")
|
||||
pattern := regexp.QuoteMeta(urlPattern) + "$"
|
||||
if re, err := regexp.Compile(pattern); err == nil {
|
||||
m.addRegexRule(re, line, !isException)
|
||||
m.addRegexRule(re, originalLine, !isException, isLocal, source)
|
||||
}
|
||||
|
||||
default:
|
||||
// 默认作为普通域名规则
|
||||
m.addDomainRule(line, !isException)
|
||||
m.addDomainRule(line, !isException, isLocal, source, originalLine)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,42 +443,65 @@ func (m *ShieldManager) parseRuleOptions(optionsStr string) map[string]string {
|
||||
}
|
||||
|
||||
// addDomainRule 添加域名规则,支持是否为阻止规则
|
||||
func (m *ShieldManager) addDomainRule(domain string, block bool) {
|
||||
func (m *ShieldManager) addDomainRule(domain string, block bool, isLocal bool, source string, original string) {
|
||||
if block {
|
||||
m.domainRules[domain] = true
|
||||
// 添加所有子域名的匹配支持
|
||||
parts := strings.Split(domain, ".")
|
||||
if len(parts) > 1 {
|
||||
// 为二级域名和顶级域名添加规则
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
subdomain := strings.Join(parts[i:], ".")
|
||||
m.domainRules[subdomain] = true
|
||||
// 如果是远程规则,检查是否已经存在本地规则,如果存在则不覆盖
|
||||
if !isLocal {
|
||||
if _, exists := m.domainRulesIsLocal[domain]; exists && m.domainRulesIsLocal[domain] {
|
||||
// 已经存在本地规则,不覆盖
|
||||
return
|
||||
}
|
||||
}
|
||||
m.domainRules[domain] = true
|
||||
m.domainRulesIsLocal[domain] = isLocal
|
||||
m.domainRulesSource[domain] = source
|
||||
m.domainRulesOriginal[domain] = original
|
||||
} else {
|
||||
// 添加到排除规则
|
||||
m.domainExceptions[domain] = true
|
||||
// 为子域名也添加排除规则
|
||||
parts := strings.Split(domain, ".")
|
||||
if len(parts) > 1 {
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
subdomain := strings.Join(parts[i:], ".")
|
||||
m.domainExceptions[subdomain] = true
|
||||
// 如果是远程规则,检查是否已经存在本地规则,如果存在则不覆盖
|
||||
if !isLocal {
|
||||
if _, exists := m.domainExceptionsIsLocal[domain]; exists && m.domainExceptionsIsLocal[domain] {
|
||||
// 已经存在本地规则,不覆盖
|
||||
return
|
||||
}
|
||||
}
|
||||
m.domainExceptions[domain] = true
|
||||
m.domainExceptionsIsLocal[domain] = isLocal
|
||||
m.domainExceptionsSource[domain] = source
|
||||
m.domainExceptionsOriginal[domain] = original
|
||||
}
|
||||
}
|
||||
|
||||
// addRegexRule 添加正则表达式规则,支持是否为阻止规则
|
||||
func (m *ShieldManager) addRegexRule(re *regexp.Regexp, original string, block bool) {
|
||||
func (m *ShieldManager) addRegexRule(re *regexp.Regexp, original string, block bool, isLocal bool, source string) {
|
||||
rule := regexRule{
|
||||
pattern: re,
|
||||
original: original,
|
||||
isLocal: isLocal,
|
||||
source: source,
|
||||
}
|
||||
if block {
|
||||
// 如果是远程规则,检查是否已经存在相同的本地规则,如果存在则不添加
|
||||
if !isLocal {
|
||||
for _, existingRule := range m.regexRules {
|
||||
if existingRule.original == original && existingRule.isLocal {
|
||||
// 已经存在相同的本地规则,不添加
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
m.regexRules = append(m.regexRules, rule)
|
||||
} else {
|
||||
// 添加到排除规则
|
||||
// 如果是远程规则,检查是否已经存在相同的本地规则,如果存在则不添加
|
||||
if !isLocal {
|
||||
for _, existingRule := range m.regexExceptions {
|
||||
if existingRule.original == original && existingRule.isLocal {
|
||||
// 已经存在相同的本地规则,不添加
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
m.regexExceptions = append(m.regexExceptions, rule)
|
||||
}
|
||||
}
|
||||
@@ -471,15 +519,16 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"domain": domain,
|
||||
"blocked": false,
|
||||
"blockRule": "",
|
||||
"blockRuleType": "",
|
||||
"excluded": false,
|
||||
"excludeRule": "",
|
||||
"domain": domain,
|
||||
"blocked": false,
|
||||
"blockRule": "",
|
||||
"blockRuleType": "",
|
||||
"blocksource": "",
|
||||
"excluded": false,
|
||||
"excludeRule": "",
|
||||
"excludeRuleType": "",
|
||||
"hasHosts": false,
|
||||
"hostsIP": "",
|
||||
"hasHosts": false,
|
||||
"hostsIP": "",
|
||||
}
|
||||
|
||||
// 检查hosts记录
|
||||
@@ -491,8 +540,9 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
// 检查域名排除规则
|
||||
if m.domainExceptions[domain] {
|
||||
result["excluded"] = true
|
||||
result["excludeRule"] = domain
|
||||
result["excludeRule"] = m.domainExceptionsOriginal[domain]
|
||||
result["excludeRuleType"] = "exact_domain"
|
||||
result["blocksource"] = m.domainExceptionsSource[domain]
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -502,8 +552,9 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
subdomain := strings.Join(parts[i:], ".")
|
||||
if m.domainExceptions[subdomain] {
|
||||
result["excluded"] = true
|
||||
result["excludeRule"] = subdomain
|
||||
result["excludeRule"] = m.domainExceptionsOriginal[subdomain]
|
||||
result["excludeRuleType"] = "subdomain"
|
||||
result["blocksource"] = m.domainExceptionsSource[subdomain]
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -514,16 +565,18 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
result["excluded"] = true
|
||||
result["excludeRule"] = re.original
|
||||
result["excludeRuleType"] = "regex"
|
||||
result["blocksource"] = re.source
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// 检查阻止规则
|
||||
// 检查阻止规则 - 先检查精确域名匹配,再检查子域名匹配
|
||||
// 检查精确域名匹配
|
||||
if m.domainRules[domain] {
|
||||
result["blocked"] = true
|
||||
result["blockRule"] = domain
|
||||
result["blockRule"] = m.domainRulesOriginal[domain]
|
||||
result["blockRuleType"] = "exact_domain"
|
||||
result["blocksource"] = m.domainRulesSource[domain]
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -533,8 +586,9 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
subdomain := strings.Join(parts[i:], ".")
|
||||
if m.domainRules[subdomain] {
|
||||
result["blocked"] = true
|
||||
result["blockRule"] = subdomain
|
||||
result["blockRule"] = m.domainRulesOriginal[subdomain]
|
||||
result["blockRuleType"] = "subdomain"
|
||||
result["blocksource"] = m.domainRulesSource[subdomain]
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -545,6 +599,7 @@ func (m *ShieldManager) CheckDomainBlockDetails(domain string) map[string]interf
|
||||
result["blocked"] = true
|
||||
result["blockRule"] = re.original
|
||||
result["blockRuleType"] = "regex"
|
||||
result["blocksource"] = re.source
|
||||
return result
|
||||
}
|
||||
}
|
||||
@@ -667,13 +722,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()
|
||||
|
||||
// 解析并添加规则到内存
|
||||
m.parseRule(rule)
|
||||
// 解析并添加规则到内存,isLocal=true表示本地规则,来源为"本地规则"
|
||||
m.parseRule(rule, true, "本地规则")
|
||||
|
||||
// 持久化保存规则到文件
|
||||
if m.config.LocalRulesFile != "" {
|
||||
@@ -724,6 +779,8 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
domain := strings.TrimPrefix(format, "@@||")
|
||||
if _, exists := m.domainExceptions[domain]; exists {
|
||||
delete(m.domainExceptions, domain)
|
||||
delete(m.domainExceptionsIsLocal, domain)
|
||||
delete(m.domainExceptionsSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -731,19 +788,28 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
// 尝试删除域名规则
|
||||
domain := strings.TrimPrefix(format, "||")
|
||||
if _, exists := m.domainRules[domain]; exists {
|
||||
// 删除主域名规则
|
||||
delete(m.domainRules, domain)
|
||||
delete(m.domainRulesIsLocal, domain)
|
||||
delete(m.domainRulesSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// 尝试直接作为域名删除
|
||||
if _, exists := m.domainRules[format]; exists {
|
||||
// 删除主域名规则
|
||||
delete(m.domainRules, format)
|
||||
delete(m.domainRulesIsLocal, format)
|
||||
delete(m.domainRulesSource, format)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
if _, exists := m.domainExceptions[format]; exists {
|
||||
// 删除主排除规则
|
||||
delete(m.domainExceptions, format)
|
||||
delete(m.domainExceptionsIsLocal, format)
|
||||
delete(m.domainExceptionsSource, format)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -752,12 +818,10 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
|
||||
// 处理正则表达式规则
|
||||
if !removed && strings.HasPrefix(cleanRule, "/") && strings.HasSuffix(cleanRule, "/") {
|
||||
pattern := strings.TrimPrefix(strings.TrimSuffix(cleanRule, "/"), "/")
|
||||
|
||||
// 检查是否在正则表达式规则中
|
||||
newRegexRules := []regexRule{}
|
||||
for _, re := range m.regexRules {
|
||||
if re.pattern.String() != pattern {
|
||||
if re.original != rule && re.original != cleanRule {
|
||||
newRegexRules = append(newRegexRules, re)
|
||||
} else {
|
||||
removed = true
|
||||
@@ -769,7 +833,7 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
if !removed {
|
||||
newRegexExceptions := []regexRule{}
|
||||
for _, re := range m.regexExceptions {
|
||||
if re.pattern.String() != pattern {
|
||||
if re.original != rule && re.original != cleanRule {
|
||||
newRegexExceptions = append(newRegexExceptions, re)
|
||||
} else {
|
||||
removed = true
|
||||
@@ -785,6 +849,8 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
for domain := range m.domainRules {
|
||||
if domain == cleanRule || domain == rule {
|
||||
delete(m.domainRules, domain)
|
||||
delete(m.domainRulesIsLocal, domain)
|
||||
delete(m.domainRulesSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -794,6 +860,8 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
for domain := range m.domainExceptions {
|
||||
if domain == cleanRule || domain == rule {
|
||||
delete(m.domainExceptions, domain)
|
||||
delete(m.domainExceptionsIsLocal, domain)
|
||||
delete(m.domainExceptionsSource, domain)
|
||||
removed = true
|
||||
break
|
||||
}
|
||||
@@ -801,6 +869,36 @@ func (m *ShieldManager) RemoveRule(rule string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有删除任何规则,尝试删除可能的子域名规则
|
||||
if !removed {
|
||||
// 解析原始规则,提取可能的主域名
|
||||
originalRule := cleanRule
|
||||
// 移除可能的前缀
|
||||
originalRule = strings.TrimPrefix(originalRule, "@@||")
|
||||
originalRule = strings.TrimPrefix(originalRule, "||")
|
||||
|
||||
// 检查是否有子域名规则需要删除
|
||||
// 遍历所有域名规则,删除包含原始规则作为后缀的子域名规则
|
||||
for domain := range m.domainRules {
|
||||
if strings.HasSuffix(domain, "."+originalRule) || domain == originalRule {
|
||||
delete(m.domainRules, domain)
|
||||
delete(m.domainRulesIsLocal, domain)
|
||||
delete(m.domainRulesSource, domain)
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历所有排除规则,删除包含原始规则作为后缀的子域名规则
|
||||
for domain := range m.domainExceptions {
|
||||
if strings.HasSuffix(domain, "."+originalRule) || domain == originalRule {
|
||||
delete(m.domainExceptions, domain)
|
||||
delete(m.domainExceptionsIsLocal, domain)
|
||||
delete(m.domainExceptionsSource, domain)
|
||||
removed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有规则被删除,持久化保存更改
|
||||
if removed && m.config.LocalRulesFile != "" {
|
||||
if err := m.saveRulesToFile(); err != nil {
|
||||
@@ -843,7 +941,7 @@ func (m *ShieldManager) StartAutoUpdate() {
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
logger.Info("规则自动更新已启动", "interval", m.config.UpdateInterval)
|
||||
|
||||
// 如果是首次启动,先保存一次数据确保目录存在
|
||||
@@ -859,28 +957,36 @@ func (m *ShieldManager) StopAutoUpdate() {
|
||||
logger.Info("规则自动更新已停止")
|
||||
}
|
||||
|
||||
// saveRulesToFile 保存规则到文件
|
||||
// saveRulesToFile 保存规则到文件,只保存本地规则
|
||||
func (m *ShieldManager) saveRulesToFile() error {
|
||||
var rules []string
|
||||
|
||||
// 添加域名规则
|
||||
for domain := range m.domainRules {
|
||||
rules = append(rules, "||"+domain)
|
||||
// 添加本地域名规则
|
||||
for domain, isLocal := range m.domainRulesIsLocal {
|
||||
if isLocal {
|
||||
rules = append(rules, "||"+domain)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加正则表达式规则
|
||||
// 添加本地正则表达式规则
|
||||
for _, re := range m.regexRules {
|
||||
rules = append(rules, re.original)
|
||||
if re.isLocal {
|
||||
rules = append(rules, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加排除规则
|
||||
for domain := range m.domainExceptions {
|
||||
rules = append(rules, "@@||"+domain)
|
||||
// 添加本地排除规则
|
||||
for domain, isLocal := range m.domainExceptionsIsLocal {
|
||||
if isLocal {
|
||||
rules = append(rules, "@@||"+domain)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加正则表达式排除规则
|
||||
// 添加本地正则表达式排除规则
|
||||
for _, re := range m.regexExceptions {
|
||||
rules = append(rules, re.original)
|
||||
if re.isLocal {
|
||||
rules = append(rules, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
@@ -1033,18 +1139,18 @@ func (m *ShieldManager) loadStatsData() {
|
||||
if len(dataSample) > 50 {
|
||||
dataSample = dataSample[:50] + "..."
|
||||
}
|
||||
logger.Error("解析Shield计数数据失败",
|
||||
"file", statsFilePath,
|
||||
"error", err,
|
||||
logger.Error("解析Shield计数数据失败",
|
||||
"file", statsFilePath,
|
||||
"error", err,
|
||||
"data_length", len(data),
|
||||
"data_sample", dataSample)
|
||||
|
||||
|
||||
// 重置为默认空数据
|
||||
m.rulesMutex.Lock()
|
||||
m.blockedDomainsCount = make(map[string]int)
|
||||
m.resolvedDomainsCount = make(map[string]int)
|
||||
m.rulesMutex.Unlock()
|
||||
|
||||
|
||||
// 尝试保存一个有效的空文件
|
||||
m.saveStatsData()
|
||||
return
|
||||
@@ -1187,6 +1293,131 @@ func (m *ShieldManager) GetHostsCount() int {
|
||||
return len(m.hostsMap)
|
||||
}
|
||||
|
||||
// GetLocalRules 获取仅本地规则
|
||||
func (m *ShieldManager) GetLocalRules() map[string]interface{} {
|
||||
m.rulesMutex.RLock()
|
||||
defer m.rulesMutex.RUnlock()
|
||||
|
||||
// 转换map和slice为字符串列表,只包含本地规则
|
||||
domainRulesList := make([]string, 0)
|
||||
for domain, isLocal := range m.domainRulesIsLocal {
|
||||
if isLocal && m.domainRules[domain] {
|
||||
domainRulesList = append(domainRulesList, "||"+domain+"^")
|
||||
}
|
||||
}
|
||||
|
||||
domainExceptionsList := make([]string, 0)
|
||||
for domain, isLocal := range m.domainExceptionsIsLocal {
|
||||
if isLocal && m.domainExceptions[domain] {
|
||||
domainExceptionsList = append(domainExceptionsList, "@@||"+domain+"^")
|
||||
}
|
||||
}
|
||||
|
||||
// 获取本地正则规则原始字符串
|
||||
regexRulesList := make([]string, 0)
|
||||
for _, re := range m.regexRules {
|
||||
if re.isLocal {
|
||||
regexRulesList = append(regexRulesList, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取本地正则排除规则原始字符串
|
||||
regexExceptionsList := make([]string, 0)
|
||||
for _, re := range m.regexExceptions {
|
||||
if re.isLocal {
|
||||
regexExceptionsList = append(regexExceptionsList, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算本地规则数量
|
||||
localDomainRulesCount := 0
|
||||
for _, isLocal := range m.domainRulesIsLocal {
|
||||
if isLocal {
|
||||
localDomainRulesCount++
|
||||
}
|
||||
}
|
||||
localRegexRulesCount := 0
|
||||
for _, re := range m.regexRules {
|
||||
if re.isLocal {
|
||||
localRegexRulesCount++
|
||||
}
|
||||
}
|
||||
localRulesCount := localDomainRulesCount + localRegexRulesCount
|
||||
|
||||
return map[string]interface{}{
|
||||
"domainRules": domainRulesList,
|
||||
"domainExceptions": domainExceptionsList,
|
||||
"regexRules": regexRulesList,
|
||||
"regexExceptions": regexExceptionsList,
|
||||
"localRulesCount": localRulesCount,
|
||||
"localDomainRulesCount": localDomainRulesCount,
|
||||
"localRegexRulesCount": localRegexRulesCount,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRemoteRules 获取仅远程规则
|
||||
func (m *ShieldManager) GetRemoteRules() map[string]interface{} {
|
||||
m.rulesMutex.RLock()
|
||||
defer m.rulesMutex.RUnlock()
|
||||
|
||||
// 转换map和slice为字符串列表,只包含远程规则
|
||||
domainRulesList := make([]string, 0)
|
||||
for domain, isLocal := range m.domainRulesIsLocal {
|
||||
if !isLocal && m.domainRules[domain] {
|
||||
domainRulesList = append(domainRulesList, "||"+domain+"^")
|
||||
}
|
||||
}
|
||||
|
||||
domainExceptionsList := make([]string, 0)
|
||||
for domain, isLocal := range m.domainExceptionsIsLocal {
|
||||
if !isLocal && m.domainExceptions[domain] {
|
||||
domainExceptionsList = append(domainExceptionsList, "@@||"+domain+"^")
|
||||
}
|
||||
}
|
||||
|
||||
// 获取远程正则规则原始字符串
|
||||
regexRulesList := make([]string, 0)
|
||||
for _, re := range m.regexRules {
|
||||
if !re.isLocal {
|
||||
regexRulesList = append(regexRulesList, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取远程正则排除规则原始字符串
|
||||
regexExceptionsList := make([]string, 0)
|
||||
for _, re := range m.regexExceptions {
|
||||
if !re.isLocal {
|
||||
regexExceptionsList = append(regexExceptionsList, re.original)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算远程规则数量
|
||||
remoteDomainRulesCount := 0
|
||||
for _, isLocal := range m.domainRulesIsLocal {
|
||||
if !isLocal {
|
||||
remoteDomainRulesCount++
|
||||
}
|
||||
}
|
||||
remoteRegexRulesCount := 0
|
||||
for _, re := range m.regexRules {
|
||||
if !re.isLocal {
|
||||
remoteRegexRulesCount++
|
||||
}
|
||||
}
|
||||
remoteRulesCount := remoteDomainRulesCount + remoteRegexRulesCount
|
||||
|
||||
return map[string]interface{}{
|
||||
"domainRules": domainRulesList,
|
||||
"domainExceptions": domainExceptionsList,
|
||||
"regexRules": regexRulesList,
|
||||
"regexExceptions": regexExceptionsList,
|
||||
"remoteRulesCount": remoteRulesCount,
|
||||
"remoteDomainRulesCount": remoteDomainRulesCount,
|
||||
"remoteRegexRulesCount": remoteRegexRulesCount,
|
||||
"blacklists": m.config.Blacklists,
|
||||
}
|
||||
}
|
||||
|
||||
// GetRules 获取所有规则
|
||||
func (m *ShieldManager) GetRules() map[string]interface{} {
|
||||
m.rulesMutex.RLock()
|
||||
|
||||
62
shield/rule_test.go
Normal file
62
shield/rule_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package shield
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dns-server/config"
|
||||
)
|
||||
|
||||
func TestRuleParsing(t *testing.T) {
|
||||
// 创建一个简单的配置
|
||||
cfg := &config.ShieldConfig{
|
||||
LocalRulesFile: "",
|
||||
RemoteRulesCacheDir: ".",
|
||||
UpdateInterval: 3600,
|
||||
StatsFile: "",
|
||||
StatsSaveInterval: 300,
|
||||
HostsFile: "",
|
||||
Blacklists: []config.BlacklistEntry{},
|
||||
}
|
||||
|
||||
// 测试规则
|
||||
testCases := []struct {
|
||||
rule string
|
||||
domain string
|
||||
blocked bool
|
||||
desc string
|
||||
}{
|
||||
// 测试关键字匹配规则
|
||||
{"/ad.qq.com/", "ad.qq.com", true, "精确匹配"},
|
||||
{"/ad.qq.com/", "sub.ad.qq.com", true, "子域名包含匹配"},
|
||||
{"/ad/", "ad.example.com", true, "开头匹配"},
|
||||
{"/ad/", "example.ad.com", true, "中间匹配"},
|
||||
{"/ad/", "example.com.ad", true, "结尾匹配"},
|
||||
{"/AD/", "ad.example.com", true, "不区分大小写匹配"},
|
||||
{"/example.com/", "example.com", true, "特殊字符转义匹配"},
|
||||
{"/ad/", "example.com", false, "不包含关键字,不应匹配"},
|
||||
{"/test/", "example.com", false, "不同关键字,不应匹配"},
|
||||
|
||||
// 测试排除规则
|
||||
{"@@/ad/", "ad.example.com", false, "排除规则,不应匹配"},
|
||||
}
|
||||
|
||||
// 运行测试
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// 为每个测试用例创建一个新的屏蔽管理器实例
|
||||
manager := NewShieldManager(cfg)
|
||||
|
||||
// 添加规则
|
||||
manager.AddRule(tc.rule)
|
||||
|
||||
// 检查域名是否被屏蔽
|
||||
result := manager.CheckDomainBlockDetails(tc.domain)
|
||||
blocked := result["blocked"].(bool)
|
||||
|
||||
// 验证结果
|
||||
if blocked != tc.blocked {
|
||||
t.Errorf("Rule %q: Domain %q expected %t, got %t", tc.rule, tc.domain, tc.blocked, blocked)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user