package rule import ( "bufio" "encoding/json" "fmt" "log/slog" "net" "os" "strconv" "strings" ) // Loader 规则加载器 type Loader struct { manager *Manager logger *slog.Logger } // NewLoader 创建规则加载器 func NewLoader(manager *Manager, logger *slog.Logger) *Loader { if logger == nil { logger = slog.Default() } return &Loader{ manager: manager, logger: logger, } } // LoadFromJSON 从JSON文件加载规则 func (l *Loader) LoadFromJSON(filename string) error { data, err := os.ReadFile(filename) if err != nil { return fmt.Errorf("读取规则文件失败: %w", err) } var config struct { Rules []json.RawMessage `json:"rules"` } if err := json.Unmarshal(data, &config); err != nil { return fmt.Errorf("解析规则文件失败: %w", err) } for _, ruleData := range config.Rules { var baseRule BaseRule if err := json.Unmarshal(ruleData, &baseRule); err != nil { l.logger.Warn("解析规则基础信息失败", "error", err.Error(), "rule_data", string(ruleData), ) continue } var rule Rule switch baseRule.Type { case RuleTypeDNS: var dnsRule DNSRule if err := json.Unmarshal(ruleData, &dnsRule); err != nil { l.logger.Warn("解析DNS规则失败", "error", err.Error(), "rule_data", string(ruleData), ) continue } rule = &dnsRule case RuleTypeRewrite: var rewriteRule RewriteRule if err := json.Unmarshal(ruleData, &rewriteRule); err != nil { l.logger.Warn("解析重写规则失败", "error", err.Error(), "rule_data", string(ruleData), ) continue } rule = &rewriteRule case RuleTypeRoute: var routeRule RouteRule if err := json.Unmarshal(ruleData, &routeRule); err != nil { l.logger.Warn("解析路由规则失败", "error", err.Error(), "rule_data", string(ruleData), ) continue } rule = &routeRule default: l.logger.Warn("未知的规则类型", "type", string(baseRule.Type), "rule_data", string(ruleData), ) continue } if err := l.manager.AddRule(rule); err != nil { l.logger.Error("添加规则失败", "error", err.Error(), "rule_id", rule.GetID(), "rule_type", string(rule.GetType()), ) } } return nil } // LoadFromHosts 从hosts文件加载DNS规则 func (l *Loader) LoadFromHosts(filename string) error { file, err := os.Open(filename) if err != nil { return fmt.Errorf("打开hosts文件失败: %w", err) } defer file.Close() scanner := bufio.NewScanner(file) lineNum := 0 for scanner.Scan() { lineNum++ line := strings.TrimSpace(scanner.Text()) // 跳过空行和注释 if line == "" || strings.HasPrefix(line, "#") { continue } fields := strings.Fields(line) if len(fields) < 2 { l.logger.Warn("无效的hosts行", "line_number", lineNum, "line", line, ) continue } // 解析IP和端口 ipStr := fields[0] ip := ipStr port := 0 if strings.Contains(ipStr, ":") { parts := strings.Split(ipStr, ":") if len(parts) != 2 { l.logger.Warn("无效的IP:端口格式", "line_number", lineNum, "ip_port", ipStr, ) continue } ip = parts[0] if p, err := strconv.Atoi(parts[1]); err == nil { port = p } else { l.logger.Warn("无效的端口号", "line_number", lineNum, "port", parts[1], ) continue } } // 验证IP地址 if net.ParseIP(ip) == nil { l.logger.Warn("无效的IP地址", "line_number", lineNum, "ip", ip, ) continue } // 处理每个域名 for _, domain := range fields[1:] { // 跳过注释 if strings.HasPrefix(domain, "#") { break } // 创建DNS规则 matchType := MatchTypeExact if strings.Contains(domain, "*") { matchType = MatchTypeWildcard } rule := &DNSRule{ BaseRule: BaseRule{ ID: fmt.Sprintf("hosts-%d-%s", lineNum, domain), Type: RuleTypeDNS, Priority: 100, Pattern: domain, MatchType: matchType, Enabled: true, }, Targets: []DNSTarget{ { IP: ip, Port: port, }, }, } if err := l.manager.AddRule(rule); err != nil { l.logger.Error("添加hosts规则失败", "error", err.Error(), "domain", domain, "ip", ip, "port", port, ) } } } if err := scanner.Err(); err != nil { return fmt.Errorf("读取hosts文件失败: %w", err) } return nil }