1package config 2 3import ( 4 "errors" 5 "fmt" 6 "net" 7 "net/url" 8 "os" 9 "strings" 10 11 "github.com/Dreamacro/clash/adapters/outbound" 12 "github.com/Dreamacro/clash/adapters/outboundgroup" 13 "github.com/Dreamacro/clash/adapters/provider" 14 "github.com/Dreamacro/clash/component/auth" 15 "github.com/Dreamacro/clash/component/fakeip" 16 "github.com/Dreamacro/clash/component/trie" 17 C "github.com/Dreamacro/clash/constant" 18 "github.com/Dreamacro/clash/dns" 19 "github.com/Dreamacro/clash/log" 20 R "github.com/Dreamacro/clash/rules" 21 T "github.com/Dreamacro/clash/tunnel" 22 23 yaml "gopkg.in/yaml.v2" 24) 25 26// General config 27type General struct { 28 Inbound 29 Controller 30 Mode T.TunnelMode `json:"mode"` 31 LogLevel log.LogLevel `json:"log-level"` 32 IPv6 bool `json:"ipv6"` 33 Interface string `json:"-"` 34} 35 36// Inbound 37type Inbound struct { 38 Port int `json:"port"` 39 SocksPort int `json:"socks-port"` 40 RedirPort int `json:"redir-port"` 41 TProxyPort int `json:"tproxy-port"` 42 MixedPort int `json:"mixed-port"` 43 Authentication []string `json:"authentication"` 44 AllowLan bool `json:"allow-lan"` 45 BindAddress string `json:"bind-address"` 46} 47 48// Controller 49type Controller struct { 50 ExternalController string `json:"-"` 51 ExternalUI string `json:"-"` 52 Secret string `json:"-"` 53} 54 55// DNS config 56type DNS struct { 57 Enable bool `yaml:"enable"` 58 IPv6 bool `yaml:"ipv6"` 59 NameServer []dns.NameServer `yaml:"nameserver"` 60 Fallback []dns.NameServer `yaml:"fallback"` 61 FallbackFilter FallbackFilter `yaml:"fallback-filter"` 62 Listen string `yaml:"listen"` 63 EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"` 64 DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` 65 FakeIPRange *fakeip.Pool 66 Hosts *trie.DomainTrie 67} 68 69// FallbackFilter config 70type FallbackFilter struct { 71 GeoIP bool `yaml:"geoip"` 72 IPCIDR []*net.IPNet `yaml:"ipcidr"` 73 Domain []string `yaml:"domain"` 74} 75 76// Profile config 77type Profile struct { 78 StoreSelected bool `yaml:"store-selected"` 79} 80 81// Experimental config 82type Experimental struct{} 83 84// Config is clash config manager 85type Config struct { 86 General *General 87 DNS *DNS 88 Experimental *Experimental 89 Hosts *trie.DomainTrie 90 Profile *Profile 91 Rules []C.Rule 92 Users []auth.AuthUser 93 Proxies map[string]C.Proxy 94 Providers map[string]provider.ProxyProvider 95} 96 97type RawDNS struct { 98 Enable bool `yaml:"enable"` 99 IPv6 bool `yaml:"ipv6"` 100 UseHosts bool `yaml:"use-hosts"` 101 NameServer []string `yaml:"nameserver"` 102 Fallback []string `yaml:"fallback"` 103 FallbackFilter RawFallbackFilter `yaml:"fallback-filter"` 104 Listen string `yaml:"listen"` 105 EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"` 106 FakeIPRange string `yaml:"fake-ip-range"` 107 FakeIPFilter []string `yaml:"fake-ip-filter"` 108 DefaultNameserver []string `yaml:"default-nameserver"` 109} 110 111type RawFallbackFilter struct { 112 GeoIP bool `yaml:"geoip"` 113 IPCIDR []string `yaml:"ipcidr"` 114 Domain []string `yaml:"domain"` 115} 116 117type RawConfig struct { 118 Port int `yaml:"port"` 119 SocksPort int `yaml:"socks-port"` 120 RedirPort int `yaml:"redir-port"` 121 TProxyPort int `yaml:"tproxy-port"` 122 MixedPort int `yaml:"mixed-port"` 123 Authentication []string `yaml:"authentication"` 124 AllowLan bool `yaml:"allow-lan"` 125 BindAddress string `yaml:"bind-address"` 126 Mode T.TunnelMode `yaml:"mode"` 127 LogLevel log.LogLevel `yaml:"log-level"` 128 IPv6 bool `yaml:"ipv6"` 129 ExternalController string `yaml:"external-controller"` 130 ExternalUI string `yaml:"external-ui"` 131 Secret string `yaml:"secret"` 132 Interface string `yaml:"interface-name"` 133 134 ProxyProvider map[string]map[string]interface{} `yaml:"proxy-providers"` 135 Hosts map[string]string `yaml:"hosts"` 136 DNS RawDNS `yaml:"dns"` 137 Experimental Experimental `yaml:"experimental"` 138 Profile Profile `yaml:"profile"` 139 Proxy []map[string]interface{} `yaml:"proxies"` 140 ProxyGroup []map[string]interface{} `yaml:"proxy-groups"` 141 Rule []string `yaml:"rules"` 142} 143 144// Parse config 145func Parse(buf []byte) (*Config, error) { 146 rawCfg, err := UnmarshalRawConfig(buf) 147 if err != nil { 148 return nil, err 149 } 150 151 return ParseRawConfig(rawCfg) 152} 153 154func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { 155 // config with default value 156 rawCfg := &RawConfig{ 157 AllowLan: false, 158 BindAddress: "*", 159 Mode: T.Rule, 160 Authentication: []string{}, 161 LogLevel: log.INFO, 162 Hosts: map[string]string{}, 163 Rule: []string{}, 164 Proxy: []map[string]interface{}{}, 165 ProxyGroup: []map[string]interface{}{}, 166 DNS: RawDNS{ 167 Enable: false, 168 UseHosts: true, 169 FakeIPRange: "198.18.0.1/16", 170 FallbackFilter: RawFallbackFilter{ 171 GeoIP: true, 172 IPCIDR: []string{}, 173 }, 174 DefaultNameserver: []string{ 175 "114.114.114.114", 176 "8.8.8.8", 177 }, 178 }, 179 Profile: Profile{ 180 StoreSelected: true, 181 }, 182 } 183 184 if err := yaml.Unmarshal(buf, &rawCfg); err != nil { 185 return nil, err 186 } 187 188 return rawCfg, nil 189} 190 191func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { 192 config := &Config{} 193 194 config.Experimental = &rawCfg.Experimental 195 config.Profile = &rawCfg.Profile 196 197 general, err := parseGeneral(rawCfg) 198 if err != nil { 199 return nil, err 200 } 201 config.General = general 202 203 proxies, providers, err := parseProxies(rawCfg) 204 if err != nil { 205 return nil, err 206 } 207 config.Proxies = proxies 208 config.Providers = providers 209 210 rules, err := parseRules(rawCfg, proxies) 211 if err != nil { 212 return nil, err 213 } 214 config.Rules = rules 215 216 hosts, err := parseHosts(rawCfg) 217 if err != nil { 218 return nil, err 219 } 220 config.Hosts = hosts 221 222 dnsCfg, err := parseDNS(rawCfg.DNS, hosts) 223 if err != nil { 224 return nil, err 225 } 226 config.DNS = dnsCfg 227 228 config.Users = parseAuthentication(rawCfg.Authentication) 229 230 return config, nil 231} 232 233func parseGeneral(cfg *RawConfig) (*General, error) { 234 externalUI := cfg.ExternalUI 235 236 // checkout externalUI exist 237 if externalUI != "" { 238 externalUI = C.Path.Resolve(externalUI) 239 240 if _, err := os.Stat(externalUI); os.IsNotExist(err) { 241 return nil, fmt.Errorf("external-ui: %s not exist", externalUI) 242 } 243 } 244 245 return &General{ 246 Inbound: Inbound{ 247 Port: cfg.Port, 248 SocksPort: cfg.SocksPort, 249 RedirPort: cfg.RedirPort, 250 TProxyPort: cfg.TProxyPort, 251 MixedPort: cfg.MixedPort, 252 AllowLan: cfg.AllowLan, 253 BindAddress: cfg.BindAddress, 254 }, 255 Controller: Controller{ 256 ExternalController: cfg.ExternalController, 257 ExternalUI: cfg.ExternalUI, 258 Secret: cfg.Secret, 259 }, 260 Mode: cfg.Mode, 261 LogLevel: cfg.LogLevel, 262 IPv6: cfg.IPv6, 263 Interface: cfg.Interface, 264 }, nil 265} 266 267func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]provider.ProxyProvider, err error) { 268 proxies = make(map[string]C.Proxy) 269 providersMap = make(map[string]provider.ProxyProvider) 270 proxyList := []string{} 271 proxiesConfig := cfg.Proxy 272 groupsConfig := cfg.ProxyGroup 273 providersConfig := cfg.ProxyProvider 274 275 proxies["DIRECT"] = outbound.NewProxy(outbound.NewDirect()) 276 proxies["REJECT"] = outbound.NewProxy(outbound.NewReject()) 277 proxyList = append(proxyList, "DIRECT", "REJECT") 278 279 // parse proxy 280 for idx, mapping := range proxiesConfig { 281 proxy, err := outbound.ParseProxy(mapping) 282 if err != nil { 283 return nil, nil, fmt.Errorf("proxy %d: %w", idx, err) 284 } 285 286 if _, exist := proxies[proxy.Name()]; exist { 287 return nil, nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name()) 288 } 289 proxies[proxy.Name()] = proxy 290 proxyList = append(proxyList, proxy.Name()) 291 } 292 293 // keep the original order of ProxyGroups in config file 294 for idx, mapping := range groupsConfig { 295 groupName, existName := mapping["name"].(string) 296 if !existName { 297 return nil, nil, fmt.Errorf("proxy group %d: missing name", idx) 298 } 299 proxyList = append(proxyList, groupName) 300 } 301 302 // check if any loop exists and sort the ProxyGroups 303 if err := proxyGroupsDagSort(groupsConfig); err != nil { 304 return nil, nil, err 305 } 306 307 // parse and initial providers 308 for name, mapping := range providersConfig { 309 if name == provider.ReservedName { 310 return nil, nil, fmt.Errorf("can not defined a provider called `%s`", provider.ReservedName) 311 } 312 313 pd, err := provider.ParseProxyProvider(name, mapping) 314 if err != nil { 315 return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err) 316 } 317 318 providersMap[name] = pd 319 } 320 321 for _, provider := range providersMap { 322 log.Infoln("Start initial provider %s", provider.Name()) 323 if err := provider.Initial(); err != nil { 324 return nil, nil, fmt.Errorf("initial proxy provider %s error: %w", provider.Name(), err) 325 } 326 } 327 328 // parse proxy group 329 for idx, mapping := range groupsConfig { 330 group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap) 331 if err != nil { 332 return nil, nil, fmt.Errorf("proxy group[%d]: %w", idx, err) 333 } 334 335 groupName := group.Name() 336 if _, exist := proxies[groupName]; exist { 337 return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName) 338 } 339 340 proxies[groupName] = outbound.NewProxy(group) 341 } 342 343 // initial compatible provider 344 for _, pd := range providersMap { 345 if pd.VehicleType() != provider.Compatible { 346 continue 347 } 348 349 log.Infoln("Start initial compatible provider %s", pd.Name()) 350 if err := pd.Initial(); err != nil { 351 return nil, nil, err 352 } 353 } 354 355 ps := []C.Proxy{} 356 for _, v := range proxyList { 357 ps = append(ps, proxies[v]) 358 } 359 hc := provider.NewHealthCheck(ps, "", 0, true) 360 pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc) 361 providersMap[provider.ReservedName] = pd 362 363 global := outboundgroup.NewSelector( 364 &outboundgroup.GroupCommonOption{ 365 Name: "GLOBAL", 366 }, 367 []provider.ProxyProvider{pd}, 368 ) 369 proxies["GLOBAL"] = outbound.NewProxy(global) 370 return proxies, providersMap, nil 371} 372 373func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) { 374 rules := []C.Rule{} 375 rulesConfig := cfg.Rule 376 377 // parse rules 378 for idx, line := range rulesConfig { 379 rule := trimArr(strings.Split(line, ",")) 380 var ( 381 payload string 382 target string 383 params = []string{} 384 ) 385 386 switch l := len(rule); { 387 case l == 2: 388 target = rule[1] 389 case l == 3: 390 payload = rule[1] 391 target = rule[2] 392 case l >= 4: 393 payload = rule[1] 394 target = rule[2] 395 params = rule[3:] 396 default: 397 return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line) 398 } 399 400 if _, ok := proxies[target]; !ok { 401 return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) 402 } 403 404 rule = trimArr(rule) 405 params = trimArr(params) 406 407 parsed, parseErr := R.ParseRule(rule[0], payload, target, params) 408 if parseErr != nil { 409 return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error()) 410 } 411 412 rules = append(rules, parsed) 413 } 414 415 return rules, nil 416} 417 418func parseHosts(cfg *RawConfig) (*trie.DomainTrie, error) { 419 tree := trie.New() 420 421 // add default hosts 422 if err := tree.Insert("localhost", net.IP{127, 0, 0, 1}); err != nil { 423 log.Errorln("insert localhost to host error: %s", err.Error()) 424 } 425 426 if len(cfg.Hosts) != 0 { 427 for domain, ipStr := range cfg.Hosts { 428 ip := net.ParseIP(ipStr) 429 if ip == nil { 430 return nil, fmt.Errorf("%s is not a valid IP", ipStr) 431 } 432 tree.Insert(domain, ip) 433 } 434 } 435 436 return tree, nil 437} 438 439func hostWithDefaultPort(host string, defPort string) (string, error) { 440 if !strings.Contains(host, ":") { 441 host += ":" 442 } 443 444 hostname, port, err := net.SplitHostPort(host) 445 if err != nil { 446 return "", err 447 } 448 449 if port == "" { 450 port = defPort 451 } 452 453 return net.JoinHostPort(hostname, port), nil 454} 455 456func parseNameServer(servers []string) ([]dns.NameServer, error) { 457 nameservers := []dns.NameServer{} 458 459 for idx, server := range servers { 460 // parse without scheme .e.g 8.8.8.8:53 461 if !strings.Contains(server, "://") { 462 server = "udp://" + server 463 } 464 u, err := url.Parse(server) 465 if err != nil { 466 return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) 467 } 468 469 var addr, dnsNetType string 470 switch u.Scheme { 471 case "udp": 472 addr, err = hostWithDefaultPort(u.Host, "53") 473 dnsNetType = "" // UDP 474 case "tcp": 475 addr, err = hostWithDefaultPort(u.Host, "53") 476 dnsNetType = "tcp" // TCP 477 case "tls": 478 addr, err = hostWithDefaultPort(u.Host, "853") 479 dnsNetType = "tcp-tls" // DNS over TLS 480 case "https": 481 clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path} 482 addr = clearURL.String() 483 dnsNetType = "https" // DNS over HTTPS 484 default: 485 return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme) 486 } 487 488 if err != nil { 489 return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) 490 } 491 492 nameservers = append( 493 nameservers, 494 dns.NameServer{ 495 Net: dnsNetType, 496 Addr: addr, 497 }, 498 ) 499 } 500 return nameservers, nil 501} 502 503func parseFallbackIPCIDR(ips []string) ([]*net.IPNet, error) { 504 ipNets := []*net.IPNet{} 505 506 for idx, ip := range ips { 507 _, ipnet, err := net.ParseCIDR(ip) 508 if err != nil { 509 return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %s", idx, err.Error()) 510 } 511 ipNets = append(ipNets, ipnet) 512 } 513 514 return ipNets, nil 515} 516 517func parseDNS(cfg RawDNS, hosts *trie.DomainTrie) (*DNS, error) { 518 if cfg.Enable && len(cfg.NameServer) == 0 { 519 return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") 520 } 521 522 dnsCfg := &DNS{ 523 Enable: cfg.Enable, 524 Listen: cfg.Listen, 525 IPv6: cfg.IPv6, 526 EnhancedMode: cfg.EnhancedMode, 527 FallbackFilter: FallbackFilter{ 528 IPCIDR: []*net.IPNet{}, 529 }, 530 } 531 var err error 532 if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer); err != nil { 533 return nil, err 534 } 535 536 if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback); err != nil { 537 return nil, err 538 } 539 540 if len(cfg.DefaultNameserver) == 0 { 541 return nil, errors.New("default nameserver should have at least one nameserver") 542 } 543 if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver); err != nil { 544 return nil, err 545 } 546 // check default nameserver is pure ip addr 547 for _, ns := range dnsCfg.DefaultNameserver { 548 host, _, err := net.SplitHostPort(ns.Addr) 549 if err != nil || net.ParseIP(host) == nil { 550 return nil, errors.New("default nameserver should be pure IP") 551 } 552 } 553 554 if cfg.EnhancedMode == dns.FAKEIP { 555 _, ipnet, err := net.ParseCIDR(cfg.FakeIPRange) 556 if err != nil { 557 return nil, err 558 } 559 560 var host *trie.DomainTrie 561 // fake ip skip host filter 562 if len(cfg.FakeIPFilter) != 0 { 563 host = trie.New() 564 for _, domain := range cfg.FakeIPFilter { 565 host.Insert(domain, true) 566 } 567 } 568 569 pool, err := fakeip.New(ipnet, 1000, host) 570 if err != nil { 571 return nil, err 572 } 573 574 dnsCfg.FakeIPRange = pool 575 } 576 577 dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP 578 if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil { 579 dnsCfg.FallbackFilter.IPCIDR = fallbackip 580 } 581 dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain 582 583 if cfg.UseHosts { 584 dnsCfg.Hosts = hosts 585 } 586 587 return dnsCfg, nil 588} 589 590func parseAuthentication(rawRecords []string) []auth.AuthUser { 591 users := make([]auth.AuthUser, 0) 592 for _, line := range rawRecords { 593 userData := strings.SplitN(line, ":", 2) 594 if len(userData) == 2 { 595 users = append(users, auth.AuthUser{User: userData[0], Pass: userData[1]}) 596 } 597 } 598 return users 599} 600