github.com/v2fly/v2ray-core/v4@v4.45.2/app/router/condition.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package router
     5  
     6  import (
     7  	"strings"
     8  
     9  	"go.starlark.net/starlark"
    10  	"go.starlark.net/syntax"
    11  
    12  	"github.com/v2fly/v2ray-core/v4/common/net"
    13  	"github.com/v2fly/v2ray-core/v4/common/strmatcher"
    14  	"github.com/v2fly/v2ray-core/v4/features/routing"
    15  )
    16  
    17  type Condition interface {
    18  	Apply(ctx routing.Context) bool
    19  }
    20  
    21  type ConditionChan []Condition
    22  
    23  func NewConditionChan() *ConditionChan {
    24  	var condChan ConditionChan = make([]Condition, 0, 8)
    25  	return &condChan
    26  }
    27  
    28  func (v *ConditionChan) Add(cond Condition) *ConditionChan {
    29  	*v = append(*v, cond)
    30  	return v
    31  }
    32  
    33  // Apply applies all conditions registered in this chan.
    34  func (v *ConditionChan) Apply(ctx routing.Context) bool {
    35  	for _, cond := range *v {
    36  		if !cond.Apply(ctx) {
    37  			return false
    38  		}
    39  	}
    40  	return true
    41  }
    42  
    43  func (v *ConditionChan) Len() int {
    44  	return len(*v)
    45  }
    46  
    47  var matcherTypeMap = map[Domain_Type]strmatcher.Type{
    48  	Domain_Plain:  strmatcher.Substr,
    49  	Domain_Regex:  strmatcher.Regex,
    50  	Domain_Domain: strmatcher.Domain,
    51  	Domain_Full:   strmatcher.Full,
    52  }
    53  
    54  func domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {
    55  	matcherType, f := matcherTypeMap[domain.Type]
    56  	if !f {
    57  		return nil, newError("unsupported domain type", domain.Type)
    58  	}
    59  
    60  	matcher, err := matcherType.New(domain.Value)
    61  	if err != nil {
    62  		return nil, newError("failed to create domain matcher").Base(err)
    63  	}
    64  
    65  	return matcher, nil
    66  }
    67  
    68  type DomainMatcher struct {
    69  	matchers strmatcher.IndexMatcher
    70  }
    71  
    72  func NewMphMatcherGroup(domains []*Domain) (*DomainMatcher, error) {
    73  	g := strmatcher.NewMphMatcherGroup()
    74  	for _, d := range domains {
    75  		matcherType, f := matcherTypeMap[d.Type]
    76  		if !f {
    77  			return nil, newError("unsupported domain type", d.Type)
    78  		}
    79  		_, err := g.AddPattern(d.Value, matcherType)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  	}
    84  	g.Build()
    85  	return &DomainMatcher{
    86  		matchers: g,
    87  	}, nil
    88  }
    89  
    90  func NewDomainMatcher(domains []*Domain) (*DomainMatcher, error) {
    91  	g := new(strmatcher.MatcherGroup)
    92  	for _, d := range domains {
    93  		m, err := domainToMatcher(d)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		g.Add(m)
    98  	}
    99  
   100  	return &DomainMatcher{
   101  		matchers: g,
   102  	}, nil
   103  }
   104  
   105  func (m *DomainMatcher) ApplyDomain(domain string) bool {
   106  	return len(m.matchers.Match(strings.ToLower(domain))) > 0
   107  }
   108  
   109  // Apply implements Condition.
   110  func (m *DomainMatcher) Apply(ctx routing.Context) bool {
   111  	domain := ctx.GetTargetDomain()
   112  	if len(domain) == 0 {
   113  		return false
   114  	}
   115  	return m.ApplyDomain(domain)
   116  }
   117  
   118  type MultiGeoIPMatcher struct {
   119  	matchers []*GeoIPMatcher
   120  	onSource bool
   121  }
   122  
   123  func NewMultiGeoIPMatcher(geoips []*GeoIP, onSource bool) (*MultiGeoIPMatcher, error) {
   124  	var matchers []*GeoIPMatcher
   125  	for _, geoip := range geoips {
   126  		matcher, err := globalGeoIPContainer.Add(geoip)
   127  		if err != nil {
   128  			return nil, err
   129  		}
   130  		matchers = append(matchers, matcher)
   131  	}
   132  
   133  	matcher := &MultiGeoIPMatcher{
   134  		matchers: matchers,
   135  		onSource: onSource,
   136  	}
   137  
   138  	return matcher, nil
   139  }
   140  
   141  // Apply implements Condition.
   142  func (m *MultiGeoIPMatcher) Apply(ctx routing.Context) bool {
   143  	var ips []net.IP
   144  	if m.onSource {
   145  		ips = ctx.GetSourceIPs()
   146  	} else {
   147  		ips = ctx.GetTargetIPs()
   148  	}
   149  	for _, ip := range ips {
   150  		for _, matcher := range m.matchers {
   151  			if matcher.Match(ip) {
   152  				return true
   153  			}
   154  		}
   155  	}
   156  	return false
   157  }
   158  
   159  type PortMatcher struct {
   160  	port     net.MemoryPortList
   161  	onSource bool
   162  }
   163  
   164  // NewPortMatcher create a new port matcher that can match source or destination port
   165  func NewPortMatcher(list *net.PortList, onSource bool) *PortMatcher {
   166  	return &PortMatcher{
   167  		port:     net.PortListFromProto(list),
   168  		onSource: onSource,
   169  	}
   170  }
   171  
   172  // Apply implements Condition.
   173  func (v *PortMatcher) Apply(ctx routing.Context) bool {
   174  	if v.onSource {
   175  		return v.port.Contains(ctx.GetSourcePort())
   176  	}
   177  	return v.port.Contains(ctx.GetTargetPort())
   178  }
   179  
   180  type NetworkMatcher struct {
   181  	list [8]bool
   182  }
   183  
   184  func NewNetworkMatcher(network []net.Network) NetworkMatcher {
   185  	var matcher NetworkMatcher
   186  	for _, n := range network {
   187  		matcher.list[int(n)] = true
   188  	}
   189  	return matcher
   190  }
   191  
   192  // Apply implements Condition.
   193  func (v NetworkMatcher) Apply(ctx routing.Context) bool {
   194  	return v.list[int(ctx.GetNetwork())]
   195  }
   196  
   197  type UserMatcher struct {
   198  	user []string
   199  }
   200  
   201  func NewUserMatcher(users []string) *UserMatcher {
   202  	usersCopy := make([]string, 0, len(users))
   203  	for _, user := range users {
   204  		if len(user) > 0 {
   205  			usersCopy = append(usersCopy, user)
   206  		}
   207  	}
   208  	return &UserMatcher{
   209  		user: usersCopy,
   210  	}
   211  }
   212  
   213  // Apply implements Condition.
   214  func (v *UserMatcher) Apply(ctx routing.Context) bool {
   215  	user := ctx.GetUser()
   216  	if len(user) == 0 {
   217  		return false
   218  	}
   219  	for _, u := range v.user {
   220  		if u == user {
   221  			return true
   222  		}
   223  	}
   224  	return false
   225  }
   226  
   227  type InboundTagMatcher struct {
   228  	tags []string
   229  }
   230  
   231  func NewInboundTagMatcher(tags []string) *InboundTagMatcher {
   232  	tagsCopy := make([]string, 0, len(tags))
   233  	for _, tag := range tags {
   234  		if len(tag) > 0 {
   235  			tagsCopy = append(tagsCopy, tag)
   236  		}
   237  	}
   238  	return &InboundTagMatcher{
   239  		tags: tagsCopy,
   240  	}
   241  }
   242  
   243  // Apply implements Condition.
   244  func (v *InboundTagMatcher) Apply(ctx routing.Context) bool {
   245  	tag := ctx.GetInboundTag()
   246  	if len(tag) == 0 {
   247  		return false
   248  	}
   249  	for _, t := range v.tags {
   250  		if t == tag {
   251  			return true
   252  		}
   253  	}
   254  	return false
   255  }
   256  
   257  type ProtocolMatcher struct {
   258  	protocols []string
   259  }
   260  
   261  func NewProtocolMatcher(protocols []string) *ProtocolMatcher {
   262  	pCopy := make([]string, 0, len(protocols))
   263  
   264  	for _, p := range protocols {
   265  		if len(p) > 0 {
   266  			pCopy = append(pCopy, p)
   267  		}
   268  	}
   269  
   270  	return &ProtocolMatcher{
   271  		protocols: pCopy,
   272  	}
   273  }
   274  
   275  // Apply implements Condition.
   276  func (m *ProtocolMatcher) Apply(ctx routing.Context) bool {
   277  	protocol := ctx.GetProtocol()
   278  	if len(protocol) == 0 {
   279  		return false
   280  	}
   281  	for _, p := range m.protocols {
   282  		if strings.HasPrefix(protocol, p) {
   283  			return true
   284  		}
   285  	}
   286  	return false
   287  }
   288  
   289  type AttributeMatcher struct {
   290  	program *starlark.Program
   291  }
   292  
   293  func NewAttributeMatcher(code string) (*AttributeMatcher, error) {
   294  	starFile, err := syntax.Parse("attr.star", "satisfied=("+code+")", 0)
   295  	if err != nil {
   296  		return nil, newError("attr rule").Base(err)
   297  	}
   298  	p, err := starlark.FileProgram(starFile, func(name string) bool {
   299  		return name == "attrs"
   300  	})
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	return &AttributeMatcher{
   305  		program: p,
   306  	}, nil
   307  }
   308  
   309  // Match implements attributes matching.
   310  func (m *AttributeMatcher) Match(attrs map[string]string) bool {
   311  	attrsDict := new(starlark.Dict)
   312  	for key, value := range attrs {
   313  		attrsDict.SetKey(starlark.String(key), starlark.String(value))
   314  	}
   315  
   316  	predefined := make(starlark.StringDict)
   317  	predefined["attrs"] = attrsDict
   318  
   319  	thread := &starlark.Thread{
   320  		Name: "matcher",
   321  	}
   322  	results, err := m.program.Init(thread, predefined)
   323  	if err != nil {
   324  		newError("attr matcher").Base(err).WriteToLog()
   325  	}
   326  	satisfied := results["satisfied"]
   327  	return satisfied != nil && bool(satisfied.Truth())
   328  }
   329  
   330  // Apply implements Condition.
   331  func (m *AttributeMatcher) Apply(ctx routing.Context) bool {
   332  	attributes := ctx.GetAttributes()
   333  	if attributes == nil {
   334  		return false
   335  	}
   336  	return m.Match(attributes)
   337  }