683 lines
18 KiB
Go
683 lines
18 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 coremain
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"net/http"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/go-chi/chi/v5"
|
||
"go.uber.org/zap"
|
||
"gopkg.in/yaml.v3"
|
||
)
|
||
|
||
// RuleConfig 域名路由规则配置
|
||
type RuleConfig struct {
|
||
Name string `json:"name"` // 规则名称(唯一标识)
|
||
DomainFile string `json:"domain_file"` // 域名文件路径
|
||
DNSStrategy string `json:"dns_strategy"` // DNS 策略:china / cloudflare / google / hybrid
|
||
EnableMikrotik bool `json:"enable_mikrotik"` // 是否启用 MikroTik 同步
|
||
MikrotikConfig MikrotikConfig `json:"mikrotik_config"` // MikroTik 配置
|
||
Description string `json:"description"` // 规则描述
|
||
Enabled bool `json:"enabled"` // 是否启用
|
||
}
|
||
|
||
// MikrotikConfig MikroTik 设备配置
|
||
type MikrotikConfig struct {
|
||
Host string `json:"host"` // MikroTik 地址
|
||
Port int `json:"port"` // API 端口
|
||
Username string `json:"username"` // 用户名
|
||
Password string `json:"password"` // 密码
|
||
AddressList string `json:"address_list"` // 地址列表名称
|
||
Mask int `json:"mask"` // IP 掩码(24/32)
|
||
MaxIPs int `json:"max_ips"` // 最大 IP 数量
|
||
CacheTTL int `json:"cache_ttl"` // 缓存时间(秒)
|
||
TimeoutAddr int `json:"timeout_addr"` // 地址超时时间(秒)
|
||
Comment string `json:"comment"` // 备注
|
||
}
|
||
|
||
// RuleInfo 规则信息(列表显示)
|
||
type RuleInfo struct {
|
||
Name string `json:"name"`
|
||
DomainFile string `json:"domain_file"`
|
||
DNSStrategy string `json:"dns_strategy"`
|
||
EnableMikrotik bool `json:"enable_mikrotik"`
|
||
MikrotikDevice string `json:"mikrotik_device"` // 简化显示:host:port
|
||
Description string `json:"description"`
|
||
Enabled bool `json:"enabled"`
|
||
FilePath string `json:"file_path"` // YAML 文件路径
|
||
}
|
||
|
||
// handleListRules 列出所有规则
|
||
func (m *Mosdns) handleListRules(w http.ResponseWriter, r *http.Request) {
|
||
// 扫描 config.d/rules 目录
|
||
rulesDir := "./config.d/rules"
|
||
files, err := filepath.Glob(filepath.Join(rulesDir, "*.yaml"))
|
||
if err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "扫描规则目录失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
var rules []RuleInfo
|
||
for _, file := range files {
|
||
ruleInfo, err := m.parseRuleFile(file)
|
||
if err != nil {
|
||
m.logger.Warn("解析规则文件失败", zap.String("file", file), zap.Error(err))
|
||
continue
|
||
}
|
||
rules = append(rules, ruleInfo)
|
||
}
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Data: rules,
|
||
Message: fmt.Sprintf("找到 %d 条规则", len(rules)),
|
||
})
|
||
}
|
||
|
||
// handleGetRule 获取规则详情
|
||
func (m *Mosdns) handleGetRule(w http.ResponseWriter, r *http.Request) {
|
||
name := chi.URLParam(r, "name")
|
||
if name == "" {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则名称不能为空",
|
||
})
|
||
return
|
||
}
|
||
|
||
// 查找规则文件(支持多种文件名格式)
|
||
filePath, err := m.findRuleFile(name)
|
||
if err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则不存在: " + name,
|
||
})
|
||
return
|
||
}
|
||
|
||
ruleConfig, err := m.parseRuleFileToConfig(filePath)
|
||
if err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "解析规则失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Data: ruleConfig,
|
||
})
|
||
}
|
||
|
||
// handleAddRule 添加新规则
|
||
func (m *Mosdns) handleAddRule(w http.ResponseWriter, r *http.Request) {
|
||
var rule RuleConfig
|
||
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "解析请求失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 验证必填字段
|
||
if rule.Name == "" {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则名称不能为空",
|
||
})
|
||
return
|
||
}
|
||
|
||
if rule.DomainFile == "" {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "域名文件路径不能为空",
|
||
})
|
||
return
|
||
}
|
||
|
||
if rule.DNSStrategy == "" {
|
||
rule.DNSStrategy = "smart-fallback" // 默认使用智能防污染
|
||
}
|
||
|
||
// 检查规则是否已存在
|
||
filePath := fmt.Sprintf("./config.d/rules/%s.yaml", rule.Name)
|
||
if _, err := os.Stat(filePath); err == nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则已存在: " + rule.Name,
|
||
})
|
||
return
|
||
}
|
||
|
||
// 使用配置构建器添加规则
|
||
builder := NewConfigBuilder(m.config, m.logger)
|
||
|
||
domainRule := DomainRule{
|
||
Name: rule.Name,
|
||
Description: rule.Description,
|
||
DomainFile: rule.DomainFile,
|
||
DNSStrategy: rule.DNSStrategy,
|
||
EnableMikroTik: rule.EnableMikrotik,
|
||
MikroTikConfig: MikroTikConfig{
|
||
Host: rule.MikrotikConfig.Host,
|
||
Port: rule.MikrotikConfig.Port,
|
||
Username: rule.MikrotikConfig.Username,
|
||
Password: rule.MikrotikConfig.Password,
|
||
AddressList: rule.MikrotikConfig.AddressList,
|
||
Mask: rule.MikrotikConfig.Mask,
|
||
MaxIPs: rule.MikrotikConfig.MaxIPs,
|
||
CacheTTL: rule.MikrotikConfig.CacheTTL,
|
||
TimeoutAddr: rule.MikrotikConfig.TimeoutAddr,
|
||
Comment: rule.MikrotikConfig.Comment,
|
||
},
|
||
Enabled: rule.Enabled,
|
||
}
|
||
|
||
if err := builder.AddDomainRule(domainRule); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "添加规则失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 保存主配置
|
||
if err := builder.Save(); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "保存主配置失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
m.logger.Info("规则已添加",
|
||
zap.String("name", rule.Name),
|
||
zap.String("domain_file", rule.DomainFile),
|
||
zap.String("dns_strategy", rule.DNSStrategy))
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Message: "规则添加成功,请重启服务使其生效",
|
||
Data: map[string]interface{}{
|
||
"name": rule.Name,
|
||
"domain_file": rule.DomainFile,
|
||
"dns_strategy": rule.DNSStrategy,
|
||
"mikrotik_enabled": rule.EnableMikrotik,
|
||
},
|
||
})
|
||
}
|
||
|
||
// handleUpdateRule 更新规则
|
||
func (m *Mosdns) handleUpdateRule(w http.ResponseWriter, r *http.Request) {
|
||
name := chi.URLParam(r, "name")
|
||
if name == "" {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则名称不能为空",
|
||
})
|
||
return
|
||
}
|
||
|
||
var rule RuleConfig
|
||
if err := json.NewDecoder(r.Body).Decode(&rule); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "解析请求失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
rule.Name = name // 确保名称一致
|
||
|
||
// 使用配置构建器更新规则
|
||
builder := NewConfigBuilder(m.config, m.logger)
|
||
|
||
domainRule := DomainRule{
|
||
Name: rule.Name,
|
||
Description: rule.Description,
|
||
DomainFile: rule.DomainFile,
|
||
DNSStrategy: rule.DNSStrategy,
|
||
EnableMikroTik: rule.EnableMikrotik,
|
||
MikroTikConfig: MikroTikConfig{
|
||
Host: rule.MikrotikConfig.Host,
|
||
Port: rule.MikrotikConfig.Port,
|
||
Username: rule.MikrotikConfig.Username,
|
||
Password: rule.MikrotikConfig.Password,
|
||
AddressList: rule.MikrotikConfig.AddressList,
|
||
Mask: rule.MikrotikConfig.Mask,
|
||
MaxIPs: rule.MikrotikConfig.MaxIPs,
|
||
CacheTTL: rule.MikrotikConfig.CacheTTL,
|
||
TimeoutAddr: rule.MikrotikConfig.TimeoutAddr,
|
||
Comment: rule.MikrotikConfig.Comment,
|
||
},
|
||
Enabled: rule.Enabled,
|
||
}
|
||
|
||
if err := builder.UpdateDomainRule(name, domainRule); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "更新规则失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
// 保存主配置
|
||
if err := builder.Save(); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "保存主配置失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
m.logger.Info("规则已更新",
|
||
zap.String("name", name),
|
||
zap.String("domain_file", rule.DomainFile),
|
||
zap.String("dns_strategy", rule.DNSStrategy))
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Message: "规则更新成功,请重启服务使其生效",
|
||
})
|
||
}
|
||
|
||
// handleDeleteRule 删除规则
|
||
func (m *Mosdns) handleDeleteRule(w http.ResponseWriter, r *http.Request) {
|
||
name := chi.URLParam(r, "name")
|
||
if name == "" {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则名称不能为空",
|
||
})
|
||
return
|
||
}
|
||
|
||
// 查找规则文件(支持多种文件名格式)
|
||
filePath, err := m.findRuleFile(name)
|
||
if err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "规则不存在: " + name,
|
||
})
|
||
return
|
||
}
|
||
|
||
// 删除文件
|
||
if err := os.Remove(filePath); err != nil {
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: false,
|
||
Message: "删除规则文件失败: " + err.Error(),
|
||
})
|
||
return
|
||
}
|
||
|
||
m.logger.Info("规则已删除",
|
||
zap.String("name", name),
|
||
zap.String("file", filePath))
|
||
|
||
m.writeJSONResponse(w, APIResponse{
|
||
Success: true,
|
||
Message: "规则删除成功,请重启服务使其生效",
|
||
})
|
||
}
|
||
|
||
// findRuleFile 查找规则文件(支持多种文件名格式)
|
||
// 优先级:{name}.yaml > example-{name}.yaml > {name}-rule.yaml
|
||
func (m *Mosdns) findRuleFile(name string) (string, error) {
|
||
rulesDir := "./config.d/rules"
|
||
|
||
// 尝试的文件名模式(按优先级)
|
||
patterns := []string{
|
||
fmt.Sprintf("%s.yaml", name), // 直接匹配
|
||
fmt.Sprintf("example-%s.yaml", name), // 示例前缀
|
||
fmt.Sprintf("%s-rule.yaml", name), // 规则后缀
|
||
}
|
||
|
||
for _, pattern := range patterns {
|
||
filePath := filepath.Join(rulesDir, pattern)
|
||
if _, err := os.Stat(filePath); err == nil {
|
||
return filePath, nil
|
||
}
|
||
}
|
||
|
||
// 如果都找不到,尝试模糊匹配(包含name的文件)
|
||
files, err := filepath.Glob(filepath.Join(rulesDir, "*.yaml"))
|
||
if err != nil {
|
||
return "", fmt.Errorf("规则文件不存在")
|
||
}
|
||
|
||
for _, file := range files {
|
||
baseName := filepath.Base(file)
|
||
// 去掉常见前缀和后缀后检查
|
||
cleanName := strings.TrimSuffix(baseName, ".yaml")
|
||
cleanName = strings.TrimPrefix(cleanName, "example-")
|
||
cleanName = strings.TrimSuffix(cleanName, "-rule")
|
||
|
||
if cleanName == name {
|
||
return file, nil
|
||
}
|
||
}
|
||
|
||
return "", fmt.Errorf("规则文件不存在")
|
||
}
|
||
|
||
// generateRuleYAML 生成规则 YAML 内容
|
||
func (m *Mosdns) generateRuleYAML(rule RuleConfig) string {
|
||
var sb strings.Builder
|
||
|
||
// 文件头注释
|
||
sb.WriteString(fmt.Sprintf(`# ============================================
|
||
# %s 域名解析规则
|
||
# 由 Web UI 自动生成
|
||
`, rule.Name))
|
||
|
||
if rule.Description != "" {
|
||
sb.WriteString(fmt.Sprintf("# 描述:%s\n", rule.Description))
|
||
}
|
||
|
||
sb.WriteString("# ============================================\n\n")
|
||
sb.WriteString("plugins:\n")
|
||
|
||
// 1. 域名集合
|
||
sb.WriteString(fmt.Sprintf(` # 域名集合定义
|
||
- tag: domains_%s
|
||
type: domain_set
|
||
args:
|
||
files:
|
||
- "%s"
|
||
|
||
`, rule.Name, rule.DomainFile))
|
||
|
||
// 2. 解析策略序列
|
||
dnsExec := m.getDNSExec(rule.DNSStrategy)
|
||
|
||
sb.WriteString(fmt.Sprintf(` # 解析策略序列
|
||
- tag: rule_%s
|
||
type: sequence
|
||
args:
|
||
# 匹配域名
|
||
- matches: qname $domains_%s
|
||
exec: prefer_ipv4
|
||
|
||
# 使用指定的 DNS 策略解析
|
||
- matches: qname $domains_%s
|
||
exec: $%s
|
||
`, rule.Name, rule.Name, rule.Name, dnsExec))
|
||
|
||
// 3. MikroTik 配置(可选)
|
||
if rule.EnableMikrotik {
|
||
sb.WriteString(fmt.Sprintf(`
|
||
# 推送到 MikroTik
|
||
- matches:
|
||
- qname $domains_%s
|
||
- has_resp
|
||
exec: $mikrotik_%s
|
||
`, rule.Name, rule.Name))
|
||
}
|
||
|
||
// 4. 返回结果
|
||
sb.WriteString(fmt.Sprintf(`
|
||
# 返回结果
|
||
- matches:
|
||
- qname $domains_%s
|
||
- has_resp
|
||
exec: accept
|
||
|
||
# 记录日志
|
||
- matches: qname $domains_%s
|
||
exec: query_summary %s_resolved
|
||
`, rule.Name, rule.Name, rule.Name))
|
||
|
||
// 5. MikroTik 插件配置(可选)
|
||
if rule.EnableMikrotik {
|
||
cfg := rule.MikrotikConfig
|
||
|
||
// 设置默认值
|
||
if cfg.Port == 0 {
|
||
cfg.Port = 9728
|
||
}
|
||
if cfg.Mask == 0 {
|
||
cfg.Mask = 24
|
||
}
|
||
if cfg.MaxIPs == 0 {
|
||
cfg.MaxIPs = 50
|
||
}
|
||
if cfg.CacheTTL == 0 {
|
||
cfg.CacheTTL = 3600
|
||
}
|
||
if cfg.TimeoutAddr == 0 {
|
||
cfg.TimeoutAddr = 43200
|
||
}
|
||
if cfg.Comment == "" {
|
||
cfg.Comment = fmt.Sprintf("%s-AutoAdd", rule.Name)
|
||
}
|
||
|
||
sb.WriteString(fmt.Sprintf(`
|
||
|
||
# MikroTik 地址列表同步配置
|
||
- tag: mikrotik_%s
|
||
type: mikrotik_addresslist
|
||
args:
|
||
domain_files:
|
||
- "%s"
|
||
host: "%s"
|
||
port: %d
|
||
username: "%s"
|
||
password: "%s"
|
||
use_tls: false
|
||
timeout: 3
|
||
address_list4: "%s"
|
||
mask4: %d
|
||
comment: "%s"
|
||
timeout_addr: %d
|
||
cache_ttl: %d
|
||
verify_add: false
|
||
add_all_ips: true
|
||
max_ips: %d
|
||
`,
|
||
rule.Name,
|
||
rule.DomainFile,
|
||
cfg.Host,
|
||
cfg.Port,
|
||
cfg.Username,
|
||
cfg.Password,
|
||
cfg.AddressList,
|
||
cfg.Mask,
|
||
cfg.Comment,
|
||
cfg.TimeoutAddr,
|
||
cfg.CacheTTL,
|
||
cfg.MaxIPs,
|
||
))
|
||
}
|
||
|
||
return sb.String()
|
||
}
|
||
|
||
// getDNSExec 获取 DNS 策略对应的执行标签
|
||
func (m *Mosdns) getDNSExec(strategy string) string {
|
||
switch strategy {
|
||
case "china":
|
||
return "china-dns"
|
||
case "cloudflare":
|
||
return "overseas-dns-cloudflare"
|
||
case "google":
|
||
return "overseas-dns-google"
|
||
case "hybrid":
|
||
return "hybrid-dns"
|
||
case "anti-pollution":
|
||
return "smart_anti_pollution"
|
||
default:
|
||
return "china-dns"
|
||
}
|
||
}
|
||
|
||
// parseRuleFile 解析规则文件为 RuleInfo
|
||
func (m *Mosdns) parseRuleFile(filePath string) (RuleInfo, error) {
|
||
data, err := os.ReadFile(filePath)
|
||
if err != nil {
|
||
return RuleInfo{}, err
|
||
}
|
||
|
||
var config struct {
|
||
Plugins []PluginConfig `yaml:"plugins"`
|
||
}
|
||
|
||
if err := yaml.Unmarshal(data, &config); err != nil {
|
||
return RuleInfo{}, err
|
||
}
|
||
|
||
info := RuleInfo{
|
||
FilePath: filePath,
|
||
Enabled: true,
|
||
}
|
||
|
||
// 从文件名提取规则名
|
||
baseName := filepath.Base(filePath)
|
||
info.Name = strings.TrimSuffix(baseName, ".yaml")
|
||
info.Name = strings.TrimPrefix(info.Name, "example-")
|
||
|
||
// 解析插件配置
|
||
for _, plugin := range config.Plugins {
|
||
if plugin.Type == "domain_set" {
|
||
if args, ok := plugin.Args.(map[string]interface{}); ok {
|
||
if files, ok := args["files"].([]interface{}); ok && len(files) > 0 {
|
||
if file, ok := files[0].(string); ok {
|
||
info.DomainFile = file
|
||
}
|
||
}
|
||
}
|
||
} else if plugin.Type == "sequence" {
|
||
// 尝试提取 DNS 策略
|
||
if args, ok := plugin.Args.([]interface{}); ok {
|
||
for _, arg := range args {
|
||
if argMap, ok := arg.(map[string]interface{}); ok {
|
||
if exec, ok := argMap["exec"].(string); ok {
|
||
if strings.HasPrefix(exec, "$") {
|
||
dnsTag := strings.TrimPrefix(exec, "$")
|
||
info.DNSStrategy = m.getDNSStrategyName(dnsTag)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else if plugin.Type == "mikrotik_addresslist" {
|
||
info.EnableMikrotik = true
|
||
if args, ok := plugin.Args.(map[string]interface{}); ok {
|
||
if host, ok := args["host"].(string); ok {
|
||
if port, ok := args["port"].(int); ok {
|
||
info.MikrotikDevice = fmt.Sprintf("%s:%d", host, port)
|
||
} else {
|
||
info.MikrotikDevice = fmt.Sprintf("%s:9728", host)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return info, nil
|
||
}
|
||
|
||
// parseRuleFileToConfig 解析规则文件为完整配置
|
||
func (m *Mosdns) parseRuleFileToConfig(filePath string) (RuleConfig, error) {
|
||
data, err := os.ReadFile(filePath)
|
||
if err != nil {
|
||
return RuleConfig{}, err
|
||
}
|
||
|
||
var yamlConfig struct {
|
||
Plugins []PluginConfig `yaml:"plugins"`
|
||
}
|
||
|
||
if err := yaml.Unmarshal(data, &yamlConfig); err != nil {
|
||
return RuleConfig{}, err
|
||
}
|
||
|
||
config := RuleConfig{
|
||
Enabled: true,
|
||
}
|
||
|
||
// 从文件名提取规则名
|
||
baseName := filepath.Base(filePath)
|
||
config.Name = strings.TrimSuffix(baseName, ".yaml")
|
||
config.Name = strings.TrimPrefix(config.Name, "example-")
|
||
|
||
// 解析插件配置
|
||
for _, plugin := range yamlConfig.Plugins {
|
||
if plugin.Type == "domain_set" {
|
||
if args, ok := plugin.Args.(map[string]interface{}); ok {
|
||
if files, ok := args["files"].([]interface{}); ok && len(files) > 0 {
|
||
if file, ok := files[0].(string); ok {
|
||
config.DomainFile = file
|
||
}
|
||
}
|
||
}
|
||
} else if plugin.Type == "mikrotik_addresslist" {
|
||
config.EnableMikrotik = true
|
||
if args, ok := plugin.Args.(map[string]interface{}); ok {
|
||
config.MikrotikConfig = MikrotikConfig{
|
||
Host: getStringValue(args, "host"),
|
||
Port: getIntValue(args, "port"),
|
||
Username: getStringValue(args, "username"),
|
||
Password: getStringValue(args, "password"),
|
||
AddressList: getStringValue(args, "address_list4"),
|
||
Mask: getIntValue(args, "mask4"),
|
||
MaxIPs: getIntValue(args, "max_ips"),
|
||
CacheTTL: getIntValue(args, "cache_ttl"),
|
||
TimeoutAddr: getIntValue(args, "timeout_addr"),
|
||
Comment: getStringValue(args, "comment"),
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return config, nil
|
||
}
|
||
|
||
// getDNSStrategyName 将 DNS 标签转换为策略名称
|
||
func (m *Mosdns) getDNSStrategyName(dnsTag string) string {
|
||
switch dnsTag {
|
||
case "china-dns":
|
||
return "china"
|
||
case "overseas-dns-cloudflare":
|
||
return "cloudflare"
|
||
case "overseas-dns-google":
|
||
return "google"
|
||
case "hybrid-dns":
|
||
return "hybrid"
|
||
case "smart_anti_pollution":
|
||
return "anti-pollution"
|
||
default:
|
||
return "china"
|
||
}
|
||
}
|
||
|
||
// getStringValue 和 getIntValue 已在 config_builder.go 中定义
|