更新版本到2.0
This commit is contained in:
241
gfw/manager.go
Normal file
241
gfw/manager.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user