github.com/Uhtred009/v2ray-core-1@v4.31.2+incompatible/infra/conf/router.go (about)

     1  package conf
     2  
     3  import (
     4  	"encoding/json"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"v2ray.com/core/app/router"
     9  	"v2ray.com/core/common/net"
    10  	"v2ray.com/core/common/platform/filesystem"
    11  
    12  	"github.com/golang/protobuf/proto"
    13  )
    14  
    15  type RouterRulesConfig struct {
    16  	RuleList       []json.RawMessage `json:"rules"`
    17  	DomainStrategy string            `json:"domainStrategy"`
    18  }
    19  
    20  type BalancingRule struct {
    21  	Tag       string     `json:"tag"`
    22  	Selectors StringList `json:"selector"`
    23  }
    24  
    25  func (r *BalancingRule) Build() (*router.BalancingRule, error) {
    26  	if r.Tag == "" {
    27  		return nil, newError("empty balancer tag")
    28  	}
    29  	if len(r.Selectors) == 0 {
    30  		return nil, newError("empty selector list")
    31  	}
    32  
    33  	return &router.BalancingRule{
    34  		Tag:              r.Tag,
    35  		OutboundSelector: []string(r.Selectors),
    36  	}, nil
    37  }
    38  
    39  type RouterConfig struct {
    40  	Settings       *RouterRulesConfig `json:"settings"` // Deprecated
    41  	RuleList       []json.RawMessage  `json:"rules"`
    42  	DomainStrategy *string            `json:"domainStrategy"`
    43  	Balancers      []*BalancingRule   `json:"balancers"`
    44  }
    45  
    46  func (c *RouterConfig) getDomainStrategy() router.Config_DomainStrategy {
    47  	ds := ""
    48  	if c.DomainStrategy != nil {
    49  		ds = *c.DomainStrategy
    50  	} else if c.Settings != nil {
    51  		ds = c.Settings.DomainStrategy
    52  	}
    53  
    54  	switch strings.ToLower(ds) {
    55  	case "alwaysip":
    56  		return router.Config_UseIp
    57  	case "ipifnonmatch":
    58  		return router.Config_IpIfNonMatch
    59  	case "ipondemand":
    60  		return router.Config_IpOnDemand
    61  	default:
    62  		return router.Config_AsIs
    63  	}
    64  }
    65  
    66  func (c *RouterConfig) Build() (*router.Config, error) {
    67  	config := new(router.Config)
    68  	config.DomainStrategy = c.getDomainStrategy()
    69  
    70  	rawRuleList := c.RuleList
    71  	if c.Settings != nil {
    72  		rawRuleList = append(c.RuleList, c.Settings.RuleList...)
    73  	}
    74  	for _, rawRule := range rawRuleList {
    75  		rule, err := ParseRule(rawRule)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		config.Rule = append(config.Rule, rule)
    80  	}
    81  	for _, rawBalancer := range c.Balancers {
    82  		balancer, err := rawBalancer.Build()
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  		config.BalancingRule = append(config.BalancingRule, balancer)
    87  	}
    88  	return config, nil
    89  }
    90  
    91  type RouterRule struct {
    92  	Type        string `json:"type"`
    93  	OutboundTag string `json:"outboundTag"`
    94  	BalancerTag string `json:"balancerTag"`
    95  }
    96  
    97  func ParseIP(s string) (*router.CIDR, error) {
    98  	var addr, mask string
    99  	i := strings.Index(s, "/")
   100  	if i < 0 {
   101  		addr = s
   102  	} else {
   103  		addr = s[:i]
   104  		mask = s[i+1:]
   105  	}
   106  	ip := net.ParseAddress(addr)
   107  	switch ip.Family() {
   108  	case net.AddressFamilyIPv4:
   109  		bits := uint32(32)
   110  		if len(mask) > 0 {
   111  			bits64, err := strconv.ParseUint(mask, 10, 32)
   112  			if err != nil {
   113  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   114  			}
   115  			bits = uint32(bits64)
   116  		}
   117  		if bits > 32 {
   118  			return nil, newError("invalid network mask for router: ", bits)
   119  		}
   120  		return &router.CIDR{
   121  			Ip:     []byte(ip.IP()),
   122  			Prefix: bits,
   123  		}, nil
   124  	case net.AddressFamilyIPv6:
   125  		bits := uint32(128)
   126  		if len(mask) > 0 {
   127  			bits64, err := strconv.ParseUint(mask, 10, 32)
   128  			if err != nil {
   129  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   130  			}
   131  			bits = uint32(bits64)
   132  		}
   133  		if bits > 128 {
   134  			return nil, newError("invalid network mask for router: ", bits)
   135  		}
   136  		return &router.CIDR{
   137  			Ip:     []byte(ip.IP()),
   138  			Prefix: bits,
   139  		}, nil
   140  	default:
   141  		return nil, newError("unsupported address for router: ", s)
   142  	}
   143  }
   144  
   145  func loadGeoIP(country string) ([]*router.CIDR, error) {
   146  	return loadIP("geoip.dat", country)
   147  }
   148  
   149  func loadIP(filename, country string) ([]*router.CIDR, error) {
   150  	geoipBytes, err := filesystem.ReadAsset(filename)
   151  	if err != nil {
   152  		return nil, newError("failed to open file: ", filename).Base(err)
   153  	}
   154  	var geoipList router.GeoIPList
   155  	if err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	for _, geoip := range geoipList.Entry {
   160  		if geoip.CountryCode == country {
   161  			return geoip.Cidr, nil
   162  		}
   163  	}
   164  
   165  	return nil, newError("country not found in ", filename, ": ", country)
   166  }
   167  
   168  func loadSite(filename, country string) ([]*router.Domain, error) {
   169  	geositeBytes, err := filesystem.ReadAsset(filename)
   170  	if err != nil {
   171  		return nil, newError("failed to open file: ", filename).Base(err)
   172  	}
   173  	var geositeList router.GeoSiteList
   174  	if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	for _, site := range geositeList.Entry {
   179  		if site.CountryCode == country {
   180  			return site.Domain, nil
   181  		}
   182  	}
   183  
   184  	return nil, newError("list not found in ", filename, ": ", country)
   185  }
   186  
   187  type AttributeMatcher interface {
   188  	Match(*router.Domain) bool
   189  }
   190  
   191  type BooleanMatcher string
   192  
   193  func (m BooleanMatcher) Match(domain *router.Domain) bool {
   194  	for _, attr := range domain.Attribute {
   195  		if attr.Key == string(m) {
   196  			return true
   197  		}
   198  	}
   199  	return false
   200  }
   201  
   202  type AttributeList struct {
   203  	matcher []AttributeMatcher
   204  }
   205  
   206  func (al *AttributeList) Match(domain *router.Domain) bool {
   207  	for _, matcher := range al.matcher {
   208  		if !matcher.Match(domain) {
   209  			return false
   210  		}
   211  	}
   212  	return true
   213  }
   214  
   215  func (al *AttributeList) IsEmpty() bool {
   216  	return len(al.matcher) == 0
   217  }
   218  
   219  func parseAttrs(attrs []string) *AttributeList {
   220  	al := new(AttributeList)
   221  	for _, attr := range attrs {
   222  		lc := strings.ToLower(attr)
   223  		al.matcher = append(al.matcher, BooleanMatcher(lc))
   224  	}
   225  	return al
   226  }
   227  
   228  func loadGeositeWithAttr(file string, siteWithAttr string) ([]*router.Domain, error) {
   229  	parts := strings.Split(siteWithAttr, "@")
   230  	if len(parts) == 0 {
   231  		return nil, newError("empty site")
   232  	}
   233  	country := strings.ToUpper(parts[0])
   234  	attrs := parseAttrs(parts[1:])
   235  	domains, err := loadSite(file, country)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	if attrs.IsEmpty() {
   241  		return domains, nil
   242  	}
   243  
   244  	filteredDomains := make([]*router.Domain, 0, len(domains))
   245  	for _, domain := range domains {
   246  		if attrs.Match(domain) {
   247  			filteredDomains = append(filteredDomains, domain)
   248  		}
   249  	}
   250  
   251  	return filteredDomains, nil
   252  }
   253  
   254  func parseDomainRule(domain string) ([]*router.Domain, error) {
   255  	if strings.HasPrefix(domain, "geosite:") {
   256  		country := strings.ToUpper(domain[8:])
   257  		domains, err := loadGeositeWithAttr("geosite.dat", country)
   258  		if err != nil {
   259  			return nil, newError("failed to load geosite: ", country).Base(err)
   260  		}
   261  		return domains, nil
   262  	}
   263  	var isExtDatFile = 0
   264  	{
   265  		const prefix = "ext:"
   266  		if strings.HasPrefix(domain, prefix) {
   267  			isExtDatFile = len(prefix)
   268  		}
   269  		const prefixQualified = "ext-domain:"
   270  		if strings.HasPrefix(domain, prefixQualified) {
   271  			isExtDatFile = len(prefixQualified)
   272  		}
   273  	}
   274  	if isExtDatFile != 0 {
   275  		kv := strings.Split(domain[isExtDatFile:], ":")
   276  		if len(kv) != 2 {
   277  			return nil, newError("invalid external resource: ", domain)
   278  		}
   279  		filename := kv[0]
   280  		country := kv[1]
   281  		domains, err := loadGeositeWithAttr(filename, country)
   282  		if err != nil {
   283  			return nil, newError("failed to load external sites: ", country, " from ", filename).Base(err)
   284  		}
   285  		return domains, nil
   286  	}
   287  
   288  	domainRule := new(router.Domain)
   289  	switch {
   290  	case strings.HasPrefix(domain, "regexp:"):
   291  		domainRule.Type = router.Domain_Regex
   292  		domainRule.Value = domain[7:]
   293  	case strings.HasPrefix(domain, "domain:"):
   294  		domainRule.Type = router.Domain_Domain
   295  		domainRule.Value = domain[7:]
   296  	case strings.HasPrefix(domain, "full:"):
   297  		domainRule.Type = router.Domain_Full
   298  		domainRule.Value = domain[5:]
   299  	case strings.HasPrefix(domain, "keyword:"):
   300  		domainRule.Type = router.Domain_Plain
   301  		domainRule.Value = domain[8:]
   302  	case strings.HasPrefix(domain, "dotless:"):
   303  		domainRule.Type = router.Domain_Regex
   304  		switch substr := domain[8:]; {
   305  		case substr == "":
   306  			domainRule.Value = "^[^.]*$"
   307  		case !strings.Contains(substr, "."):
   308  			domainRule.Value = "^[^.]*" + substr + "[^.]*$"
   309  		default:
   310  			return nil, newError("substr in dotless rule should not contain a dot: ", substr)
   311  		}
   312  	default:
   313  		domainRule.Type = router.Domain_Plain
   314  		domainRule.Value = domain
   315  	}
   316  	return []*router.Domain{domainRule}, nil
   317  }
   318  
   319  func toCidrList(ips StringList) ([]*router.GeoIP, error) {
   320  	var geoipList []*router.GeoIP
   321  	var customCidrs []*router.CIDR
   322  
   323  	for _, ip := range ips {
   324  		if strings.HasPrefix(ip, "geoip:") {
   325  			country := ip[6:]
   326  			geoip, err := loadGeoIP(strings.ToUpper(country))
   327  			if err != nil {
   328  				return nil, newError("failed to load GeoIP: ", country).Base(err)
   329  			}
   330  
   331  			geoipList = append(geoipList, &router.GeoIP{
   332  				CountryCode: strings.ToUpper(country),
   333  				Cidr:        geoip,
   334  			})
   335  			continue
   336  		}
   337  		var isExtDatFile = 0
   338  		{
   339  			const prefix = "ext:"
   340  			if strings.HasPrefix(ip, prefix) {
   341  				isExtDatFile = len(prefix)
   342  			}
   343  			const prefixQualified = "ext-ip:"
   344  			if strings.HasPrefix(ip, prefixQualified) {
   345  				isExtDatFile = len(prefixQualified)
   346  			}
   347  		}
   348  		if isExtDatFile != 0 {
   349  			kv := strings.Split(ip[isExtDatFile:], ":")
   350  			if len(kv) != 2 {
   351  				return nil, newError("invalid external resource: ", ip)
   352  			}
   353  
   354  			filename := kv[0]
   355  			country := kv[1]
   356  			geoip, err := loadIP(filename, strings.ToUpper(country))
   357  			if err != nil {
   358  				return nil, newError("failed to load IPs: ", country, " from ", filename).Base(err)
   359  			}
   360  
   361  			geoipList = append(geoipList, &router.GeoIP{
   362  				CountryCode: strings.ToUpper(filename + "_" + country),
   363  				Cidr:        geoip,
   364  			})
   365  
   366  			continue
   367  		}
   368  
   369  		ipRule, err := ParseIP(ip)
   370  		if err != nil {
   371  			return nil, newError("invalid IP: ", ip).Base(err)
   372  		}
   373  		customCidrs = append(customCidrs, ipRule)
   374  	}
   375  
   376  	if len(customCidrs) > 0 {
   377  		geoipList = append(geoipList, &router.GeoIP{
   378  			Cidr: customCidrs,
   379  		})
   380  	}
   381  
   382  	return geoipList, nil
   383  }
   384  
   385  func parseFieldRule(msg json.RawMessage) (*router.RoutingRule, error) {
   386  	type RawFieldRule struct {
   387  		RouterRule
   388  		Domain     *StringList  `json:"domain"`
   389  		IP         *StringList  `json:"ip"`
   390  		Port       *PortList    `json:"port"`
   391  		Network    *NetworkList `json:"network"`
   392  		SourceIP   *StringList  `json:"source"`
   393  		SourcePort *PortList    `json:"sourcePort"`
   394  		User       *StringList  `json:"user"`
   395  		InboundTag *StringList  `json:"inboundTag"`
   396  		Protocols  *StringList  `json:"protocol"`
   397  		Attributes string       `json:"attrs"`
   398  	}
   399  	rawFieldRule := new(RawFieldRule)
   400  	err := json.Unmarshal(msg, rawFieldRule)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  
   405  	rule := new(router.RoutingRule)
   406  	if len(rawFieldRule.OutboundTag) > 0 {
   407  		rule.TargetTag = &router.RoutingRule_Tag{
   408  			Tag: rawFieldRule.OutboundTag,
   409  		}
   410  	} else if len(rawFieldRule.BalancerTag) > 0 {
   411  		rule.TargetTag = &router.RoutingRule_BalancingTag{
   412  			BalancingTag: rawFieldRule.BalancerTag,
   413  		}
   414  	} else {
   415  		return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
   416  	}
   417  
   418  	if rawFieldRule.Domain != nil {
   419  		for _, domain := range *rawFieldRule.Domain {
   420  			rules, err := parseDomainRule(domain)
   421  			if err != nil {
   422  				return nil, newError("failed to parse domain rule: ", domain).Base(err)
   423  			}
   424  			rule.Domain = append(rule.Domain, rules...)
   425  		}
   426  	}
   427  
   428  	if rawFieldRule.IP != nil {
   429  		geoipList, err := toCidrList(*rawFieldRule.IP)
   430  		if err != nil {
   431  			return nil, err
   432  		}
   433  		rule.Geoip = geoipList
   434  	}
   435  
   436  	if rawFieldRule.Port != nil {
   437  		rule.PortList = rawFieldRule.Port.Build()
   438  	}
   439  
   440  	if rawFieldRule.Network != nil {
   441  		rule.Networks = rawFieldRule.Network.Build()
   442  	}
   443  
   444  	if rawFieldRule.SourceIP != nil {
   445  		geoipList, err := toCidrList(*rawFieldRule.SourceIP)
   446  		if err != nil {
   447  			return nil, err
   448  		}
   449  		rule.SourceGeoip = geoipList
   450  	}
   451  
   452  	if rawFieldRule.SourcePort != nil {
   453  		rule.SourcePortList = rawFieldRule.SourcePort.Build()
   454  	}
   455  
   456  	if rawFieldRule.User != nil {
   457  		for _, s := range *rawFieldRule.User {
   458  			rule.UserEmail = append(rule.UserEmail, s)
   459  		}
   460  	}
   461  
   462  	if rawFieldRule.InboundTag != nil {
   463  		for _, s := range *rawFieldRule.InboundTag {
   464  			rule.InboundTag = append(rule.InboundTag, s)
   465  		}
   466  	}
   467  
   468  	if rawFieldRule.Protocols != nil {
   469  		for _, s := range *rawFieldRule.Protocols {
   470  			rule.Protocol = append(rule.Protocol, s)
   471  		}
   472  	}
   473  
   474  	if len(rawFieldRule.Attributes) > 0 {
   475  		rule.Attributes = rawFieldRule.Attributes
   476  	}
   477  
   478  	return rule, nil
   479  }
   480  
   481  func ParseRule(msg json.RawMessage) (*router.RoutingRule, error) {
   482  	rawRule := new(RouterRule)
   483  	err := json.Unmarshal(msg, rawRule)
   484  	if err != nil {
   485  		return nil, newError("invalid router rule").Base(err)
   486  	}
   487  	if rawRule.Type == "field" {
   488  		fieldrule, err := parseFieldRule(msg)
   489  		if err != nil {
   490  			return nil, newError("invalid field rule").Base(err)
   491  		}
   492  		return fieldrule, nil
   493  	}
   494  	if rawRule.Type == "chinaip" {
   495  		chinaiprule, err := parseChinaIPRule(msg)
   496  		if err != nil {
   497  			return nil, newError("invalid chinaip rule").Base(err)
   498  		}
   499  		return chinaiprule, nil
   500  	}
   501  	if rawRule.Type == "chinasites" {
   502  		chinasitesrule, err := parseChinaSitesRule(msg)
   503  		if err != nil {
   504  			return nil, newError("invalid chinasites rule").Base(err)
   505  		}
   506  		return chinasitesrule, nil
   507  	}
   508  	return nil, newError("unknown router rule type: ", rawRule.Type)
   509  }
   510  
   511  func parseChinaIPRule(data []byte) (*router.RoutingRule, error) {
   512  	rawRule := new(RouterRule)
   513  	err := json.Unmarshal(data, rawRule)
   514  	if err != nil {
   515  		return nil, newError("invalid router rule").Base(err)
   516  	}
   517  	chinaIPs, err := loadGeoIP("CN")
   518  	if err != nil {
   519  		return nil, newError("failed to load geoip:cn").Base(err)
   520  	}
   521  	return &router.RoutingRule{
   522  		TargetTag: &router.RoutingRule_Tag{
   523  			Tag: rawRule.OutboundTag,
   524  		},
   525  		Cidr: chinaIPs,
   526  	}, nil
   527  }
   528  
   529  func parseChinaSitesRule(data []byte) (*router.RoutingRule, error) {
   530  	rawRule := new(RouterRule)
   531  	err := json.Unmarshal(data, rawRule)
   532  	if err != nil {
   533  		return nil, newError("invalid router rule").Base(err).AtError()
   534  	}
   535  	domains, err := loadGeositeWithAttr("geosite.dat", "CN")
   536  	if err != nil {
   537  		return nil, newError("failed to load geosite:cn.").Base(err)
   538  	}
   539  	return &router.RoutingRule{
   540  		TargetTag: &router.RoutingRule_Tag{
   541  			Tag: rawRule.OutboundTag,
   542  		},
   543  		Domain: domains,
   544  	}, nil
   545  }