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

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