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