更新版本到2.0

This commit is contained in:
Alex Yang
2026-01-25 16:13:52 +08:00
parent ecbc20d89f
commit a5dc5841fb
54 changed files with 76520 additions and 2914 deletions

241
gfw/manager.go Normal file
View File

@@ -0,0 +1,241 @@
package gfw
import (
"encoding/base64"
"fmt"
"os"
"regexp"
"strings"
"sync"
"dns-server/config"
"dns-server/logger"
)
// regexRule 正则规则结构,包含编译后的表达式和原始字符串
type regexRule struct {
pattern *regexp.Regexp
original string
}
// GFWListManager GFWList管理器
type GFWListManager struct {
config *config.GFWListConfig
domainRules map[string]bool
regexRules []regexRule
rulesMutex sync.RWMutex
}
// NewGFWListManager 创建GFWList管理器实例
func NewGFWListManager(config *config.GFWListConfig) *GFWListManager {
return &GFWListManager{
config: config,
domainRules: make(map[string]bool),
regexRules: []regexRule{},
}
}
// LoadRules 加载GFWList规则
func (m *GFWListManager) LoadRules() error {
// 如果GFWList功能未启用不加载规则
if !m.config.Enabled {
return nil
}
m.rulesMutex.Lock()
defer m.rulesMutex.Unlock()
// 清空现有规则
m.domainRules = make(map[string]bool)
m.regexRules = []regexRule{}
if m.config.Content == "" {
return nil // 没有GFWList内容直接返回
}
// 从文件路径读取GFWList内容
fileContent, err := os.ReadFile(m.config.Content)
if err != nil {
return fmt.Errorf("读取GFWList文件失败: %w", err)
}
rawContent := string(fileContent)
var ruleContent string
// 过滤注释行收集可能的Base64内容
var base64Content strings.Builder
lines := strings.Split(rawContent, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
// 跳过注释行和头信息行
continue
}
base64Content.WriteString(line)
}
// 尝试Base64解码
decoded, err := base64.StdEncoding.DecodeString(base64Content.String())
if err == nil {
// 解码成功,使用解码后的内容
ruleContent = string(decoded)
logger.Info("GFWList文件为Base64编码已成功解码")
} else {
// 解码失败,使用原始内容(可能是纯文本格式)
ruleContent = rawContent
logger.Info("GFWList文件为纯文本格式直接解析")
}
// 按行解析规则内容
ruleLines := strings.Split(ruleContent, "\n")
for _, line := range ruleLines {
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "!") || strings.HasPrefix(line, "[") {
// 跳过空行、注释行和头信息行
continue
}
m.parseRule(line)
}
logger.Info(fmt.Sprintf("GFWList规则加载完成域名规则: %d, 正则规则: %d",
len(m.domainRules), len(m.regexRules)))
return nil
}
// parseRule 解析规则行
func (m *GFWListManager) parseRule(line string) {
// 保存原始规则用于后续使用
originalLine := line
// 处理注释
if strings.HasPrefix(line, "!") || strings.HasPrefix(line, "#") || line == "" {
return
}
// 移除规则选项部分(暂时不处理规则选项)
if strings.Contains(line, "$") {
parts := strings.SplitN(line, "$", 2)
line = parts[0]
// 规则选项暂时不处理
}
// 处理不同类型的规则
switch {
case strings.HasPrefix(line, "||") && strings.HasSuffix(line, "^"):
// AdGuardHome域名规则格式: ||example.com^
domain := strings.TrimSuffix(strings.TrimPrefix(line, "||"), "^")
m.addDomainRule(domain, originalLine)
case strings.HasPrefix(line, "||"):
// 域名片段匹配规则: ||google 匹配任何包含google的域名
domain := strings.TrimPrefix(line, "||")
// 添加精确域名匹配
m.addDomainRule(domain, originalLine)
// 同时添加正则表达式规则,匹配任何包含该域名片段的域名
if re, err := regexp.Compile("(?i).*" + regexp.QuoteMeta(domain) + ".*"); err == nil {
m.addRegexRule(re, originalLine)
}
case strings.HasPrefix(line, "*"):
// 通配符规则,转换为正则表达式
pattern := strings.ReplaceAll(line, "*", ".*")
pattern = "^" + pattern + "$"
if re, err := regexp.Compile(pattern); err == nil {
// 保存原始规则字符串
m.addRegexRule(re, originalLine)
}
case strings.HasPrefix(line, "/") && strings.HasSuffix(line, "/"):
// 正则表达式匹配规则:/regex/ 格式,不区分大小写
pattern := strings.TrimPrefix(strings.TrimSuffix(line, "/"), "/")
// 编译为不区分大小写的正则表达式,确保能匹配域名中任意位置
// 对于像 /domain/ 这样的规则,应该匹配包含 domain 字符串的任何域名
if re, err := regexp.Compile("(?i).*" + regexp.QuoteMeta(pattern) + ".*"); err == nil {
// 保存原始规则字符串
m.addRegexRule(re, originalLine)
}
case strings.HasPrefix(line, "|") && strings.HasSuffix(line, "|"):
// 完整URL匹配规则
urlPattern := strings.TrimPrefix(strings.TrimSuffix(line, "|"), "|")
// 将URL模式转换为正则表达式
pattern := "^" + regexp.QuoteMeta(urlPattern) + "$"
if re, err := regexp.Compile(pattern); err == nil {
m.addRegexRule(re, originalLine)
}
case strings.HasPrefix(line, "|"):
// URL开头匹配规则
urlPattern := strings.TrimPrefix(line, "|")
pattern := "^" + regexp.QuoteMeta(urlPattern)
if re, err := regexp.Compile(pattern); err == nil {
m.addRegexRule(re, originalLine)
}
case strings.HasSuffix(line, "|"):
// URL结尾匹配规则
urlPattern := strings.TrimSuffix(line, "|")
pattern := regexp.QuoteMeta(urlPattern) + "$"
if re, err := regexp.Compile(pattern); err == nil {
m.addRegexRule(re, originalLine)
}
default:
// 默认作为普通域名规则
m.addDomainRule(line, originalLine)
}
}
// addDomainRule 添加域名规则
func (m *GFWListManager) addDomainRule(domain string, original string) {
m.domainRules[domain] = true
}
// addRegexRule 添加正则表达式规则
func (m *GFWListManager) addRegexRule(re *regexp.Regexp, original string) {
rule := regexRule{
pattern: re,
original: original,
}
m.regexRules = append(m.regexRules, rule)
}
// IsMatch 检查域名是否匹配GFWList规则
func (m *GFWListManager) IsMatch(domain string) bool {
m.rulesMutex.RLock()
defer m.rulesMutex.RUnlock()
// 预处理域名,去除可能的端口号
if strings.Contains(domain, ":") {
parts := strings.Split(domain, ":")
domain = parts[0]
}
// 检查精确域名匹配
if m.domainRules[domain] {
return true
}
// 检查子域名匹配
parts := strings.Split(domain, ".")
for i := 0; i < len(parts)-1; i++ {
subdomain := strings.Join(parts[i:], ".")
if m.domainRules[subdomain] {
return true
}
}
// 检查正则表达式匹配
for _, re := range m.regexRules {
if re.pattern.MatchString(domain) {
return true
}
}
return false
}
// GetGFWListIP 获取GFWList的目标IP地址
func (m *GFWListManager) GetGFWListIP() string {
return m.config.IP
}