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