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