package main import ( "flag" "fmt" "log" "os" "os/signal" "path/filepath" "syscall" "dns-server/config" "dns-server/dns" "dns-server/http" "dns-server/logger" "dns-server/shield" ) // createDefaultConfig 创建默认配置文件 func createDefaultConfig(configFile string) error { // 默认配置内容 defaultConfig := `{ "dns": { "port": 53, "upstreamDNS": [ "223.5.5.5:53", "223.6.6.6:53" ], "timeout": 5000, "statsFile": "./data/stats.json", "saveInterval": 300 }, "http": { "port": 8081, "host": "0.0.0.0", "enableAPI": true }, "shield": { "localRulesFile": "data/rules.txt", "blacklists": [ { "name": "AdGuard DNS filter", "url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/filter.txt", "enabled": true }, { "name": "Adaway Default Blocklist", "url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/hosts/adaway.txt", "enabled": true }, { "name": "CHN-anti-AD", "url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/list/easylist.txt", "enabled": true }, { "name": "My GitHub Rules", "url": "https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/costomize.txt", "enabled": true } ], "updateInterval": 3600, "hostsFile": "data/hosts.txt", "blockMethod": "NXDOMAIN", "customBlockIP": "", "statsFile": "./data/shield_stats.json", "statsSaveInterval": 60, "remoteRulesCacheDir": "./data/remote_rules" }, "log": { "file": "logs/dns-server.log", "level": "debug", "maxSize": 100, "maxBackups": 10, "maxAge": 30 } }` // 写入默认配置到文件 return os.WriteFile(configFile, []byte(defaultConfig), 0644) } // createRequiredFiles 创建所需的文件和文件夹 func createRequiredFiles(cfg *config.Config) error { // 创建数据文件夹 dataDir := "./data" if err := os.MkdirAll(dataDir, 0755); err != nil { return fmt.Errorf("创建数据文件夹失败: %w", err) } // 创建远程规则缓存文件夹 if err := os.MkdirAll(cfg.Shield.RemoteRulesCacheDir, 0755); err != nil { return fmt.Errorf("创建远程规则缓存文件夹失败: %w", err) } // 创建日志文件夹 logDir := filepath.Dir(cfg.Log.File) if logDir != "." { if err := os.MkdirAll(logDir, 0755); err != nil { return fmt.Errorf("创建日志文件夹失败: %w", err) } } // 创建本地规则文件 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) } } // 创建Hosts文件 if _, err := os.Stat(cfg.Shield.HostsFile); os.IsNotExist(err) { if err := os.WriteFile(cfg.Shield.HostsFile, []byte("# Hosts文件\n# 格式:IP 域名\n# 例如:127.0.0.1 localhost\n"), 0644); err != nil { return fmt.Errorf("创建Hosts文件失败: %w", err) } } // 创建统计数据文件 if _, err := os.Stat(cfg.DNS.StatsFile); os.IsNotExist(err) { if err := os.WriteFile(cfg.DNS.StatsFile, []byte("{}"), 0644); err != nil { return fmt.Errorf("创建统计数据文件失败: %w", err) } } // 创建Shield统计数据文件 if _, err := os.Stat(cfg.Shield.StatsFile); os.IsNotExist(err) { if err := os.WriteFile(cfg.Shield.StatsFile, []byte("{}"), 0644); err != nil { return fmt.Errorf("创建Shield统计数据文件失败: %w", err) } } return nil } func main() { // 命令行参数解析 var configFile string var daemonMode bool flag.StringVar(&configFile, "config", "config.json", "配置文件路径") flag.BoolVar(&daemonMode, "daemon", false, "以守护进程模式运行") flag.Parse() // 如果是守护进程模式,创建守护进程 if daemonMode { if err := daemonize(); err != nil { log.Fatalf("创建守护进程失败: %v", err) } // 父进程退出 os.Exit(0) } // 检查配置文件是否存在,如果不存在则创建默认配置文件 if _, err := os.Stat(configFile); os.IsNotExist(err) { log.Printf("配置文件 %s 不存在,正在创建默认配置文件...", configFile) if err := createDefaultConfig(configFile); err != nil { log.Fatalf("创建默认配置文件失败: %v", err) } log.Printf("默认配置文件 %s 创建成功", configFile) } // 初始化配置 var cfg *config.Config var err error cfg, err = config.LoadConfig(configFile) if err != nil { log.Fatalf("加载配置失败: %v", err) } // 创建所需的文件和文件夹 log.Println("正在创建所需的文件和文件夹...") if err := createRequiredFiles(cfg); err != nil { log.Fatalf("创建所需文件和文件夹失败: %v", err) } log.Println("所需文件和文件夹创建成功") // 初始化日志系统 if err := logger.InitLogger(cfg.Log.File, cfg.Log.Level, 0, 0, 0); err != nil { log.Fatalf("初始化日志系统失败: %v", err) } defer logger.Close() // 初始化屏蔽管理系统 shieldManager := shield.NewShieldManager(&cfg.Shield) if err := shieldManager.LoadRules(); err != nil { logger.Error("加载屏蔽规则失败", "error", err) } // 启动DNS服务器 dnsServer := dns.NewServer(&cfg.DNS, &cfg.Shield, shieldManager) go func() { if err := dnsServer.Start(); err != nil { logger.Error("DNS服务器启动失败", "error", err) os.Exit(1) } }() // 启动HTTP控制台服务器 httpServer := http.NewServer(cfg, dnsServer, shieldManager) go func() { if err := httpServer.Start(); err != nil { logger.Error("HTTP控制台服务器启动失败", "error", err) } }() // 启动定时更新任务 go shieldManager.StartAutoUpdate() logger.Info(fmt.Sprintf("DNS服务器已启动,监听端口: %d", cfg.DNS.Port)) logger.Info(fmt.Sprintf("HTTP控制台已启动,监听端口: %d", cfg.HTTP.Port)) // 监听信号 sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh // 清理资源 log.Println("正在关闭服务...") dnsServer.Stop() httpServer.Stop() shieldManager.StopAutoUpdate() // 守护进程模式下不需要删除PID文件 log.Println("服务已关闭") } // daemonize 创建守护进程 func daemonize() error { // 使用更简单的方式创建守护进程:直接在当前进程中进行守护化处理 // 1. 重定向标准输入、输出、错误 nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0) if err != nil { return fmt.Errorf("打开/dev/null失败: %w", err) } defer nullFile.Close() // 重定向文件描述符 err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd())) if err != nil { return fmt.Errorf("重定向stdin失败: %w", err) } err = syscall.Dup2(int(nullFile.Fd()), int(os.Stdout.Fd())) if err != nil { return fmt.Errorf("重定向stdout失败: %w", err) } err = syscall.Dup2(int(nullFile.Fd()), int(os.Stderr.Fd())) if err != nil { return fmt.Errorf("重定向stderr失败: %w", err) } // 2. 创建新的会话和进程组 _, err = syscall.Setsid() if err != nil { return fmt.Errorf("创建新会话失败: %w", err) } fmt.Println("守护进程已启动") return nil }