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

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