github.com/xraypb/xray-core@v1.6.6/app/router/condition.go (about)

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