github.com/moqsien/xraycore@v1.8.5/infra/conf/router.go (about)

     1  package conf
     2  
     3  import (
     4  	"encoding/json"
     5  	"runtime"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/moqsien/xraycore/app/router"
    10  	"github.com/moqsien/xraycore/common/net"
    11  	"github.com/moqsien/xraycore/common/platform/filesystem"
    12  	"google.golang.org/protobuf/proto"
    13  )
    14  
    15  type RouterRulesConfig struct {
    16  	RuleList       []json.RawMessage `json:"rules"`
    17  	DomainStrategy string            `json:"domainStrategy"`
    18  }
    19  
    20  // StrategyConfig represents a strategy config
    21  type StrategyConfig struct {
    22  	Type     string           `json:"type"`
    23  	Settings *json.RawMessage `json:"settings"`
    24  }
    25  
    26  type BalancingRule struct {
    27  	Tag       string         `json:"tag"`
    28  	Selectors StringList     `json:"selector"`
    29  	Strategy  StrategyConfig `json:"strategy"`
    30  }
    31  
    32  func (r *BalancingRule) Build() (*router.BalancingRule, error) {
    33  	if r.Tag == "" {
    34  		return nil, newError("empty balancer tag")
    35  	}
    36  	if len(r.Selectors) == 0 {
    37  		return nil, newError("empty selector list")
    38  	}
    39  
    40  	var strategy string
    41  	switch strings.ToLower(r.Strategy.Type) {
    42  	case strategyRandom, "":
    43  		strategy = strategyRandom
    44  	case strategyLeastPing:
    45  		strategy = "leastPing"
    46  	default:
    47  		return nil, newError("unknown balancing strategy: " + r.Strategy.Type)
    48  	}
    49  
    50  	return &router.BalancingRule{
    51  		Tag:              r.Tag,
    52  		OutboundSelector: []string(r.Selectors),
    53  		Strategy:         strategy,
    54  	}, nil
    55  }
    56  
    57  type RouterConfig struct {
    58  	Settings       *RouterRulesConfig `json:"settings"` // Deprecated
    59  	RuleList       []json.RawMessage  `json:"rules"`
    60  	DomainStrategy *string            `json:"domainStrategy"`
    61  	Balancers      []*BalancingRule   `json:"balancers"`
    62  
    63  	DomainMatcher string `json:"domainMatcher"`
    64  }
    65  
    66  func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
    67  	ds := ""
    68  	if c.DomainStrategy != nil {
    69  		ds = *c.DomainStrategy
    70  	} else if c.Settings != nil {
    71  		ds = c.Settings.DomainStrategy
    72  	}
    73  
    74  	switch strings.ToLower(ds) {
    75  	case "alwaysip":
    76  		return router.Config_UseIp
    77  	case "ipifnonmatch":
    78  		return router.Config_IpIfNonMatch
    79  	case "ipondemand":
    80  		return router.Config_IpOnDemand
    81  	default:
    82  		return router.Config_AsIs
    83  	}
    84  }
    85  
    86  func (c *RouterConfig) Build() (*router.Config, error) {
    87  	config := new(router.Config)
    88  	config.DomainStrategy = c.getDomainStrategy()
    89  
    90  	var rawRuleList []json.RawMessage
    91  	if c != nil {
    92  		rawRuleList = c.RuleList
    93  		if c.Settings != nil {
    94  			c.RuleList = append(c.RuleList, c.Settings.RuleList...)
    95  			rawRuleList = c.RuleList
    96  		}
    97  	}
    98  
    99  	for _, rawRule := range rawRuleList {
   100  		rule, err := ParseRule(rawRule)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  
   105  		if rule.DomainMatcher == "" {
   106  			rule.DomainMatcher = c.DomainMatcher
   107  		}
   108  
   109  		config.Rule = append(config.Rule, rule)
   110  	}
   111  	for _, rawBalancer := range c.Balancers {
   112  		balancer, err := rawBalancer.Build()
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		config.BalancingRule = append(config.BalancingRule, balancer)
   117  	}
   118  	return config, nil
   119  }
   120  
   121  type RouterRule struct {
   122  	Type        string `json:"type"`
   123  	OutboundTag string `json:"outboundTag"`
   124  	BalancerTag string `json:"balancerTag"`
   125  
   126  	DomainMatcher string `json:"domainMatcher"`
   127  }
   128  
   129  func ParseIP(s string) (*router.CIDR, error) {
   130  	var addr, mask string
   131  	i := strings.Index(s, "/")
   132  	if i < 0 {
   133  		addr = s
   134  	} else {
   135  		addr = s[:i]
   136  		mask = s[i+1:]
   137  	}
   138  	ip := net.ParseAddress(addr)
   139  	switch ip.Family() {
   140  	case net.AddressFamilyIPv4:
   141  		bits := uint32(32)
   142  		if len(mask) > 0 {
   143  			bits64, err := strconv.ParseUint(mask, 10, 32)
   144  			if err != nil {
   145  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   146  			}
   147  			bits = uint32(bits64)
   148  		}
   149  		if bits > 32 {
   150  			return nil, newError("invalid network mask for router: ", bits)
   151  		}
   152  		return &router.CIDR{
   153  			Ip:     []byte(ip.IP()),
   154  			Prefix: bits,
   155  		}, nil
   156  	case net.AddressFamilyIPv6:
   157  		bits := uint32(128)
   158  		if len(mask) > 0 {
   159  			bits64, err := strconv.ParseUint(mask, 10, 32)
   160  			if err != nil {
   161  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   162  			}
   163  			bits = uint32(bits64)
   164  		}
   165  		if bits > 128 {
   166  			return nil, newError("invalid network mask for router: ", bits)
   167  		}
   168  		return &router.CIDR{
   169  			Ip:     []byte(ip.IP()),
   170  			Prefix: bits,
   171  		}, nil
   172  	default:
   173  		return nil, newError("unsupported address for router: ", s)
   174  	}
   175  }
   176  
   177  func loadGeoIP(code string) ([]*router.CIDR, error) {
   178  	return loadIP("geoip.dat", code)
   179  }
   180  
   181  var (
   182  	FileCache = make(map[string][]byte)
   183  	IPCache   = make(map[string]*router.GeoIP)
   184  	SiteCache = make(map[string]*router.GeoSite)
   185  )
   186  
   187  func loadFile(file string) ([]byte, error) {
   188  	if FileCache[file] == nil {
   189  		bs, err := filesystem.ReadAsset(file)
   190  		if err != nil {
   191  			return nil, newError("failed to open file: ", file).Base(err)
   192  		}
   193  		if len(bs) == 0 {
   194  			return nil, newError("empty file: ", file)
   195  		}
   196  		// Do not cache file, may save RAM when there
   197  		// are many files, but consume CPU each time.
   198  		return bs, nil
   199  		FileCache[file] = bs
   200  	}
   201  	return FileCache[file], nil
   202  }
   203  
   204  func loadIP(file, code string) ([]*router.CIDR, error) {
   205  	index := file + ":" + code
   206  	if IPCache[index] == nil {
   207  		bs, err := loadFile(file)
   208  		if err != nil {
   209  			return nil, newError("failed to load file: ", file).Base(err)
   210  		}
   211  		bs = find(bs, []byte(code))
   212  		if bs == nil {
   213  			return nil, newError("code not found in ", file, ": ", code)
   214  		}
   215  		var geoip router.GeoIP
   216  		if err := proto.Unmarshal(bs, &geoip); err != nil {
   217  			return nil, newError("error unmarshal IP in ", file, ": ", code).Base(err)
   218  		}
   219  		defer runtime.GC()     // or debug.FreeOSMemory()
   220  		return geoip.Cidr, nil // do not cache geoip
   221  		IPCache[index] = &geoip
   222  	}
   223  	return IPCache[index].Cidr, nil
   224  }
   225  
   226  func loadSite(file, code string) ([]*router.Domain, error) {
   227  	index := file + ":" + code
   228  	if SiteCache[index] == nil {
   229  		bs, err := loadFile(file)
   230  		if err != nil {
   231  			return nil, newError("failed to load file: ", file).Base(err)
   232  		}
   233  		bs = find(bs, []byte(code))
   234  		if bs == nil {
   235  			return nil, newError("list not found in ", file, ": ", code)
   236  		}
   237  		var geosite router.GeoSite
   238  		if err := proto.Unmarshal(bs, &geosite); err != nil {
   239  			return nil, newError("error unmarshal Site in ", file, ": ", code).Base(err)
   240  		}
   241  		defer runtime.GC()         // or debug.FreeOSMemory()
   242  		return geosite.Domain, nil // do not cache geosite
   243  		SiteCache[index] = &geosite
   244  	}
   245  	return SiteCache[index].Domain, nil
   246  }
   247  
   248  func DecodeVarint(buf []byte) (x uint64, n int) {
   249  	for shift := uint(0); shift < 64; shift += 7 {
   250  		if n >= len(buf) {
   251  			return 0, 0
   252  		}
   253  		b := uint64(buf[n])
   254  		n++
   255  		x |= (b & 0x7F) << shift
   256  		if (b & 0x80) == 0 {
   257  			return x, n
   258  		}
   259  	}
   260  
   261  	// The number is too large to represent in a 64-bit value.
   262  	return 0, 0
   263  }
   264  
   265  func find(data, code []byte) []byte {
   266  	codeL := len(code)
   267  	if codeL == 0 {
   268  		return nil
   269  	}
   270  	for {
   271  		dataL := len(data)
   272  		if dataL < 2 {
   273  			return nil
   274  		}
   275  		x, y := DecodeVarint(data[1:])
   276  		if x == 0 && y == 0 {
   277  			return nil
   278  		}
   279  		headL, bodyL := 1+y, int(x)
   280  		dataL -= headL
   281  		if dataL < bodyL {
   282  			return nil
   283  		}
   284  		data = data[headL:]
   285  		if int(data[1]) == codeL {
   286  			for i := 0; i < codeL && data[2+i] == code[i]; i++ {
   287  				if i+1 == codeL {
   288  					return data[:bodyL]
   289  				}
   290  			}
   291  		}
   292  		if dataL == bodyL {
   293  			return nil
   294  		}
   295  		data = data[bodyL:]
   296  	}
   297  }
   298  
   299  type AttributeMatcher interface {
   300  	Match(*router.Domain) bool
   301  }
   302  
   303  type BooleanMatcher string
   304  
   305  func (m BooleanMatcher) Match(domain *router.Domain) bool {
   306  	for _, attr := range domain.Attribute {
   307  		if attr.Key == string(m) {
   308  			return true
   309  		}
   310  	}
   311  	return false
   312  }
   313  
   314  type AttributeList struct {
   315  	matcher []AttributeMatcher
   316  }
   317  
   318  func (al *AttributeList) Match(domain *router.Domain) bool {
   319  	for _, matcher := range al.matcher {
   320  		if !matcher.Match(domain) {
   321  			return false
   322  		}
   323  	}
   324  	return true
   325  }
   326  
   327  func (al *AttributeList) IsEmpty() bool {
   328  	return len(al.matcher) == 0
   329  }
   330  
   331  func parseAttrs(attrs []string) *AttributeList {
   332  	al := new(AttributeList)
   333  	for _, attr := range attrs {
   334  		lc := strings.ToLower(attr)
   335  		al.matcher = append(al.matcher, BooleanMatcher(lc))
   336  	}
   337  	return al
   338  }
   339  
   340  func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
   341  	parts := strings.Split(siteWithAttr, "@")
   342  	if len(parts) == 0 {
   343  		return nil, newError("empty site")
   344  	}
   345  	country := strings.ToUpper(parts[0])
   346  	attrs := parseAttrs(parts[1:])
   347  	domains, err := loadSite(file, country)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	if attrs.IsEmpty() {
   353  		return domains, nil
   354  	}
   355  
   356  	filteredDomains := make([]*router.Domain, 0, len(domains))
   357  	for _, domain := range domains {
   358  		if attrs.Match(domain) {
   359  			filteredDomains = append(filteredDomains, domain)
   360  		}
   361  	}
   362  
   363  	return filteredDomains, nil
   364  }
   365  
   366  func parseDomainRule(domain string) ([]*router.Domain, error) {
   367  	if strings.HasPrefix(domain, "geosite:") {
   368  		country := strings.ToUpper(domain[8:])
   369  		domains, err := loadGeositeWithAttr("geosite.dat", country)
   370  		if err != nil {
   371  			return nil, newError("failed to load geosite: ", country).Base(err)
   372  		}
   373  		return domains, nil
   374  	}
   375  	isExtDatFile := 0
   376  	{
   377  		const prefix = "ext:"
   378  		if strings.HasPrefix(domain, prefix) {
   379  			isExtDatFile = len(prefix)
   380  		}
   381  		const prefixQualified = "ext-domain:"
   382  		if strings.HasPrefix(domain, prefixQualified) {
   383  			isExtDatFile = len(prefixQualified)
   384  		}
   385  	}
   386  	if isExtDatFile != 0 {
   387  		kv := strings.Split(domain[isExtDatFile:], ":")
   388  		if len(kv) != 2 {
   389  			return nil, newError("invalid external resource: ", domain)
   390  		}
   391  		filename := kv[0]
   392  		country := kv[1]
   393  		domains, err := loadGeositeWithAttr(filename, country)
   394  		if err != nil {
   395  			return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err)
   396  		}
   397  		return domains, nil
   398  	}
   399  
   400  	domainRule := new(router.Domain)
   401  	switch {
   402  	case strings.HasPrefix(domain, "regexp:"):
   403  		domainRule.Type = router.Domain_Regex
   404  		domainRule.Value = domain[7:]
   405  
   406  	case strings.HasPrefix(domain, "domain:"):
   407  		domainRule.Type = router.Domain_Domain
   408  		domainRule.Value = domain[7:]
   409  
   410  	case strings.HasPrefix(domain, "full:"):
   411  		domainRule.Type = router.Domain_Full
   412  		domainRule.Value = domain[5:]
   413  
   414  	case strings.HasPrefix(domain, "keyword:"):
   415  		domainRule.Type = router.Domain_Plain
   416  		domainRule.Value = domain[8:]
   417  
   418  	case strings.HasPrefix(domain, "dotless:"):
   419  		domainRule.Type = router.Domain_Regex
   420  		switch substr := domain[8:]; {
   421  		case substr == "":
   422  			domainRule.Value = "^[^.]*$"
   423  		case !strings.Contains(substr, "."):
   424  			domainRule.Value = "^[^.]*" + substr + "[^.]*$"
   425  		default:
   426  			return nil, newError("substr in dotless rule should not contain a dot: ", substr)
   427  		}
   428  
   429  	default:
   430  		domainRule.Type = router.Domain_Plain
   431  		domainRule.Value = domain
   432  	}
   433  	return []*router.Domain{domainRule}, nil
   434  }
   435  
   436  func ToCidrList(ips StringList) ([]*router.GeoIP, error) {
   437  	var geoipList []*router.GeoIP
   438  	var customCidrs []*router.CIDR
   439  
   440  	for _, ip := range ips {
   441  		if strings.HasPrefix(ip, "geoip:") {
   442  			country := ip[6:]
   443  			isReverseMatch := false
   444  			if strings.HasPrefix(ip, "geoip:!") {
   445  				country = ip[7:]
   446  				isReverseMatch = true
   447  			}
   448  			if len(country) == 0 {
   449  				return nil, newError("empty country name in rule")
   450  			}
   451  			geoip, err := loadGeoIP(strings.ToUpper(country))
   452  			if err != nil {
   453  				return nil, newError("failed to load GeoIP: ", country).Base(err)
   454  			}
   455  
   456  			geoipList = append(geoipList, &router.GeoIP{
   457  				CountryCode:  strings.ToUpper(country),
   458  				Cidr:         geoip,
   459  				ReverseMatch: isReverseMatch,
   460  			})
   461  			continue
   462  		}
   463  		isExtDatFile := 0
   464  		{
   465  			const prefix = "ext:"
   466  			if strings.HasPrefix(ip, prefix) {
   467  				isExtDatFile = len(prefix)
   468  			}
   469  			const prefixQualified = "ext-ip:"
   470  			if strings.HasPrefix(ip, prefixQualified) {
   471  				isExtDatFile = len(prefixQualified)
   472  			}
   473  		}
   474  		if isExtDatFile != 0 {
   475  			kv := strings.Split(ip[isExtDatFile:], ":")
   476  			if len(kv) != 2 {
   477  				return nil, newError("invalid external resource: ", ip)
   478  			}
   479  
   480  			filename := kv[0]
   481  			country := kv[1]
   482  			if len(filename) == 0 || len(country) == 0 {
   483  				return nil, newError("empty filename or empty country in rule")
   484  			}
   485  
   486  			isReverseMatch := false
   487  			if strings.HasPrefix(country, "!") {
   488  				country = country[1:]
   489  				isReverseMatch = true
   490  			}
   491  			geoip, err := loadIP(filename, strings.ToUpper(country))
   492  			if err != nil {
   493  				return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
   494  			}
   495  
   496  			geoipList = append(geoipList, &router.GeoIP{
   497  				CountryCode:  strings.ToUpper(filename + "_" + country),
   498  				Cidr:         geoip,
   499  				ReverseMatch: isReverseMatch,
   500  			})
   501  
   502  			continue
   503  		}
   504  
   505  		ipRule, err := ParseIP(ip)
   506  		if err != nil {
   507  			return nil, newError("invalid IP: ", ip).Base(err)
   508  		}
   509  		customCidrs = append(customCidrs, ipRule)
   510  	}
   511  
   512  	if len(customCidrs) > 0 {
   513  		geoipList = append(geoipList, &router.GeoIP{
   514  			Cidr: customCidrs,
   515  		})
   516  	}
   517  
   518  	return geoipList, nil
   519  }
   520  
   521  func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
   522  	type RawFieldRule struct {
   523  		RouterRule
   524  		Domain     *StringList       `json:"domain"`
   525  		Domains    *StringList       `json:"domains"`
   526  		IP         *StringList       `json:"ip"`
   527  		Port       *PortList         `json:"port"`
   528  		Network    *NetworkList      `json:"network"`
   529  		SourceIP   *StringList       `json:"source"`
   530  		SourcePort *PortList         `json:"sourcePort"`
   531  		User       *StringList       `json:"user"`
   532  		InboundTag *StringList       `json:"inboundTag"`
   533  		Protocols  *StringList       `json:"protocol"`
   534  		Attributes map[string]string `json:"attrs"`
   535  	}
   536  	rawFieldRule := new(RawFieldRule)
   537  	err := json.Unmarshal(msg, rawFieldRule)
   538  	if err != nil {
   539  		return nil, err
   540  	}
   541  
   542  	rule := new(router.RoutingRule)
   543  	switch {
   544  	case len(rawFieldRule.OutboundTag) > 0:
   545  		rule.TargetTag = &router.RoutingRule_Tag{
   546  			Tag: rawFieldRule.OutboundTag,
   547  		}
   548  	case len(rawFieldRule.BalancerTag) > 0:
   549  		rule.TargetTag = &router.RoutingRule_BalancingTag{
   550  			BalancingTag: rawFieldRule.BalancerTag,
   551  		}
   552  	default:
   553  		return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
   554  	}
   555  
   556  	if rawFieldRule.DomainMatcher != "" {
   557  		rule.DomainMatcher = rawFieldRule.DomainMatcher
   558  	}
   559  
   560  	if rawFieldRule.Domain != nil {
   561  		for _, domain := range *rawFieldRule.Domain {
   562  			rules, err := parseDomainRule(domain)
   563  			if err != nil {
   564  				return nil, newError("failed to parse domain rule: ", domain).Base(err)
   565  			}
   566  			rule.Domain = append(rule.Domain, rules...)
   567  		}
   568  	}
   569  
   570  	if rawFieldRule.Domains != nil {
   571  		for _, domain := range *rawFieldRule.Domains {
   572  			rules, err := parseDomainRule(domain)
   573  			if err != nil {
   574  				return nil, newError("failed to parse domain rule: ", domain).Base(err)
   575  			}
   576  			rule.Domain = append(rule.Domain, rules...)
   577  		}
   578  	}
   579  
   580  	if rawFieldRule.IP != nil {
   581  		geoipList, err := ToCidrList(*rawFieldRule.IP)
   582  		if err != nil {
   583  			return nil, err
   584  		}
   585  		rule.Geoip = geoipList
   586  	}
   587  
   588  	if rawFieldRule.Port != nil {
   589  		rule.PortList = rawFieldRule.Port.Build()
   590  	}
   591  
   592  	if rawFieldRule.Network != nil {
   593  		rule.Networks = rawFieldRule.Network.Build()
   594  	}
   595  
   596  	if rawFieldRule.SourceIP != nil {
   597  		geoipList, err := ToCidrList(*rawFieldRule.SourceIP)
   598  		if err != nil {
   599  			return nil, err
   600  		}
   601  		rule.SourceGeoip = geoipList
   602  	}
   603  
   604  	if rawFieldRule.SourcePort != nil {
   605  		rule.SourcePortList = rawFieldRule.SourcePort.Build()
   606  	}
   607  
   608  	if rawFieldRule.User != nil {
   609  		for _, s := range *rawFieldRule.User {
   610  			rule.UserEmail = append(rule.UserEmail, s)
   611  		}
   612  	}
   613  
   614  	if rawFieldRule.InboundTag != nil {
   615  		for _, s := range *rawFieldRule.InboundTag {
   616  			rule.InboundTag = append(rule.InboundTag, s)
   617  		}
   618  	}
   619  
   620  	if rawFieldRule.Protocols != nil {
   621  		for _, s := range *rawFieldRule.Protocols {
   622  			rule.Protocol = append(rule.Protocol, s)
   623  		}
   624  	}
   625  
   626  	if len(rawFieldRule.Attributes) > 0 {
   627  		rule.Attributes = rawFieldRule.Attributes
   628  	}
   629  
   630  	return rule, nil
   631  }
   632  
   633  func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
   634  	rawRule := new(RouterRule)
   635  	err := json.Unmarshal(msg, rawRule)
   636  	if err != nil {
   637  		return nil, newError("invalid router rule").Base(err)
   638  	}
   639  	if strings.EqualFold(rawRule.Type, "field") {
   640  		fieldrule, err := parseFieldRule(msg)
   641  		if err != nil {
   642  			return nil, newError("invalid field rule").Base(err)
   643  		}
   644  		return fieldrule, nil
   645  	}
   646  	if strings.EqualFold(rawRule.Type, "chinaip") {
   647  		chinaiprule, err := parseChinaIPRule(msg)
   648  		if err != nil {
   649  			return nil, newError("invalid chinaip rule").Base(err)
   650  		}
   651  		return chinaiprule, nil
   652  	}
   653  	if strings.EqualFold(rawRule.Type, "chinasites") {
   654  		chinasitesrule, err := parseChinaSitesRule(msg)
   655  		if err != nil {
   656  			return nil, newError("invalid chinasites rule").Base(err)
   657  		}
   658  		return chinasitesrule, nil
   659  	}
   660  	return nil, newError("unknown router rule type: ", rawRule.Type)
   661  }
   662  
   663  func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
   664  	rawRule := new(RouterRule)
   665  	err := json.Unmarshal(data, rawRule)
   666  	if err != nil {
   667  		return nil, newError("invalid router rule").Base(err)
   668  	}
   669  	chinaIPs, err := loadGeoIP("CN")
   670  	if err != nil {
   671  		return nil, newError("failed to load geoip:cn").Base(err)
   672  	}
   673  	return &router.RoutingRule{
   674  		TargetTag: &router.RoutingRule_Tag{
   675  			Tag: rawRule.OutboundTag,
   676  		},
   677  		Cidr: chinaIPs,
   678  	}, nil
   679  }
   680  
   681  func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
   682  	rawRule := new(RouterRule)
   683  	err := json.Unmarshal(data, rawRule)
   684  	if err != nil {
   685  		return nil, newError("invalid router rule").Base(err).AtError()
   686  	}
   687  	domains, err := loadGeositeWithAttr("geosite.dat", "CN")
   688  	if err != nil {
   689  		return nil, newError("failed to load geosite:cn.").Base(err)
   690  	}
   691  	return &router.RoutingRule{
   692  		TargetTag: &router.RoutingRule_Tag{
   693  			Tag: rawRule.OutboundTag,
   694  		},
   695  		Domain: domains,
   696  	}, nil
   697  }