github.com/v2fly/v2ray-core/v4@v4.45.2/infra/conf/rule/rule.go (about)

     1  package rule
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/v2fly/v2ray-core/v4/app/router"
    10  	"github.com/v2fly/v2ray-core/v4/common/net"
    11  	"github.com/v2fly/v2ray-core/v4/infra/conf/cfgcommon"
    12  )
    13  
    14  //go:generate go run github.com/v2fly/v2ray-core/v4/common/errors/errorgen
    15  
    16  func parseDomainRule(ctx context.Context, domain string) ([]*router.Domain, error) {
    17  	cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx)
    18  	geoLoader := cfgEnv.GetGeoLoader()
    19  
    20  	if strings.HasPrefix(domain, "geosite:") {
    21  		list := domain[8:]
    22  		if len(list) == 0 {
    23  			return nil, newError("empty listname in rule: ", domain)
    24  		}
    25  		domains, err := geoLoader.LoadGeoSite(list)
    26  		if err != nil {
    27  			return nil, newError("failed to load geosite: ", list).Base(err)
    28  		}
    29  
    30  		return domains, nil
    31  	}
    32  
    33  	isExtDatFile := 0
    34  	{
    35  		const prefix = "ext:"
    36  		if strings.HasPrefix(domain, prefix) {
    37  			isExtDatFile = len(prefix)
    38  		}
    39  		const prefixQualified = "ext-domain:"
    40  		if strings.HasPrefix(domain, prefixQualified) {
    41  			isExtDatFile = len(prefixQualified)
    42  		}
    43  	}
    44  
    45  	if isExtDatFile != 0 {
    46  		kv := strings.Split(domain[isExtDatFile:], ":")
    47  		if len(kv) != 2 {
    48  			return nil, newError("invalid external resource: ", domain)
    49  		}
    50  		filename := kv[0]
    51  		list := kv[1]
    52  		domains, err := geoLoader.LoadGeoSiteWithAttr(filename, list)
    53  		if err != nil {
    54  			return nil, newError("failed to load external geosite: ", list, " from ", filename).Base(err)
    55  		}
    56  
    57  		return domains, nil
    58  	}
    59  
    60  	domainRule := new(router.Domain)
    61  	switch {
    62  	case strings.HasPrefix(domain, "regexp:"):
    63  		regexpVal := domain[7:]
    64  		if len(regexpVal) == 0 {
    65  			return nil, newError("empty regexp type of rule: ", domain)
    66  		}
    67  		domainRule.Type = router.Domain_Regex
    68  		domainRule.Value = regexpVal
    69  
    70  	case strings.HasPrefix(domain, "domain:"):
    71  		domainName := domain[7:]
    72  		if len(domainName) == 0 {
    73  			return nil, newError("empty domain type of rule: ", domain)
    74  		}
    75  		domainRule.Type = router.Domain_Domain
    76  		domainRule.Value = domainName
    77  
    78  	case strings.HasPrefix(domain, "full:"):
    79  		fullVal := domain[5:]
    80  		if len(fullVal) == 0 {
    81  			return nil, newError("empty full domain type of rule: ", domain)
    82  		}
    83  		domainRule.Type = router.Domain_Full
    84  		domainRule.Value = fullVal
    85  
    86  	case strings.HasPrefix(domain, "keyword:"):
    87  		keywordVal := domain[8:]
    88  		if len(keywordVal) == 0 {
    89  			return nil, newError("empty keyword type of rule: ", domain)
    90  		}
    91  		domainRule.Type = router.Domain_Plain
    92  		domainRule.Value = keywordVal
    93  
    94  	case strings.HasPrefix(domain, "dotless:"):
    95  		domainRule.Type = router.Domain_Regex
    96  		switch substr := domain[8:]; {
    97  		case substr == "":
    98  			domainRule.Value = "^[^.]*$"
    99  		case !strings.Contains(substr, "."):
   100  			domainRule.Value = "^[^.]*" + substr + "[^.]*$"
   101  		default:
   102  			return nil, newError("substr in dotless rule should not contain a dot: ", substr)
   103  		}
   104  
   105  	default:
   106  		domainRule.Type = router.Domain_Plain
   107  		domainRule.Value = domain
   108  	}
   109  	return []*router.Domain{domainRule}, nil
   110  }
   111  
   112  func toCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*router.GeoIP, error) {
   113  	cfgEnv := cfgcommon.GetConfigureLoadingEnvironment(ctx)
   114  	geoLoader := cfgEnv.GetGeoLoader()
   115  
   116  	var geoipList []*router.GeoIP
   117  	var customCidrs []*router.CIDR
   118  
   119  	for _, ip := range ips {
   120  		if strings.HasPrefix(ip, "geoip:") {
   121  			country := ip[6:]
   122  			isReverseMatch := false
   123  			if strings.HasPrefix(ip, "geoip:!") {
   124  				country = ip[7:]
   125  				isReverseMatch = true
   126  			}
   127  			if len(country) == 0 {
   128  				return nil, newError("empty country name in rule")
   129  			}
   130  			geoip, err := geoLoader.LoadGeoIP(country)
   131  			if err != nil {
   132  				return nil, newError("failed to load geoip: ", country).Base(err)
   133  			}
   134  
   135  			geoipList = append(geoipList, &router.GeoIP{
   136  				CountryCode:  strings.ToUpper(country),
   137  				Cidr:         geoip,
   138  				ReverseMatch: isReverseMatch,
   139  			})
   140  
   141  			continue
   142  		}
   143  
   144  		isExtDatFile := 0
   145  		{
   146  			const prefix = "ext:"
   147  			if strings.HasPrefix(ip, prefix) {
   148  				isExtDatFile = len(prefix)
   149  			}
   150  			const prefixQualified = "ext-ip:"
   151  			if strings.HasPrefix(ip, prefixQualified) {
   152  				isExtDatFile = len(prefixQualified)
   153  			}
   154  		}
   155  
   156  		if isExtDatFile != 0 {
   157  			kv := strings.Split(ip[isExtDatFile:], ":")
   158  			if len(kv) != 2 {
   159  				return nil, newError("invalid external resource: ", ip)
   160  			}
   161  
   162  			filename := kv[0]
   163  			country := kv[1]
   164  			if len(filename) == 0 || len(country) == 0 {
   165  				return nil, newError("empty filename or empty country in rule")
   166  			}
   167  
   168  			isReverseMatch := false
   169  			if strings.HasPrefix(country, "!") {
   170  				country = country[1:]
   171  				isReverseMatch = true
   172  			}
   173  			geoip, err := geoLoader.LoadIP(filename, country)
   174  			if err != nil {
   175  				return nil, newError("failed to load geoip: ", country, " from ", filename).Base(err)
   176  			}
   177  
   178  			geoipList = append(geoipList, &router.GeoIP{
   179  				CountryCode:  strings.ToUpper(filename + "_" + country),
   180  				Cidr:         geoip,
   181  				ReverseMatch: isReverseMatch,
   182  			})
   183  
   184  			continue
   185  		}
   186  
   187  		ipRule, err := ParseIP(ip)
   188  		if err != nil {
   189  			return nil, newError("invalid IP: ", ip).Base(err)
   190  		}
   191  		customCidrs = append(customCidrs, ipRule)
   192  	}
   193  
   194  	if len(customCidrs) > 0 {
   195  		geoipList = append(geoipList, &router.GeoIP{
   196  			Cidr: customCidrs,
   197  		})
   198  	}
   199  
   200  	return geoipList, nil
   201  }
   202  
   203  func parseFieldRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) {
   204  	type RawFieldRule struct {
   205  		RouterRule
   206  		Domain     *cfgcommon.StringList  `json:"domain"`
   207  		Domains    *cfgcommon.StringList  `json:"domains"`
   208  		IP         *cfgcommon.StringList  `json:"ip"`
   209  		Port       *cfgcommon.PortList    `json:"port"`
   210  		Network    *cfgcommon.NetworkList `json:"network"`
   211  		SourceIP   *cfgcommon.StringList  `json:"source"`
   212  		SourcePort *cfgcommon.PortList    `json:"sourcePort"`
   213  		User       *cfgcommon.StringList  `json:"user"`
   214  		InboundTag *cfgcommon.StringList  `json:"inboundTag"`
   215  		Protocols  *cfgcommon.StringList  `json:"protocol"`
   216  		Attributes string                 `json:"attrs"`
   217  	}
   218  	rawFieldRule := new(RawFieldRule)
   219  	err := json.Unmarshal(msg, rawFieldRule)
   220  	if err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	rule := new(router.RoutingRule)
   225  	switch {
   226  	case len(rawFieldRule.OutboundTag) > 0:
   227  		rule.TargetTag = &router.RoutingRule_Tag{
   228  			Tag: rawFieldRule.OutboundTag,
   229  		}
   230  	case len(rawFieldRule.BalancerTag) > 0:
   231  		rule.TargetTag = &router.RoutingRule_BalancingTag{
   232  			BalancingTag: rawFieldRule.BalancerTag,
   233  		}
   234  	default:
   235  		return nil, newError("neither outboundTag nor balancerTag is specified in routing rule")
   236  	}
   237  
   238  	if rawFieldRule.DomainMatcher != "" {
   239  		rule.DomainMatcher = rawFieldRule.DomainMatcher
   240  	}
   241  
   242  	if rawFieldRule.Domain != nil {
   243  		for _, domain := range *rawFieldRule.Domain {
   244  			rules, err := parseDomainRule(ctx, domain)
   245  			if err != nil {
   246  				return nil, newError("failed to parse domain rule: ", domain).Base(err)
   247  			}
   248  			rule.Domain = append(rule.Domain, rules...)
   249  		}
   250  	}
   251  
   252  	if rawFieldRule.Domains != nil {
   253  		for _, domain := range *rawFieldRule.Domains {
   254  			rules, err := parseDomainRule(ctx, domain)
   255  			if err != nil {
   256  				return nil, newError("failed to parse domain rule: ", domain).Base(err)
   257  			}
   258  			rule.Domain = append(rule.Domain, rules...)
   259  		}
   260  	}
   261  
   262  	if rawFieldRule.IP != nil {
   263  		geoipList, err := toCidrList(ctx, *rawFieldRule.IP)
   264  		if err != nil {
   265  			return nil, err
   266  		}
   267  		rule.Geoip = geoipList
   268  	}
   269  
   270  	if rawFieldRule.Port != nil {
   271  		rule.PortList = rawFieldRule.Port.Build()
   272  	}
   273  
   274  	if rawFieldRule.Network != nil {
   275  		rule.Networks = rawFieldRule.Network.Build()
   276  	}
   277  
   278  	if rawFieldRule.SourceIP != nil {
   279  		geoipList, err := toCidrList(ctx, *rawFieldRule.SourceIP)
   280  		if err != nil {
   281  			return nil, err
   282  		}
   283  		rule.SourceGeoip = geoipList
   284  	}
   285  
   286  	if rawFieldRule.SourcePort != nil {
   287  		rule.SourcePortList = rawFieldRule.SourcePort.Build()
   288  	}
   289  
   290  	if rawFieldRule.User != nil {
   291  		for _, s := range *rawFieldRule.User {
   292  			rule.UserEmail = append(rule.UserEmail, s)
   293  		}
   294  	}
   295  
   296  	if rawFieldRule.InboundTag != nil {
   297  		for _, s := range *rawFieldRule.InboundTag {
   298  			rule.InboundTag = append(rule.InboundTag, s)
   299  		}
   300  	}
   301  
   302  	if rawFieldRule.Protocols != nil {
   303  		for _, s := range *rawFieldRule.Protocols {
   304  			rule.Protocol = append(rule.Protocol, s)
   305  		}
   306  	}
   307  
   308  	if len(rawFieldRule.Attributes) > 0 {
   309  		rule.Attributes = rawFieldRule.Attributes
   310  	}
   311  
   312  	return rule, nil
   313  }
   314  
   315  func ParseRule(ctx context.Context, msg json.RawMessage) (*router.RoutingRule, error) {
   316  	rawRule := new(RouterRule)
   317  	err := json.Unmarshal(msg, rawRule)
   318  	if err != nil {
   319  		return nil, newError("invalid router rule").Base(err)
   320  	}
   321  	if strings.EqualFold(rawRule.Type, "field") {
   322  		fieldrule, err := parseFieldRule(ctx, msg)
   323  		if err != nil {
   324  			return nil, newError("invalid field rule").Base(err)
   325  		}
   326  		return fieldrule, nil
   327  	}
   328  
   329  	return nil, newError("unknown router rule type: ", rawRule.Type)
   330  }
   331  
   332  func ParseIP(s string) (*router.CIDR, error) {
   333  	var addr, mask string
   334  	i := strings.Index(s, "/")
   335  	if i < 0 {
   336  		addr = s
   337  	} else {
   338  		addr = s[:i]
   339  		mask = s[i+1:]
   340  	}
   341  	ip := net.ParseAddress(addr)
   342  	switch ip.Family() {
   343  	case net.AddressFamilyIPv4:
   344  		bits := uint32(32)
   345  		if len(mask) > 0 {
   346  			bits64, err := strconv.ParseUint(mask, 10, 32)
   347  			if err != nil {
   348  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   349  			}
   350  			bits = uint32(bits64)
   351  		}
   352  		if bits > 32 {
   353  			return nil, newError("invalid network mask for router: ", bits)
   354  		}
   355  		return &router.CIDR{
   356  			Ip:     []byte(ip.IP()),
   357  			Prefix: bits,
   358  		}, nil
   359  	case net.AddressFamilyIPv6:
   360  		bits := uint32(128)
   361  		if len(mask) > 0 {
   362  			bits64, err := strconv.ParseUint(mask, 10, 32)
   363  			if err != nil {
   364  				return nil, newError("invalid network mask for router: ", mask).Base(err)
   365  			}
   366  			bits = uint32(bits64)
   367  		}
   368  		if bits > 128 {
   369  			return nil, newError("invalid network mask for router: ", bits)
   370  		}
   371  		return &router.CIDR{
   372  			Ip:     []byte(ip.IP()),
   373  			Prefix: bits,
   374  		}, nil
   375  	default:
   376  		return nil, newError("unsupported address for router: ", s)
   377  	}
   378  }
   379  
   380  func ParseDomainRule(ctx context.Context, domain string) ([]*router.Domain, error) {
   381  	return parseDomainRule(ctx, domain)
   382  }
   383  
   384  func ToCidrList(ctx context.Context, ips cfgcommon.StringList) ([]*router.GeoIP, error) {
   385  	return toCidrList(ctx, ips)
   386  }
   387  
   388  type RouterRule struct {
   389  	Type        string `json:"type"`
   390  	OutboundTag string `json:"outboundTag"`
   391  	BalancerTag string `json:"balancerTag"`
   392  
   393  	DomainMatcher string `json:"domainMatcher"`
   394  }