332 lines
8.9 KiB
Go
332 lines
8.9 KiB
Go
/*
|
||
* Copyright (C) 2020-2022, IrineSistiana
|
||
*
|
||
* This file is part of mosdns.
|
||
*
|
||
* mosdns is free software: you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation, either version 3 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* mosdns is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
package tools
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
|
||
"github.com/IrineSistiana/mosdns/v5/coremain"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
func init() {
|
||
probeCmd := &cobra.Command{
|
||
Use: "probe",
|
||
Short: "Run some server tests.",
|
||
}
|
||
probeCmd.AddCommand(
|
||
newConnReuseCmd(),
|
||
newIdleTimeoutCmd(),
|
||
newPipelineCmd(),
|
||
)
|
||
coremain.AddSubCmd(probeCmd)
|
||
|
||
configCmd := &cobra.Command{
|
||
Use: "config",
|
||
Short: "Tools that can generate/convert mosdns config file.",
|
||
}
|
||
configCmd.AddCommand(newGenCmd(), newConvCmd())
|
||
coremain.AddSubCmd(configCmd)
|
||
|
||
// 添加 init 命令用于快速初始化配置
|
||
initCmd := &cobra.Command{
|
||
Use: "init",
|
||
Short: "Initialize mosdns configuration and directories.",
|
||
Long: "Create default config.yaml and necessary directories for quick deployment on any server.",
|
||
RunE: runInit,
|
||
}
|
||
var forceFlag bool
|
||
initCmd.Flags().BoolVarP(&forceFlag, "force", "f", false, "强制覆盖已存在的配置文件")
|
||
coremain.AddSubCmd(initCmd)
|
||
}
|
||
|
||
// runInit 执行初始化操作
|
||
func runInit(cmd *cobra.Command, args []string) error {
|
||
force, _ := cmd.Flags().GetBool("force")
|
||
|
||
fmt.Println("========================================")
|
||
fmt.Println(" 🚀 MosDNS 初始化向导")
|
||
fmt.Println("========================================")
|
||
fmt.Println()
|
||
|
||
// 1. 检查并创建配置文件
|
||
configFile := "config.yaml"
|
||
if err := createConfigFile(configFile, force); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 2. 创建必要的目录结构
|
||
if err := createDirectories(); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 3. 创建示例数据文件(如果不存在)
|
||
if err := createDataFiles(); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 4. 显示完成信息
|
||
showCompletionInfo()
|
||
|
||
return nil
|
||
}
|
||
|
||
// createConfigFile 创建默认配置文件
|
||
func createConfigFile(filename string, force bool) error {
|
||
// 检查文件是否已存在
|
||
if _, err := os.Stat(filename); err == nil && !force {
|
||
fmt.Printf("⚠️ 配置文件已存在: %s\n", filename)
|
||
fmt.Println(" 使用 --force 或 -f 参数强制覆盖")
|
||
return nil
|
||
}
|
||
|
||
// 生成默认配置内容
|
||
configContent := `# ========================================
|
||
# MosDNS 配置文件 - 智能 DNS 服务器
|
||
# 自动生成时间: $(date)
|
||
# ========================================
|
||
|
||
# 日志配置
|
||
log:
|
||
level: info # 日志级别: debug, info, warn, error
|
||
file: "" # 日志文件路径(空表示输出到控制台)
|
||
|
||
# API 管理接口配置
|
||
api:
|
||
http: "0.0.0.0:8080" # API 监听地址和端口
|
||
|
||
# Web 管理界面配置
|
||
web:
|
||
http: "0.0.0.0:5555" # Web UI 监听地址和端口
|
||
|
||
# 插件配置
|
||
plugins:
|
||
# ========================================
|
||
# 1. 数据源插件
|
||
# ========================================
|
||
|
||
# 中国 IP 地址库(用于智能分流)
|
||
- tag: geoip_cn
|
||
type: ip_set
|
||
args:
|
||
files:
|
||
- "./data/chn_ip.txt"
|
||
|
||
# 中国域名列表(用于智能分流)
|
||
- tag: geosite_cn
|
||
type: domain_set
|
||
args:
|
||
files:
|
||
- "./data/geosite_china-list.txt"
|
||
|
||
# ========================================
|
||
# 2. DNS 上游服务器
|
||
# ========================================
|
||
|
||
# 国内 DNS 上游(用于解析国内域名)
|
||
- tag: forward_local
|
||
type: forward
|
||
args:
|
||
concurrent: 2 # 并发查询数量
|
||
upstreams:
|
||
- addr: "223.5.5.5" # 阿里云 DNS
|
||
- addr: "119.29.29.29" # 腾讯云 DNS
|
||
|
||
# 国外 DNS 上游(用于解析国外域名)
|
||
- tag: forward_remote
|
||
type: forward
|
||
args:
|
||
concurrent: 2
|
||
upstreams:
|
||
- addr: "https://1.1.1.1/dns-query" # Cloudflare DoH
|
||
enable_http3: false
|
||
- addr: "https://8.8.8.8/dns-query" # Google DoH
|
||
enable_http3: false
|
||
|
||
# ========================================
|
||
# 3. 缓存插件
|
||
# ========================================
|
||
|
||
- tag: main_cache
|
||
type: cache
|
||
args:
|
||
size: 100000 # 缓存条目数量
|
||
lazy_cache_ttl: 86400 # 惰性缓存 TTL(秒)
|
||
dump_file: "./cache.dump" # 缓存持久化文件
|
||
dump_interval: 3600 # 缓存保存间隔(秒)
|
||
|
||
# ========================================
|
||
# 4. 主执行序列
|
||
# ========================================
|
||
|
||
- tag: main
|
||
type: sequence
|
||
args:
|
||
# 先查缓存
|
||
- exec: $main_cache
|
||
|
||
# 如果是国内域名,使用国内 DNS
|
||
- matches:
|
||
- qname $geosite_cn
|
||
exec: $forward_local
|
||
|
||
# 其他域名使用国外 DNS
|
||
- exec: $forward_remote
|
||
|
||
# 将结果存入缓存
|
||
- matches:
|
||
- has_resp
|
||
exec: $main_cache
|
||
|
||
# ========================================
|
||
# 5. DNS 服务器
|
||
# ========================================
|
||
|
||
# UDP 服务器
|
||
- tag: udp_server
|
||
type: udp_server
|
||
args:
|
||
entry: main # 入口执行序列
|
||
listen: ":53" # 监听端口(需要 root 权限)
|
||
|
||
# TCP 服务器
|
||
- tag: tcp_server
|
||
type: tcp_server
|
||
args:
|
||
entry: main
|
||
listen: ":53"
|
||
`
|
||
|
||
// 写入文件
|
||
if err := os.WriteFile(filename, []byte(configContent), 0644); err != nil {
|
||
return fmt.Errorf("创建配置文件失败: %w", err)
|
||
}
|
||
|
||
fmt.Printf("✅ 配置文件已创建: %s\n", filename)
|
||
return nil
|
||
}
|
||
|
||
// createDirectories 创建必要的目录结构
|
||
func createDirectories() error {
|
||
dirs := []string{
|
||
"./data", // 数据文件目录
|
||
"./config.d", // 配置文件目录
|
||
"./config.d/rules", // 规则文件目录
|
||
"./logs", // 日志目录(可选)
|
||
}
|
||
|
||
fmt.Println()
|
||
fmt.Println("📁 创建目录结构...")
|
||
|
||
for _, dir := range dirs {
|
||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||
return fmt.Errorf("创建目录失败 %s: %w", dir, err)
|
||
}
|
||
fmt.Printf(" ✅ %s\n", dir)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// createDataFiles 创建示例数据文件
|
||
func createDataFiles() error {
|
||
fmt.Println()
|
||
fmt.Println("📄 检查数据文件...")
|
||
|
||
dataFiles := map[string]string{
|
||
"./data/chn_ip.txt": `# 中国 IP 地址段(示例)
|
||
# 请从以下地址下载完整列表:
|
||
# https://github.com/17mon/china_ip_list
|
||
|
||
# 示例 IP 段
|
||
1.0.1.0/24
|
||
1.0.2.0/23
|
||
`,
|
||
"./data/geosite_china-list.txt": `# 中国常见域名列表(示例)
|
||
# 请从以下地址下载完整列表:
|
||
# https://github.com/felixonmars/dnsmasq-china-list
|
||
|
||
# 示例域名
|
||
domain:baidu.com
|
||
domain:qq.com
|
||
domain:taobao.com
|
||
domain:tmall.com
|
||
domain:jd.com
|
||
`,
|
||
}
|
||
|
||
for file, content := range dataFiles {
|
||
// 如果文件已存在且不为空,跳过
|
||
if stat, err := os.Stat(file); err == nil && stat.Size() > 0 {
|
||
fmt.Printf(" ⏭️ 已存在: %s\n", file)
|
||
continue
|
||
}
|
||
|
||
// 创建示例文件
|
||
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
|
||
return fmt.Errorf("创建数据文件失败 %s: %w", file, err)
|
||
}
|
||
fmt.Printf(" ✅ 已创建: %s\n", file)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// showCompletionInfo 显示完成信息和后续步骤
|
||
func showCompletionInfo() {
|
||
wd, _ := os.Getwd()
|
||
|
||
fmt.Println()
|
||
fmt.Println("========================================")
|
||
fmt.Println(" 🎉 初始化完成!")
|
||
fmt.Println("========================================")
|
||
fmt.Println()
|
||
fmt.Println("📂 工作目录:", wd)
|
||
fmt.Println()
|
||
fmt.Println("📋 已创建的文件和目录:")
|
||
fmt.Println(" config.yaml - 主配置文件")
|
||
fmt.Println(" data/ - 数据文件目录")
|
||
fmt.Println(" config.d/rules/ - 规则文件目录")
|
||
fmt.Println()
|
||
fmt.Println("⚠️ 重要提示:")
|
||
fmt.Println(" 1. 数据文件为示例文件,建议下载完整的 CN IP 和域名列表")
|
||
fmt.Println(" - CN IP: https://github.com/17mon/china_ip_list")
|
||
fmt.Println(" - CN 域名: https://github.com/felixonmars/dnsmasq-china-list")
|
||
fmt.Println()
|
||
fmt.Println(" 2. 默认端口 53 需要 root 权限,可以修改为其他端口(如 5310)")
|
||
fmt.Println()
|
||
fmt.Println("🚀 启动服务:")
|
||
fmt.Println()
|
||
fmt.Println(" # 开发模式(非 root)")
|
||
fmt.Println(" sed -i 's/:53/:5310/g' config.yaml")
|
||
fmt.Println(" ./mosdns-linux-amd64 start -c config.yaml")
|
||
fmt.Println()
|
||
fmt.Println(" # 生产模式(需要 root)")
|
||
fmt.Println(" sudo ./mosdns-linux-amd64 start -c config.yaml")
|
||
fmt.Println()
|
||
fmt.Println("🌐 管理界面:")
|
||
fmt.Println(" Web UI: http://localhost:5555")
|
||
fmt.Println(" API: http://localhost:8080")
|
||
fmt.Println()
|
||
fmt.Println("========================================")
|
||
fmt.Println()
|
||
}
|