github.com/xtls/xray-core@v1.8.3/app/router/config.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/features/outbound"
     8  	"github.com/xtls/xray-core/features/routing"
     9  )
    10  
    11  // CIDRList is an alias of []*CIDR to provide sort.Interface.
    12  type CIDRList []*CIDR
    13  
    14  // Len implements sort.Interface.
    15  func (l *CIDRList) Len() int {
    16  	return len(*l)
    17  }
    18  
    19  // Less implements sort.Interface.
    20  func (l *CIDRList) Less(i int, j int) bool {
    21  	ci := (*l)[i]
    22  	cj := (*l)[j]
    23  
    24  	if len(ci.Ip) < len(cj.Ip) {
    25  		return true
    26  	}
    27  
    28  	if len(ci.Ip) > len(cj.Ip) {
    29  		return false
    30  	}
    31  
    32  	for k := 0; k < len(ci.Ip); k++ {
    33  		if ci.Ip[k] < cj.Ip[k] {
    34  			return true
    35  		}
    36  
    37  		if ci.Ip[k] > cj.Ip[k] {
    38  			return false
    39  		}
    40  	}
    41  
    42  	return ci.Prefix < cj.Prefix
    43  }
    44  
    45  // Swap implements sort.Interface.
    46  func (l *CIDRList) Swap(i int, j int) {
    47  	(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
    48  }
    49  
    50  type Rule struct {
    51  	Tag       string
    52  	Balancer  *Balancer
    53  	Condition Condition
    54  }
    55  
    56  func (r *Rule) GetTag() (string, error) {
    57  	if r.Balancer != nil {
    58  		return r.Balancer.PickOutbound()
    59  	}
    60  	return r.Tag, nil
    61  }
    62  
    63  // Apply checks rule matching of current routing context.
    64  func (r *Rule) Apply(ctx routing.Context) bool {
    65  	return r.Condition.Apply(ctx)
    66  }
    67  
    68  func (rr *RoutingRule) BuildCondition() (Condition, error) {
    69  	conds := NewConditionChan()
    70  
    71  	if len(rr.Domain) > 0 {
    72  		switch rr.DomainMatcher {
    73  		case "linear":
    74  			matcher, err := NewDomainMatcher(rr.Domain)
    75  			if err != nil {
    76  				return nil, newError("failed to build domain condition").Base(err)
    77  			}
    78  			conds.Add(matcher)
    79  		case "mph", "hybrid":
    80  			fallthrough
    81  		default:
    82  			matcher, err := NewMphMatcherGroup(rr.Domain)
    83  			if err != nil {
    84  				return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err)
    85  			}
    86  			newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
    87  			conds.Add(matcher)
    88  		}
    89  	}
    90  
    91  	if len(rr.UserEmail) > 0 {
    92  		conds.Add(NewUserMatcher(rr.UserEmail))
    93  	}
    94  
    95  	if len(rr.InboundTag) > 0 {
    96  		conds.Add(NewInboundTagMatcher(rr.InboundTag))
    97  	}
    98  
    99  	if rr.PortList != nil {
   100  		conds.Add(NewPortMatcher(rr.PortList, false))
   101  	} else if rr.PortRange != nil {
   102  		conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false))
   103  	}
   104  
   105  	if rr.SourcePortList != nil {
   106  		conds.Add(NewPortMatcher(rr.SourcePortList, true))
   107  	}
   108  
   109  	if len(rr.Networks) > 0 {
   110  		conds.Add(NewNetworkMatcher(rr.Networks))
   111  	} else if rr.NetworkList != nil {
   112  		conds.Add(NewNetworkMatcher(rr.NetworkList.Network))
   113  	}
   114  
   115  	if len(rr.Geoip) > 0 {
   116  		cond, err := NewMultiGeoIPMatcher(rr.Geoip, false)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  		conds.Add(cond)
   121  	} else if len(rr.Cidr) > 0 {
   122  		cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.Cidr}}, false)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  		conds.Add(cond)
   127  	}
   128  
   129  	if len(rr.SourceGeoip) > 0 {
   130  		cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		conds.Add(cond)
   135  	} else if len(rr.SourceCidr) > 0 {
   136  		cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.SourceCidr}}, true)
   137  		if err != nil {
   138  			return nil, err
   139  		}
   140  		conds.Add(cond)
   141  	}
   142  
   143  	if len(rr.Protocol) > 0 {
   144  		conds.Add(NewProtocolMatcher(rr.Protocol))
   145  	}
   146  
   147  	if len(rr.Attributes) > 0 {
   148  		configuredKeys := make(map[string]string)
   149  		for key, value := range rr.Attributes {
   150  			configuredKeys[strings.ToLower(key)] = strings.ToLower(value)
   151  		}
   152  		conds.Add(&AttributeMatcher{configuredKeys})
   153  	}
   154  
   155  	if conds.Len() == 0 {
   156  		return nil, newError("this rule has no effective fields").AtWarning()
   157  	}
   158  
   159  	return conds, nil
   160  }
   161  
   162  func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) {
   163  	switch br.Strategy {
   164  	case "leastPing":
   165  		return &Balancer{
   166  			selectors: br.OutboundSelector,
   167  			strategy:  &LeastPingStrategy{},
   168  			ohm:       ohm,
   169  		}, nil
   170  	case "random":
   171  		fallthrough
   172  	default:
   173  		return &Balancer{
   174  			selectors: br.OutboundSelector,
   175  			strategy:  &RandomStrategy{},
   176  			ohm:       ohm,
   177  		}, nil
   178  
   179  	}
   180  }