github.com/yaling888/clash@v1.53.0/config/config.go (about)

     1  package config
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"net/netip"
     8  	"net/url"
     9  	"os"
    10  	"regexp"
    11  	"runtime"
    12  	"strings"
    13  
    14  	"github.com/phuslu/log"
    15  	"github.com/samber/lo"
    16  	"gopkg.in/yaml.v3"
    17  
    18  	"github.com/yaling888/clash/adapter"
    19  	"github.com/yaling888/clash/adapter/outbound"
    20  	"github.com/yaling888/clash/adapter/outboundgroup"
    21  	"github.com/yaling888/clash/adapter/provider"
    22  	"github.com/yaling888/clash/component/auth"
    23  	"github.com/yaling888/clash/component/fakeip"
    24  	"github.com/yaling888/clash/component/geodata"
    25  	"github.com/yaling888/clash/component/geodata/router"
    26  	_ "github.com/yaling888/clash/component/geodata/standard"
    27  	"github.com/yaling888/clash/component/resolver"
    28  	S "github.com/yaling888/clash/component/script"
    29  	"github.com/yaling888/clash/component/trie"
    30  	C "github.com/yaling888/clash/constant"
    31  	providerTypes "github.com/yaling888/clash/constant/provider"
    32  	"github.com/yaling888/clash/dns"
    33  	L "github.com/yaling888/clash/log"
    34  	"github.com/yaling888/clash/mitm"
    35  	R "github.com/yaling888/clash/rule"
    36  	T "github.com/yaling888/clash/tunnel"
    37  )
    38  
    39  // General config
    40  type General struct {
    41  	LegacyInbound
    42  	Controller
    43  	Authentication []string     `json:"authentication"`
    44  	Mode           T.TunnelMode `json:"mode"`
    45  	LogLevel       L.LogLevel   `json:"log-level"`
    46  	IPv6           bool         `json:"ipv6"`
    47  	Sniffing       bool         `json:"sniffing"`
    48  	Interface      string       `json:"-"`
    49  	RoutingMark    int          `json:"-"`
    50  	Tun            C.Tun        `json:"tun"`
    51  	EBpf           EBpf         `json:"-"`
    52  }
    53  
    54  // LegacyInbound config
    55  type LegacyInbound struct {
    56  	Port        int    `json:"port"`
    57  	SocksPort   int    `json:"socks-port"`
    58  	RedirPort   int    `json:"redir-port"`
    59  	TProxyPort  int    `json:"tproxy-port"`
    60  	MixedPort   int    `json:"mixed-port"`
    61  	MitmPort    int    `json:"mitm-port"`
    62  	AllowLan    bool   `json:"allow-lan"`
    63  	BindAddress string `json:"bind-address"`
    64  }
    65  
    66  // Controller config
    67  type Controller struct {
    68  	ExternalController string `json:"-"`
    69  	ExternalUI         string `json:"-"`
    70  	Secret             string `json:"-"`
    71  	PPROF              bool   `json:"-"`
    72  }
    73  
    74  // DNS config
    75  type DNS struct {
    76  	Enable                bool             `yaml:"enable"`
    77  	IPv6                  bool             `yaml:"ipv6"`
    78  	RemoteDnsResolve      bool             `yaml:"remote-dns-resolve"`
    79  	NameServer            []dns.NameServer `yaml:"nameserver"`
    80  	Fallback              []dns.NameServer `yaml:"fallback"`
    81  	ProxyServerNameserver []dns.NameServer `yaml:"proxy-server-nameserver"`
    82  	RemoteNameserver      []dns.NameServer `yaml:"remote-nameserver"`
    83  	FallbackFilter        FallbackFilter   `yaml:"fallback-filter"`
    84  	Listen                string           `yaml:"listen"`
    85  	EnhancedMode          C.DNSMode        `yaml:"enhanced-mode"`
    86  	DefaultNameserver     []dns.NameServer `yaml:"default-nameserver"`
    87  	FakeIPRange           *fakeip.Pool
    88  	Hosts                 *trie.DomainTrie[netip.Addr]
    89  	NameServerPolicy      map[string]dns.NameServer
    90  	SearchDomains         []string
    91  }
    92  
    93  // FallbackFilter config
    94  type FallbackFilter struct {
    95  	GeoIP     bool                    `yaml:"geoip"`
    96  	GeoIPCode string                  `yaml:"geoip-code"`
    97  	IPCIDR    []*netip.Prefix         `yaml:"ipcidr"`
    98  	Domain    []string                `yaml:"domain"`
    99  	GeoSite   []*router.DomainMatcher `yaml:"geosite"`
   100  }
   101  
   102  // Profile config
   103  type Profile struct {
   104  	StoreSelected bool `yaml:"store-selected"`
   105  	StoreFakeIP   bool `yaml:"store-fake-ip"`
   106  	Tracing       bool `yaml:"tracing"`
   107  }
   108  
   109  // Script config
   110  type Script struct {
   111  	Engine        string            `yaml:"engine" json:"engine"`
   112  	MainCode      string            `yaml:"code" json:"code"`
   113  	MainPath      string            `yaml:"path" json:"path"`
   114  	ShortcutsCode map[string]string `yaml:"shortcuts" json:"shortcuts"`
   115  }
   116  
   117  // Mitm config
   118  type Mitm struct {
   119  	Hosts *trie.DomainTrie[bool] `yaml:"hosts" json:"hosts"`
   120  	Rules C.RewriteRule          `yaml:"rules" json:"rules"`
   121  }
   122  
   123  // EBpf config
   124  type EBpf struct {
   125  	RedirectToTun []string `yaml:"redirect-to-tun" json:"redirect-to-tun"`
   126  	AutoRedir     []string `yaml:"auto-redir" json:"auto-redir"`
   127  }
   128  
   129  // Experimental config
   130  type Experimental struct {
   131  	UDPFallbackMatch  bool   `yaml:"udp-fallback-match"`
   132  	UDPFallbackPolicy string `yaml:"udp-fallback-policy"`
   133  }
   134  
   135  // Config is clash config manager
   136  type Config struct {
   137  	General       *General
   138  	Mitm          *Mitm
   139  	DNS           *DNS
   140  	Experimental  *Experimental
   141  	Hosts         *trie.DomainTrie[netip.Addr]
   142  	Profile       *Profile
   143  	Inbounds      []C.Inbound
   144  	Rules         []C.Rule
   145  	RuleProviders map[string]C.Rule
   146  	Users         []auth.AuthUser
   147  	Proxies       map[string]C.Proxy
   148  	Providers     map[string]providerTypes.ProxyProvider
   149  	MainMatcher   C.Matcher
   150  	Tunnels       []Tunnel
   151  }
   152  
   153  type RawDNS struct {
   154  	Enable                bool              `yaml:"enable"`
   155  	IPv6                  *bool             `yaml:"ipv6"`
   156  	RemoteDnsResolve      bool              `yaml:"remote-dns-resolve"`
   157  	UseHosts              bool              `yaml:"use-hosts"`
   158  	NameServer            []string          `yaml:"nameserver"`
   159  	Fallback              []string          `yaml:"fallback"`
   160  	FallbackFilter        RawFallbackFilter `yaml:"fallback-filter"`
   161  	Listen                string            `yaml:"listen"`
   162  	EnhancedMode          C.DNSMode         `yaml:"enhanced-mode"`
   163  	FakeIPRange           string            `yaml:"fake-ip-range"`
   164  	FakeIPFilter          []string          `yaml:"fake-ip-filter"`
   165  	DefaultNameserver     []string          `yaml:"default-nameserver"`
   166  	NameServerPolicy      map[string]string `yaml:"nameserver-policy"`
   167  	ProxyServerNameserver []string          `yaml:"proxy-server-nameserver"`
   168  	SearchDomains         []string          `yaml:"search-domains"`
   169  	RemoteNameserver      []string          `yaml:"remote-nameserver"`
   170  }
   171  
   172  type RawFallbackFilter struct {
   173  	GeoIP     bool     `yaml:"geoip"`
   174  	GeoIPCode string   `yaml:"geoip-code"`
   175  	IPCIDR    []string `yaml:"ipcidr"`
   176  	Domain    []string `yaml:"domain"`
   177  	GeoSite   []string `yaml:"geosite"`
   178  }
   179  
   180  type RawMitm struct {
   181  	Hosts []string `yaml:"hosts" json:"hosts"`
   182  	Rules []string `yaml:"rules" json:"rules"`
   183  }
   184  
   185  type tunnel struct {
   186  	Network []string `yaml:"network"`
   187  	Address string   `yaml:"address"`
   188  	Target  string   `yaml:"target"`
   189  	Proxy   string   `yaml:"proxy"`
   190  }
   191  
   192  type Tunnel tunnel
   193  
   194  // UnmarshalYAML implements yaml.Unmarshaler
   195  func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error {
   196  	var tp string
   197  	if err := unmarshal(&tp); err != nil {
   198  		var inner tunnel
   199  		if err := unmarshal(&inner); err != nil {
   200  			return err
   201  		}
   202  
   203  		*t = Tunnel(inner)
   204  		return nil
   205  	}
   206  
   207  	// parse udp/tcp,address,target,proxy
   208  	parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string {
   209  		return strings.TrimSpace(s)
   210  	})
   211  	if len(parts) != 4 {
   212  		return fmt.Errorf("invalid tunnel config %s", tp)
   213  	}
   214  	network := strings.Split(parts[0], "/")
   215  
   216  	// validate network
   217  	for _, n := range network {
   218  		switch n {
   219  		case "tcp", "udp":
   220  		default:
   221  			return fmt.Errorf("invalid tunnel network %s", n)
   222  		}
   223  	}
   224  
   225  	// validate address and target
   226  	address := parts[1]
   227  	target := parts[2]
   228  	for _, addr := range []string{address, target} {
   229  		if _, _, err := net.SplitHostPort(addr); err != nil {
   230  			return fmt.Errorf("invalid tunnel target or address %s", addr)
   231  		}
   232  	}
   233  
   234  	*t = Tunnel(tunnel{
   235  		Network: network,
   236  		Address: address,
   237  		Target:  target,
   238  		Proxy:   parts[3],
   239  	})
   240  	return nil
   241  }
   242  
   243  type rawRule struct {
   244  	If     string    `yaml:"if"`
   245  	Name   string    `yaml:"name"`
   246  	Engine string    `yaml:"engine"`
   247  	Rules  []RawRule `yaml:"rules"`
   248  	Line   string    `yaml:"-"`
   249  }
   250  
   251  type RawRule rawRule
   252  
   253  // UnmarshalYAML implements yaml.Unmarshaler
   254  func (r *RawRule) UnmarshalYAML(unmarshal func(any) error) error {
   255  	var line string
   256  	if err := unmarshal(&line); err != nil {
   257  		inner := rawRule{
   258  			Engine: "expr",
   259  		}
   260  		if err = unmarshal(&inner); err != nil {
   261  			return err
   262  		}
   263  
   264  		if inner.Name == "" {
   265  			return fmt.Errorf("invalid rule name")
   266  		}
   267  
   268  		if inner.If == "" {
   269  			return fmt.Errorf("invalid rule %s if", inner.Name)
   270  		}
   271  
   272  		if inner.Engine != "expr" && inner.Engine != "starlark" {
   273  			return fmt.Errorf("invalid rule %s engine, got %s, want expr or starlark", inner.Name, inner.Engine)
   274  		}
   275  
   276  		if len(inner.Rules) == 0 {
   277  			return fmt.Errorf("rule %s sub-rules can not be empty", inner.Name)
   278  		}
   279  
   280  		*r = RawRule(inner)
   281  		return nil
   282  	}
   283  
   284  	*r = RawRule(rawRule{
   285  		Line: line,
   286  	})
   287  	return nil
   288  }
   289  
   290  type RawConfig struct {
   291  	Port               int          `yaml:"port"`
   292  	SocksPort          int          `yaml:"socks-port"`
   293  	RedirPort          int          `yaml:"redir-port"`
   294  	TProxyPort         int          `yaml:"tproxy-port"`
   295  	MixedPort          int          `yaml:"mixed-port"`
   296  	MitmPort           int          `yaml:"mitm-port"`
   297  	Authentication     []string     `yaml:"authentication"`
   298  	AllowLan           bool         `yaml:"allow-lan"`
   299  	BindAddress        string       `yaml:"bind-address"`
   300  	Mode               T.TunnelMode `yaml:"mode"`
   301  	LogLevel           L.LogLevel   `yaml:"log-level"`
   302  	IPv6               bool         `yaml:"ipv6"`
   303  	ExternalController string       `yaml:"external-controller"`
   304  	ExternalUI         string       `yaml:"external-ui"`
   305  	Secret             string       `yaml:"secret"`
   306  	PPROF              bool         `yaml:"pprof"`
   307  	Interface          string       `yaml:"interface-name"`
   308  	RoutingMark        int          `yaml:"routing-mark"`
   309  	Sniffing           bool         `yaml:"sniffing"`
   310  	ForceCertVerify    bool         `yaml:"force-cert-verify"`
   311  	Tunnels            []Tunnel     `yaml:"tunnels"`
   312  
   313  	ProxyProvider map[string]map[string]any `yaml:"proxy-providers"`
   314  	Hosts         map[string]string         `yaml:"hosts"`
   315  	Inbounds      []C.Inbound               `yaml:"inbounds"`
   316  	DNS           RawDNS                    `yaml:"dns"`
   317  	Tun           C.Tun                     `yaml:"tun"`
   318  	MITM          RawMitm                   `yaml:"mitm"`
   319  	Experimental  Experimental              `yaml:"experimental"`
   320  	Profile       Profile                   `yaml:"profile"`
   321  	Proxy         []C.RawProxy              `yaml:"proxies"`
   322  	ProxyGroup    []map[string]any          `yaml:"proxy-groups"`
   323  	Rule          []RawRule                 `yaml:"rules"`
   324  	Script        Script                    `yaml:"script"`
   325  	EBpf          EBpf                      `yaml:"ebpf"`
   326  }
   327  
   328  // Parse config
   329  func Parse(buf []byte) (*Config, error) {
   330  	rawCfg, err := UnmarshalRawConfig(buf)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  
   335  	return ParseRawConfig(rawCfg)
   336  }
   337  
   338  func UnmarshalRawConfig(buf []byte) (*RawConfig, error) {
   339  	// config with default value
   340  	rawCfg := &RawConfig{
   341  		AllowLan:        false,
   342  		Sniffing:        false,
   343  		ForceCertVerify: false,
   344  		BindAddress:     "*",
   345  		Mode:            T.Rule,
   346  		Authentication:  []string{},
   347  		LogLevel:        L.INFO,
   348  		Hosts:           map[string]string{},
   349  		Rule:            []RawRule{},
   350  		Proxy:           []C.RawProxy{},
   351  		ProxyGroup:      []map[string]any{},
   352  		Tun: C.Tun{
   353  			Enable: false,
   354  			Device: "",
   355  			Stack:  C.TunGvisor,
   356  			DNSHijack: []C.DNSUrl{ // default hijack all dns lookup
   357  				{
   358  					Network: "udp",
   359  					AddrPort: C.DNSAddrPort{
   360  						AddrPort: netip.MustParseAddrPort("0.0.0.0:53"),
   361  					},
   362  				},
   363  				{
   364  					Network: "tcp",
   365  					AddrPort: C.DNSAddrPort{
   366  						AddrPort: netip.MustParseAddrPort("0.0.0.0:53"),
   367  					},
   368  				},
   369  			},
   370  			AutoRoute:           false,
   371  			AutoDetectInterface: false,
   372  		},
   373  		EBpf: EBpf{
   374  			RedirectToTun: []string{},
   375  			AutoRedir:     []string{},
   376  		},
   377  		DNS: RawDNS{
   378  			Enable:           false,
   379  			UseHosts:         true,
   380  			RemoteDnsResolve: true,
   381  			FakeIPRange:      "198.18.0.1/16",
   382  			FallbackFilter: RawFallbackFilter{
   383  				GeoIP:     true,
   384  				GeoIPCode: "CN",
   385  				IPCIDR:    []string{},
   386  				GeoSite:   []string{},
   387  			},
   388  			DefaultNameserver: []string{
   389  				"114.114.114.114",
   390  				"8.8.8.8",
   391  			},
   392  			RemoteNameserver: []string{
   393  				"tcp://1.1.1.1",
   394  				"tcp://8.8.8.8",
   395  			},
   396  		},
   397  		MITM: RawMitm{
   398  			Hosts: []string{},
   399  			Rules: []string{},
   400  		},
   401  		Profile: Profile{
   402  			StoreSelected: true,
   403  			Tracing:       true,
   404  		},
   405  		Script: Script{
   406  			Engine: "expr",
   407  		},
   408  	}
   409  
   410  	if err := yaml.Unmarshal(buf, rawCfg); err != nil {
   411  		return nil, err
   412  	}
   413  
   414  	return rawCfg, nil
   415  }
   416  
   417  func ParseRawConfig(rawCfg *RawConfig) (config *Config, err error) {
   418  	defer func() {
   419  		if err != nil {
   420  			providerTypes.Cleanup(config.Proxies, config.Providers)
   421  			config = nil
   422  		}
   423  		geodata.CleanGeoSiteCache()
   424  		runtime.GC()
   425  	}()
   426  
   427  	config = &Config{}
   428  
   429  	config.Experimental = &rawCfg.Experimental
   430  	config.Profile = &rawCfg.Profile
   431  
   432  	general, err := parseGeneral(rawCfg)
   433  	if err != nil {
   434  		return
   435  	}
   436  	config.General = general
   437  
   438  	config.Inbounds = rawCfg.Inbounds
   439  
   440  	proxies, providers, err := parseProxies(rawCfg)
   441  	if err != nil {
   442  		return
   443  	}
   444  	config.Proxies = proxies
   445  	config.Providers = providers
   446  
   447  	matchers, rawRules, err := parseScript(rawCfg.Script, rawCfg.Rule)
   448  	if err != nil {
   449  		return
   450  	}
   451  	rawCfg.Rule = rawRules
   452  	config.MainMatcher = matchers["main"]
   453  
   454  	rules, ruleProviders, err := parseRules(rawCfg, proxies, matchers)
   455  	if err != nil {
   456  		return
   457  	}
   458  	config.Rules = rules
   459  	config.RuleProviders = ruleProviders
   460  
   461  	hosts, err := parseHosts(rawCfg)
   462  	if err != nil {
   463  		return
   464  	}
   465  	config.Hosts = hosts
   466  
   467  	dnsCfg, err := parseDNS(rawCfg, hosts)
   468  	if err != nil {
   469  		return
   470  	}
   471  	config.DNS = dnsCfg
   472  
   473  	mitmCfg, err := parseMitm(rawCfg.MITM)
   474  	if err != nil {
   475  		return
   476  	}
   477  	config.Mitm = mitmCfg
   478  
   479  	config.Users = ParseAuthentication(rawCfg.Authentication)
   480  
   481  	config.Tunnels = rawCfg.Tunnels
   482  	// verify tunnels
   483  	for _, t := range config.Tunnels {
   484  		if _, ok := config.Proxies[t.Proxy]; !ok {
   485  			pds := config.Providers
   486  		loop:
   487  			for _, pd := range pds {
   488  				if pd.VehicleType() == providerTypes.Compatible {
   489  					continue
   490  				}
   491  				for _, p := range pd.Proxies() {
   492  					ok = p.Name() == t.Proxy
   493  					if ok {
   494  						break loop
   495  					}
   496  				}
   497  			}
   498  			if !ok {
   499  				err = fmt.Errorf("tunnel proxy %s not found", t.Proxy)
   500  				return
   501  			}
   502  		}
   503  	}
   504  
   505  	err = verifyScriptMatcher(config, matchers)
   506  	return
   507  }
   508  
   509  func parseGeneral(cfg *RawConfig) (*General, error) {
   510  	externalUI := cfg.ExternalUI
   511  
   512  	// checkout externalUI exist
   513  	if externalUI != "" {
   514  		externalUI = C.Path.Resolve(externalUI)
   515  
   516  		if _, err := os.Stat(externalUI); os.IsNotExist(err) {
   517  			return nil, fmt.Errorf("external-ui: %s not exist", externalUI)
   518  		}
   519  	}
   520  
   521  	cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun
   522  
   523  	return &General{
   524  		LegacyInbound: LegacyInbound{
   525  			Port:        cfg.Port,
   526  			SocksPort:   cfg.SocksPort,
   527  			RedirPort:   cfg.RedirPort,
   528  			TProxyPort:  cfg.TProxyPort,
   529  			MixedPort:   cfg.MixedPort,
   530  			MitmPort:    cfg.MitmPort,
   531  			AllowLan:    cfg.AllowLan,
   532  			BindAddress: cfg.BindAddress,
   533  		},
   534  		Controller: Controller{
   535  			ExternalController: cfg.ExternalController,
   536  			ExternalUI:         cfg.ExternalUI,
   537  			Secret:             cfg.Secret,
   538  			PPROF:              cfg.PPROF,
   539  		},
   540  		Mode:        cfg.Mode,
   541  		LogLevel:    cfg.LogLevel,
   542  		IPv6:        cfg.IPv6,
   543  		Interface:   cfg.Interface,
   544  		RoutingMark: cfg.RoutingMark,
   545  		Sniffing:    cfg.Sniffing,
   546  		Tun:         cfg.Tun,
   547  		EBpf:        cfg.EBpf,
   548  	}, nil
   549  }
   550  
   551  func parseProxies(cfg *RawConfig) (proxiesMap map[string]C.Proxy, pdsMap map[string]providerTypes.ProxyProvider, err error) {
   552  	proxies := make(map[string]C.Proxy)
   553  	providersMap := make(map[string]providerTypes.ProxyProvider)
   554  	proxiesConfig := cfg.Proxy
   555  	groupsConfig := cfg.ProxyGroup
   556  	providersConfig := cfg.ProxyProvider
   557  	forceCertVerify := cfg.ForceCertVerify
   558  
   559  	var proxyList []string
   560  
   561  	proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect())
   562  	proxies["REJECT"] = adapter.NewProxy(outbound.NewReject())
   563  	proxyList = append(proxyList, "DIRECT", "REJECT")
   564  
   565  	defer func() {
   566  		if err != nil {
   567  			providerTypes.Cleanup(proxies, providersMap)
   568  		}
   569  	}()
   570  
   571  	// parse proxy
   572  	for idx, mapping := range proxiesConfig {
   573  		mapping.Init()
   574  		proxy, err := adapter.ParseProxy(mapping.M, adapter.ProxyOption{ForceCertVerify: forceCertVerify})
   575  		if err != nil {
   576  			return nil, nil, fmt.Errorf("proxy %d: %w", idx, err)
   577  		}
   578  
   579  		if _, exist := proxies[proxy.Name()]; exist {
   580  			return nil, nil, fmt.Errorf("proxy %s is the duplicate name", proxy.Name())
   581  		}
   582  		proxies[proxy.Name()] = proxy
   583  		proxyList = append(proxyList, proxy.Name())
   584  	}
   585  
   586  	// keep the original order of ProxyGroups in config file
   587  	for idx, mapping := range groupsConfig {
   588  		groupName, existName := mapping["name"].(string)
   589  		if !existName {
   590  			return nil, nil, fmt.Errorf("proxy group %d: missing name", idx)
   591  		}
   592  		proxyList = append(proxyList, groupName)
   593  	}
   594  
   595  	// check if any loop exists and sort the ProxyGroups
   596  	if err := proxyGroupsDagSort(groupsConfig); err != nil {
   597  		return nil, nil, err
   598  	}
   599  
   600  	// parse and initial providers
   601  	for name, mapping := range providersConfig {
   602  		if name == provider.ReservedName {
   603  			return nil, nil, fmt.Errorf(
   604  				"can not defined a provider called `%s`", provider.ReservedName,
   605  			)
   606  		}
   607  
   608  		pd, err := provider.ParseProxyProvider(name, mapping, forceCertVerify)
   609  		if err != nil {
   610  			return nil, nil, fmt.Errorf("parse proxy provider %s error: %w", name, err)
   611  		}
   612  
   613  		providersMap[name] = pd
   614  	}
   615  
   616  	for _, proxyProvider := range providersMap {
   617  		log.Info().Str("name", proxyProvider.Name()).Msg("[Config] initial proxy provider")
   618  		if err := proxyProvider.Initial(); err != nil {
   619  			return nil, nil, fmt.Errorf(
   620  				"initial proxy provider %s error: %w", proxyProvider.Name(), err,
   621  			)
   622  		}
   623  	}
   624  
   625  	// parse proxy group
   626  	for idx, mapping := range groupsConfig {
   627  		group, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap)
   628  		if err != nil {
   629  			return nil, nil, fmt.Errorf("proxy group[%d]: %w", idx, err)
   630  		}
   631  
   632  		groupName := group.Name()
   633  		if _, exist := proxies[groupName]; exist {
   634  			return nil, nil, fmt.Errorf("proxy group %s: the duplicate name", groupName)
   635  		}
   636  
   637  		proxies[groupName] = adapter.NewProxy(group)
   638  	}
   639  
   640  	// initial compatible provider
   641  	for _, pd := range providersMap {
   642  		if pd.VehicleType() != providerTypes.Compatible {
   643  			continue
   644  		}
   645  
   646  		log.Info().Str("name", pd.Name()).Msg("[Config] initial compatible provider")
   647  		if err := pd.Initial(); err != nil {
   648  			return nil, nil, fmt.Errorf(
   649  				"initial compatible provider %s error: %w", pd.Name(), err,
   650  			)
   651  		}
   652  	}
   653  
   654  	var ps []C.Proxy
   655  	for _, v := range proxyList {
   656  		ps = append(ps, proxies[v])
   657  	}
   658  	hc := provider.NewHealthCheck(ps, "", 0, true)
   659  	pd, _ := provider.NewCompatibleProvider(provider.ReservedName, hc, nil)
   660  	pd.SetProxies(ps)
   661  	providersMap[provider.ReservedName] = pd
   662  
   663  	global := outboundgroup.NewSelector(
   664  		&outboundgroup.GroupCommonOption{
   665  			Name: "GLOBAL",
   666  		},
   667  		[]providerTypes.ProxyProvider{pd},
   668  	)
   669  	proxies["GLOBAL"] = adapter.NewProxy(global)
   670  	return proxies, providersMap, nil
   671  }
   672  
   673  func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, matchers map[string]C.Matcher) ([]C.Rule, map[string]C.Rule, error) {
   674  	defer runtime.GC()
   675  
   676  	ruleProviders := make(map[string]C.Rule)
   677  
   678  	rules, err := parseRawRules(cfg.Rule, ruleProviders, proxies, matchers)
   679  	if err != nil {
   680  		return nil, nil, err
   681  	}
   682  
   683  	return rules, ruleProviders, nil
   684  }
   685  
   686  func parseRawRules(
   687  	rawRules []RawRule,
   688  	ruleProviders map[string]C.Rule,
   689  	proxies map[string]C.Proxy,
   690  	matchers map[string]C.Matcher,
   691  ) ([]C.Rule, error) {
   692  	rules := make([]C.Rule, 0, len(rawRules))
   693  
   694  	for idx, raw := range rawRules {
   695  		line := raw.Line
   696  		if line == "" {
   697  			if raw.Name == "" {
   698  				continue
   699  			}
   700  
   701  			mk := "rule:" + raw.Name
   702  			if _, ok := matchers[mk]; ok {
   703  				return nil, fmt.Errorf("parse rule %s failed, rule name is exist", raw.Name)
   704  			}
   705  
   706  			var (
   707  				groupMatcher C.Matcher
   708  				err          error
   709  			)
   710  
   711  			if raw.Engine == "expr" {
   712  				groupMatcher, err = S.NewExprMatcher(raw.Name, raw.If)
   713  			} else {
   714  				groupMatcher, err = S.NewMatcher(raw.Name, "", raw.If)
   715  			}
   716  
   717  			if err != nil {
   718  				return nil, fmt.Errorf("parse rule %s failed, %w", raw.Name, err)
   719  			}
   720  
   721  			matchers[mk] = groupMatcher
   722  
   723  			subRules, err := parseRawRules(raw.Rules, ruleProviders, proxies, matchers)
   724  			if err != nil {
   725  				return nil, err
   726  			}
   727  
   728  			appendRuleGroupName(subRules, raw.Name)
   729  
   730  			parsed := R.NewGroup(fmt.Sprintf("%s (%s)", raw.Name, raw.If), groupMatcher, subRules)
   731  
   732  			rules = append(rules, parsed)
   733  
   734  			rpdArr := findRuleProvidersName(raw.If)
   735  			for _, v := range rpdArr {
   736  				v = strings.ToLower(v)
   737  				if _, ok := ruleProviders[v]; ok {
   738  					continue
   739  				}
   740  				rpd, err := R.NewGEOSITE(v, C.ScriptRuleGeoSiteTarget)
   741  				if err != nil {
   742  					continue
   743  				}
   744  				ruleProviders[v] = rpd
   745  			}
   746  			continue
   747  		}
   748  
   749  		rule := trimArr(strings.Split(line, ","))
   750  		var (
   751  			payload  string
   752  			target   string
   753  			params   []string
   754  			ruleName = strings.ToUpper(rule[0])
   755  		)
   756  
   757  		l := len(rule)
   758  
   759  		if l < 2 {
   760  			return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
   761  		}
   762  
   763  		if l < 4 {
   764  			rule = append(rule, make([]string, 4-l)...)
   765  		}
   766  
   767  		if ruleName == "MATCH" {
   768  			l = 2
   769  		}
   770  
   771  		if l >= 3 {
   772  			l = 3
   773  			payload = rule[1]
   774  		}
   775  
   776  		target = rule[l-1]
   777  		params = rule[l:]
   778  
   779  		if _, ok := proxies[target]; !ok && ruleName != "GEOSITE" && target != C.ScriptRuleGeoSiteTarget {
   780  			return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target)
   781  		}
   782  
   783  		pvName := strings.ToLower(payload)
   784  		_, foundRP := ruleProviders[pvName]
   785  		if ruleName == "GEOSITE" && target == C.ScriptRuleGeoSiteTarget && foundRP {
   786  			continue
   787  		}
   788  
   789  		params = trimArr(params)
   790  
   791  		parsed, parseErr := R.ParseRule(ruleName, payload, target, params)
   792  		if parseErr != nil {
   793  			return nil, fmt.Errorf("rules[%d] [%s] error: %w", idx, line, parseErr)
   794  		}
   795  
   796  		if scr, ok := parsed.(*R.Script); ok {
   797  			m := matchers[payload]
   798  			if m == nil {
   799  				return nil, fmt.Errorf(
   800  					"rules[%d] [%s] error: shortcut name [%s] not found", idx, line, payload,
   801  				)
   802  			}
   803  			scr.SetMatcher(m)
   804  		}
   805  
   806  		if ruleName == "GEOSITE" && !foundRP {
   807  			ruleProviders[pvName] = parsed
   808  		}
   809  
   810  		rules = append(rules, parsed)
   811  	}
   812  
   813  	return rules, nil
   814  }
   815  
   816  func appendRuleGroupName(subRules []C.Rule, groupName string) {
   817  	for i := range subRules {
   818  		subRules[i].AppendGroup(groupName)
   819  		if subRules[i].RuleType() != C.Group {
   820  			continue
   821  		}
   822  		appendRuleGroupName(subRules[i].SubRules(), groupName)
   823  	}
   824  }
   825  
   826  func parseHosts(cfg *RawConfig) (*trie.DomainTrie[netip.Addr], error) {
   827  	tree := trie.New[netip.Addr]()
   828  
   829  	// add default hosts
   830  	if err := tree.Insert("localhost", netip.AddrFrom4([4]byte{127, 0, 0, 1})); err != nil {
   831  		log.Error().Err(err).Msg("[Config] insert localhost to host failed")
   832  	}
   833  
   834  	if len(cfg.Hosts) != 0 {
   835  		for domain, ipStr := range cfg.Hosts {
   836  			ip, err := netip.ParseAddr(ipStr)
   837  			if err != nil {
   838  				return nil, fmt.Errorf("%s is not a valid IP", ipStr)
   839  			}
   840  			_ = tree.Insert(domain, ip)
   841  		}
   842  	}
   843  
   844  	// add mitm.clash hosts
   845  	if err := tree.Insert("mitm.clash", netip.AddrFrom4([4]byte{1, 2, 3, 4})); err != nil {
   846  		log.Error().Err(err).Msg("[Config] insert mitm.clash to host failed")
   847  	}
   848  
   849  	return tree, nil
   850  }
   851  
   852  func hostWithDefaultPort(host string, defPort string) (string, error) {
   853  	if !strings.Contains(host, ":") {
   854  		host += ":"
   855  	}
   856  
   857  	hostname, port, err := net.SplitHostPort(host)
   858  	if err != nil {
   859  		return "", err
   860  	}
   861  
   862  	if port == "" {
   863  		port = defPort
   864  	}
   865  
   866  	return net.JoinHostPort(hostname, port), nil
   867  }
   868  
   869  func parseNameServer(servers []string) ([]dns.NameServer, error) {
   870  	var nameservers []dns.NameServer
   871  
   872  	for idx, server := range servers {
   873  		// parse without scheme .e.g 8.8.8.8:53
   874  		if !strings.Contains(server, "://") {
   875  			server = "udp://" + server
   876  		}
   877  		u, err := url.Parse(server)
   878  		if err != nil {
   879  			return nil, fmt.Errorf("DNS NameServer[%d] format error: %w", idx, err)
   880  		}
   881  
   882  		var addr, dnsNetType string
   883  		switch u.Scheme {
   884  		case "udp":
   885  			addr, err = hostWithDefaultPort(u.Host, "53")
   886  			dnsNetType = "" // UDP
   887  		case "tcp":
   888  			addr, err = hostWithDefaultPort(u.Host, "53")
   889  			dnsNetType = "tcp" // TCP
   890  		case "tls":
   891  			addr, err = hostWithDefaultPort(u.Host, "853")
   892  			dnsNetType = "tcp-tls" // DNS over TLS
   893  		case "https":
   894  			clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path, User: u.User}
   895  			addr = clearURL.String()
   896  			dnsNetType = "https" // DNS over HTTPS
   897  		case "dhcp":
   898  			addr = u.Host
   899  			dnsNetType = "dhcp" // UDP from DHCP
   900  		default:
   901  			return nil, fmt.Errorf("DNS NameServer[%d] unsupport scheme: %s", idx, u.Scheme)
   902  		}
   903  
   904  		if err != nil {
   905  			return nil, fmt.Errorf("DNS NameServer[%d] format error: %w", idx, err)
   906  		}
   907  
   908  		nameservers = append(
   909  			nameservers,
   910  			dns.NameServer{
   911  				Net:   dnsNetType,
   912  				Addr:  addr,
   913  				Proxy: u.Fragment,
   914  			},
   915  		)
   916  	}
   917  	return nameservers, nil
   918  }
   919  
   920  func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServer, error) {
   921  	policy := map[string]dns.NameServer{}
   922  
   923  	for domain, server := range nsPolicy {
   924  		nameservers, err := parseNameServer([]string{server})
   925  		if err != nil {
   926  			return nil, err
   927  		}
   928  		if _, valid := trie.ValidAndSplitDomain(domain); !valid {
   929  			return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
   930  		}
   931  		policy[domain] = nameservers[0]
   932  	}
   933  
   934  	return policy, nil
   935  }
   936  
   937  func parseFallbackIPCIDR(ips []string) ([]*netip.Prefix, error) {
   938  	var ipNets []*netip.Prefix
   939  
   940  	for idx, ip := range ips {
   941  		ipnet, err := netip.ParsePrefix(ip)
   942  		if err != nil {
   943  			return nil, fmt.Errorf("DNS FallbackIP[%d] format error: %w", idx, err)
   944  		}
   945  		ipNets = append(ipNets, &ipnet)
   946  	}
   947  
   948  	return ipNets, nil
   949  }
   950  
   951  func parseFallbackGeoSite(countries []string) ([]*router.DomainMatcher, error) {
   952  	var sites []*router.DomainMatcher
   953  	for _, country := range countries {
   954  		matcher, recordsCount, err := geodata.LoadProviderByCode(country)
   955  		if err != nil {
   956  			return nil, err
   957  		}
   958  
   959  		sites = append(sites, matcher)
   960  
   961  		cont := fmt.Sprintf("%d", recordsCount)
   962  		if recordsCount == 0 {
   963  			cont = "from cache"
   964  		}
   965  		log.Info().
   966  			Str("country", country).
   967  			Str("records", cont).
   968  			Msg("[Config] initial GeoSite dns fallback filter")
   969  	}
   970  	return sites, nil
   971  }
   972  
   973  func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr]) (*DNS, error) {
   974  	cfg := rawCfg.DNS
   975  	if cfg.Enable && len(cfg.NameServer) == 0 {
   976  		return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
   977  	}
   978  
   979  	dnsCfg := &DNS{
   980  		Enable:           cfg.Enable,
   981  		Listen:           cfg.Listen,
   982  		IPv6:             lo.FromPtrOr(cfg.IPv6, rawCfg.IPv6),
   983  		EnhancedMode:     cfg.EnhancedMode,
   984  		RemoteDnsResolve: cfg.RemoteDnsResolve,
   985  		FallbackFilter: FallbackFilter{
   986  			IPCIDR:  []*netip.Prefix{},
   987  			GeoSite: []*router.DomainMatcher{},
   988  		},
   989  	}
   990  	var err error
   991  	if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer); err != nil {
   992  		return nil, err
   993  	}
   994  
   995  	if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback); err != nil {
   996  		return nil, err
   997  	}
   998  
   999  	if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy); err != nil {
  1000  		return nil, err
  1001  	}
  1002  
  1003  	if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver); err != nil {
  1004  		return nil, err
  1005  	}
  1006  
  1007  	if cfg.RemoteDnsResolve && len(cfg.RemoteNameserver) == 0 {
  1008  		return nil, errors.New(
  1009  			"remote nameserver should have at least one nameserver when `remote-dns-resolve` is enable",
  1010  		)
  1011  	}
  1012  	if dnsCfg.RemoteNameserver, err = parseNameServer(cfg.RemoteNameserver); err != nil {
  1013  		return nil, err
  1014  	}
  1015  	// check remote nameserver should not include any dhcp client
  1016  	for _, ns := range dnsCfg.RemoteNameserver {
  1017  		if ns.Net == "dhcp" {
  1018  			return nil, errors.New("remote nameserver should not contain any dhcp client")
  1019  		}
  1020  	}
  1021  
  1022  	if len(cfg.DefaultNameserver) == 0 {
  1023  		return nil, errors.New("default nameserver should have at least one nameserver")
  1024  	}
  1025  	if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver); err != nil {
  1026  		return nil, err
  1027  	}
  1028  	// check default nameserver is pure ip addr
  1029  	for _, ns := range dnsCfg.DefaultNameserver {
  1030  		host, _, err := net.SplitHostPort(ns.Addr)
  1031  		if err != nil || net.ParseIP(host) == nil {
  1032  			return nil, errors.New("default nameserver should be pure IP")
  1033  		}
  1034  	}
  1035  
  1036  	if cfg.EnhancedMode == C.DNSFakeIP {
  1037  		ipnet, err := netip.ParsePrefix(cfg.FakeIPRange)
  1038  		if err != nil {
  1039  			return nil, err
  1040  		}
  1041  
  1042  		defaultFakeIPFilter := []string{
  1043  			"*.lan",
  1044  			"*.local",
  1045  			"*.localhost",
  1046  			"*.test",
  1047  			"+.msftconnecttest.com",
  1048  			"localhost.ptlogin2.qq.com",
  1049  			"localhost.sec.qq.com",
  1050  		}
  1051  
  1052  		// add all nameserver host to fake ip skip host filter
  1053  		defaultFakeIPFilter = append(defaultFakeIPFilter,
  1054  			lo.Filter(
  1055  				lo.Map(
  1056  					append(dnsCfg.NameServer,
  1057  						append(dnsCfg.Fallback,
  1058  							append(dnsCfg.ProxyServerNameserver, dnsCfg.RemoteNameserver...)...)...),
  1059  					func(ns dns.NameServer, _ int) string {
  1060  						h, _, _ := net.SplitHostPort(ns.Addr)
  1061  						if _, err := netip.ParseAddr(h); err != nil {
  1062  							return h
  1063  						}
  1064  						return ""
  1065  					}),
  1066  				func(s string, _ int) bool {
  1067  					return s != ""
  1068  				},
  1069  			)...,
  1070  		)
  1071  
  1072  		// add policy to fake ip skip host filter
  1073  		if len(dnsCfg.NameServerPolicy) != 0 {
  1074  			for key, policy := range dnsCfg.NameServerPolicy {
  1075  				h, _, _ := net.SplitHostPort(policy.Addr)
  1076  
  1077  				if a, err := netip.ParseAddr(h); err != nil {
  1078  					defaultFakeIPFilter = append(defaultFakeIPFilter, h)
  1079  				} else if a.IsLoopback() || a.IsPrivate() {
  1080  					defaultFakeIPFilter = append(defaultFakeIPFilter, key)
  1081  				}
  1082  			}
  1083  		}
  1084  
  1085  		host := trie.New[bool]()
  1086  
  1087  		// fake ip skip host filter
  1088  		fakeIPFilter := lo.Uniq(append(cfg.FakeIPFilter, defaultFakeIPFilter...))
  1089  		for _, domain := range fakeIPFilter {
  1090  			_ = host.Insert(domain, true)
  1091  		}
  1092  
  1093  		resolver.StoreFakePoolState()
  1094  
  1095  		pool, err := fakeip.New(fakeip.Options{
  1096  			IPNet:       &ipnet,
  1097  			Size:        1000,
  1098  			Host:        host,
  1099  			Persistence: rawCfg.Profile.StoreFakeIP,
  1100  		})
  1101  		if err != nil {
  1102  			return nil, err
  1103  		}
  1104  
  1105  		dnsCfg.FakeIPRange = pool
  1106  	}
  1107  
  1108  	if len(cfg.Fallback) != 0 {
  1109  		dnsCfg.FallbackFilter.GeoIP = cfg.FallbackFilter.GeoIP
  1110  		dnsCfg.FallbackFilter.GeoIPCode = cfg.FallbackFilter.GeoIPCode
  1111  		if fallbackip, err := parseFallbackIPCIDR(cfg.FallbackFilter.IPCIDR); err == nil {
  1112  			dnsCfg.FallbackFilter.IPCIDR = fallbackip
  1113  		}
  1114  		dnsCfg.FallbackFilter.Domain = cfg.FallbackFilter.Domain
  1115  		fallbackGeoSite, err := parseFallbackGeoSite(cfg.FallbackFilter.GeoSite)
  1116  		if err != nil {
  1117  			return nil, fmt.Errorf("load GeoSite dns fallback filter error, %w", err)
  1118  		}
  1119  		dnsCfg.FallbackFilter.GeoSite = fallbackGeoSite
  1120  	}
  1121  
  1122  	if cfg.UseHosts {
  1123  		dnsCfg.Hosts = hosts
  1124  	}
  1125  
  1126  	if len(cfg.SearchDomains) != 0 {
  1127  		for _, domain := range cfg.SearchDomains {
  1128  			if strings.HasPrefix(domain, ".") || strings.HasSuffix(domain, ".") {
  1129  				return nil, errors.New("search domains should not start or end with '.'")
  1130  			}
  1131  			if strings.Contains(domain, ":") {
  1132  				return nil, errors.New("search domains are for ipv4 only and should not contain ports")
  1133  			}
  1134  		}
  1135  		dnsCfg.SearchDomains = cfg.SearchDomains
  1136  	}
  1137  
  1138  	return dnsCfg, nil
  1139  }
  1140  
  1141  func ParseAuthentication(rawRecords []string) []auth.AuthUser {
  1142  	var users []auth.AuthUser
  1143  	for _, line := range rawRecords {
  1144  		if user, pass, found := strings.Cut(line, ":"); found {
  1145  			users = append(users, auth.AuthUser{User: user, Pass: pass})
  1146  		}
  1147  	}
  1148  	return users
  1149  }
  1150  
  1151  func parseScript(script Script, rawRules []RawRule) (map[string]C.Matcher, []RawRule, error) {
  1152  	var (
  1153  		engine        = script.Engine
  1154  		path          = script.MainPath
  1155  		mainCode      = script.MainCode
  1156  		shortcutsCode = script.ShortcutsCode
  1157  	)
  1158  
  1159  	if len(shortcutsCode) > 0 && engine != "expr" && engine != "starlark" {
  1160  		return nil, nil, fmt.Errorf("invalid script shortcut engine, got %s, want expr or starlark", engine)
  1161  	}
  1162  
  1163  	if path != "" {
  1164  		if !strings.HasSuffix(path, ".star") {
  1165  			return nil, nil, fmt.Errorf("initialized script file failure, script path [%s] invalid", path)
  1166  		}
  1167  		path = C.Path.Resolve(path)
  1168  		if _, err := os.Stat(path); os.IsNotExist(err) {
  1169  			return nil, nil, fmt.Errorf("initialized script file failure, script path invalid: %w", err)
  1170  		}
  1171  		data, err := os.ReadFile(path)
  1172  		if err != nil {
  1173  			return nil, nil, fmt.Errorf("initialized script file failure, read file error: %w", err)
  1174  		}
  1175  		mainCode = string(data)
  1176  	}
  1177  
  1178  	if strings.TrimSpace(mainCode) == "" {
  1179  		mainCode = `
  1180  def main(ctx, metadata):
  1181    return "DIRECT"
  1182  `
  1183  	} else {
  1184  		mainCode = cleanScriptKeywords(mainCode)
  1185  	}
  1186  
  1187  	content := mainCode + "\n"
  1188  
  1189  	matcher, err := S.NewMatcher("main", "", mainCode)
  1190  	if err != nil {
  1191  		return nil, nil, fmt.Errorf("initialized script module failure, %w", err)
  1192  	}
  1193  
  1194  	matchers := make(map[string]C.Matcher)
  1195  	matchers["main"] = matcher
  1196  	for k, v := range shortcutsCode {
  1197  		if _, ok := matchers[k]; ok {
  1198  			return nil, nil, fmt.Errorf("initialized rule SCRIPT failure, shortcut name [%s] is exist", k)
  1199  		}
  1200  
  1201  		v = strings.TrimSpace(v)
  1202  		if v == "" {
  1203  			return nil, nil, fmt.Errorf("initialized rule SCRIPT failure, shortcut [%s] code syntax invalid", k)
  1204  		}
  1205  
  1206  		v = strings.ReplaceAll(strings.ReplaceAll(v, "\r", " "), "\n", " ")
  1207  
  1208  		var m C.Matcher
  1209  		if engine == "expr" {
  1210  			m, err = S.NewExprMatcher(k, v)
  1211  		} else {
  1212  			m, err = S.NewMatcher(k, "", v)
  1213  		}
  1214  		if err != nil {
  1215  			return nil, nil, fmt.Errorf("initialized script module failure, %w", err)
  1216  		}
  1217  
  1218  		matchers[k] = m
  1219  		content += v + "\n"
  1220  	}
  1221  
  1222  	rpdArr := findRuleProvidersName(content)
  1223  	for _, v := range rpdArr {
  1224  		rule := fmt.Sprintf("GEOSITE,%s,%s", v, C.ScriptRuleGeoSiteTarget)
  1225  		rawRules = append(rawRules, RawRule(rawRule{Line: rule}))
  1226  	}
  1227  
  1228  	log.Info().Str("engine", engine).Msg("[Config] initial script module successful")
  1229  
  1230  	return matchers, rawRules, nil
  1231  }
  1232  
  1233  func cleanScriptKeywords(code string) string {
  1234  	keywords := []string{
  1235  		`load\(`, `def resolve_ip\(`, `def in_cidr\(`, `def in_ipset\(`, `def geoip\(`, `def log\(`,
  1236  		`def match_provider\(`, `def resolve_process_name\(`, `def resolve_process_path\(`,
  1237  	}
  1238  
  1239  	for _, kw := range keywords {
  1240  		reg := regexp.MustCompile("(?m)[\r\n]+^.*" + kw + ".*$")
  1241  		code = reg.ReplaceAllString(code, "")
  1242  	}
  1243  	return code
  1244  }
  1245  
  1246  func findRuleProvidersName(s string) []string {
  1247  	var (
  1248  		regxStr = `ctx.rule_providers\[["'](\S+)["']\]\.match|match_provider\(["'](\S+)["']\)`
  1249  		regx    = regexp.MustCompile(regxStr)
  1250  		arr     = regx.FindAllStringSubmatch(s, -1)
  1251  		rpd     []string
  1252  	)
  1253  
  1254  	for _, rpdArr := range arr {
  1255  		for i, v := range rpdArr {
  1256  			if i == 0 || v == "" {
  1257  				continue
  1258  			}
  1259  			rpd = append(rpd, v)
  1260  		}
  1261  	}
  1262  
  1263  	return lo.Uniq(rpd)
  1264  }
  1265  
  1266  func parseMitm(rawMitm RawMitm) (*Mitm, error) {
  1267  	var (
  1268  		req []C.Rewrite
  1269  		res []C.Rewrite
  1270  	)
  1271  
  1272  	for _, line := range rawMitm.Rules {
  1273  		rule, err := mitm.ParseRewrite(line)
  1274  		if err != nil {
  1275  			return nil, fmt.Errorf("parse rewrite rule failure: %w", err)
  1276  		}
  1277  
  1278  		if rule.RuleType() == C.MitmResponseHeader || rule.RuleType() == C.MitmResponseBody {
  1279  			res = append(res, rule)
  1280  		} else {
  1281  			req = append(req, rule)
  1282  		}
  1283  	}
  1284  
  1285  	hosts := trie.New[bool]()
  1286  
  1287  	if len(rawMitm.Hosts) != 0 {
  1288  		for _, domain := range rawMitm.Hosts {
  1289  			_ = hosts.Insert(domain, true)
  1290  		}
  1291  	}
  1292  
  1293  	_ = hosts.Insert("mitm.clash", true)
  1294  
  1295  	return &Mitm{
  1296  		Hosts: hosts,
  1297  		Rules: mitm.NewRewriteRules(req, res),
  1298  	}, nil
  1299  }
  1300  
  1301  func verifyScriptMatcher(config *Config, matchers map[string]C.Matcher) (err error) {
  1302  	defer func() {
  1303  		if r := recover(); r != nil {
  1304  			err = fmt.Errorf("test script code panic: %v", r)
  1305  		}
  1306  	}()
  1307  
  1308  	if len(matchers) == 0 {
  1309  		return
  1310  	}
  1311  
  1312  	metadata := &C.Metadata{
  1313  		Type:    C.SOCKS5,
  1314  		NetWork: C.TCP,
  1315  		Host:    "www.example.com",
  1316  		SrcIP:   netip.MustParseAddr("198.18.0.8"),
  1317  		SrcPort: 12345,
  1318  		DstPort: 443,
  1319  	}
  1320  
  1321  	metadata1 := &C.Metadata{
  1322  		Type:    C.TUN,
  1323  		NetWork: C.UDP,
  1324  		Host:    "8.8.8.8",
  1325  		SrcIP:   netip.MustParseAddr("192.168.1.123"),
  1326  		SrcPort: 6789,
  1327  		DstPort: 2023,
  1328  	}
  1329  
  1330  	cases := []*C.Metadata{metadata, metadata1}
  1331  
  1332  	C.BackupScriptState()
  1333  
  1334  	C.GetScriptProxyProviders = func() map[string][]C.Proxy {
  1335  		providersMap := make(map[string][]C.Proxy)
  1336  		for k, v := range config.Providers {
  1337  			providersMap[k] = v.Proxies()
  1338  		}
  1339  		return providersMap
  1340  	}
  1341  
  1342  	C.SetScriptRuleProviders(config.RuleProviders)
  1343  	defer C.RestoreScriptState()
  1344  
  1345  	for k, v := range matchers {
  1346  		isMain := k == "main"
  1347  		for i := range cases {
  1348  			if isMain {
  1349  				_, err = v.Eval(cases[i])
  1350  			} else {
  1351  				_, err = v.Match(cases[i])
  1352  			}
  1353  			if err != nil {
  1354  				err = fmt.Errorf("verify script code failed: %w", err)
  1355  				return
  1356  			}
  1357  		}
  1358  	}
  1359  
  1360  	return
  1361  }