package resolver import ( "os" "strings" ) type DNSConfig struct { Default []string Meta []string SmartDNS string Mode string } type DNSConfigDeps struct { ActivePool []string IsSmartDNSForced bool SmartDNSAddr string SmartDNSForcedMode string ResolveFallbackPool func() []string MergeDNSUpstreamPools func(primary, fallback []string) []string NormalizeDNSUpstream func(raw string, defaultPort string) string NormalizeSmartDNSAddr func(raw string) string NormalizeDNSResolverMode func(raw string) string } func LoadDNSConfig(path string, base DNSConfig, deps DNSConfigDeps, logf func(string, ...any)) DNSConfig { cfg := DNSConfig{ Default: append([]string(nil), base.Default...), Meta: append([]string(nil), base.Meta...), SmartDNS: strings.TrimSpace(base.SmartDNS), Mode: strings.TrimSpace(base.Mode), } if cfg.Mode == "" { cfg.Mode = "direct" } if len(deps.ActivePool) > 0 { cfg.Default = append([]string(nil), deps.ActivePool...) cfg.Meta = append([]string(nil), deps.ActivePool...) } if deps.IsSmartDNSForced { addr := strings.TrimSpace(deps.SmartDNSAddr) if deps.NormalizeSmartDNSAddr != nil { if n := deps.NormalizeSmartDNSAddr(addr); n != "" { addr = n } } if addr == "" { addr = cfg.SmartDNS } cfg.Default = []string{addr} cfg.Meta = []string{addr} cfg.SmartDNS = addr if strings.TrimSpace(deps.SmartDNSForcedMode) != "" { cfg.Mode = deps.SmartDNSForcedMode } else { cfg.Mode = "smartdns" } if logf != nil { logf("dns-config: SmartDNS forced (%s), ignore %s", addr, path) } return cfg } data, err := os.ReadFile(path) if err != nil { if logf != nil { logf("dns-config: can't read %s: %v", path, err) } fallback := []string(nil) if deps.ResolveFallbackPool != nil { fallback = deps.ResolveFallbackPool() } if deps.MergeDNSUpstreamPools != nil { cfg.Default = deps.MergeDNSUpstreamPools(cfg.Default, fallback) cfg.Meta = deps.MergeDNSUpstreamPools(cfg.Meta, fallback) } return cfg } var def, meta []string lines := strings.Split(string(data), "\n") for _, ln := range lines { s := strings.TrimSpace(ln) if s == "" || strings.HasPrefix(s, "#") { continue } parts := strings.Fields(s) if len(parts) < 2 { continue } key := strings.ToLower(parts[0]) vals := parts[1:] switch key { case "default": for _, v := range vals { if deps.NormalizeDNSUpstream != nil { if n := deps.NormalizeDNSUpstream(v, "53"); n != "" { def = append(def, n) } } } case "meta": for _, v := range vals { if deps.NormalizeDNSUpstream != nil { if n := deps.NormalizeDNSUpstream(v, "53"); n != "" { meta = append(meta, n) } } } case "smartdns": if len(vals) > 0 && deps.NormalizeSmartDNSAddr != nil { if n := deps.NormalizeSmartDNSAddr(vals[0]); n != "" { cfg.SmartDNS = n } } case "mode": if len(vals) > 0 { rawMode := vals[0] if deps.NormalizeDNSResolverMode != nil { cfg.Mode = deps.NormalizeDNSResolverMode(rawMode) } else { cfg.Mode = strings.ToLower(strings.TrimSpace(rawMode)) } } } } if len(deps.ActivePool) == 0 { if len(def) > 0 { cfg.Default = def } if len(meta) > 0 { cfg.Meta = meta } } fallback := []string(nil) if deps.ResolveFallbackPool != nil { fallback = deps.ResolveFallbackPool() } if deps.MergeDNSUpstreamPools != nil { cfg.Default = deps.MergeDNSUpstreamPools(cfg.Default, fallback) cfg.Meta = deps.MergeDNSUpstreamPools(cfg.Meta, fallback) } if logf != nil { logf("dns-config: accept %s: mode=%s smartdns=%s default=%v; meta=%v", path, cfg.Mode, cfg.SmartDNS, cfg.Default, cfg.Meta) } return cfg }