github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/app/router/condition.go (about)

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