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

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