mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-26 23:26:03 +03:00
bf4b1fab3c
In the past, we use Starlark script, it is over engineered and barely used. By switching to simple key value string contains logic we can reduce core size about 0.7MB
181 lines
3.8 KiB
Go
181 lines
3.8 KiB
Go
package router
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/features/outbound"
|
|
"github.com/xtls/xray-core/features/routing"
|
|
)
|
|
|
|
// CIDRList is an alias of []*CIDR to provide sort.Interface.
|
|
type CIDRList []*CIDR
|
|
|
|
// Len implements sort.Interface.
|
|
func (l *CIDRList) Len() int {
|
|
return len(*l)
|
|
}
|
|
|
|
// Less implements sort.Interface.
|
|
func (l *CIDRList) Less(i int, j int) bool {
|
|
ci := (*l)[i]
|
|
cj := (*l)[j]
|
|
|
|
if len(ci.Ip) < len(cj.Ip) {
|
|
return true
|
|
}
|
|
|
|
if len(ci.Ip) > len(cj.Ip) {
|
|
return false
|
|
}
|
|
|
|
for k := 0; k < len(ci.Ip); k++ {
|
|
if ci.Ip[k] < cj.Ip[k] {
|
|
return true
|
|
}
|
|
|
|
if ci.Ip[k] > cj.Ip[k] {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return ci.Prefix < cj.Prefix
|
|
}
|
|
|
|
// Swap implements sort.Interface.
|
|
func (l *CIDRList) Swap(i int, j int) {
|
|
(*l)[i], (*l)[j] = (*l)[j], (*l)[i]
|
|
}
|
|
|
|
type Rule struct {
|
|
Tag string
|
|
Balancer *Balancer
|
|
Condition Condition
|
|
}
|
|
|
|
func (r *Rule) GetTag() (string, error) {
|
|
if r.Balancer != nil {
|
|
return r.Balancer.PickOutbound()
|
|
}
|
|
return r.Tag, nil
|
|
}
|
|
|
|
// Apply checks rule matching of current routing context.
|
|
func (r *Rule) Apply(ctx routing.Context) bool {
|
|
return r.Condition.Apply(ctx)
|
|
}
|
|
|
|
func (rr *RoutingRule) BuildCondition() (Condition, error) {
|
|
conds := NewConditionChan()
|
|
|
|
if len(rr.Domain) > 0 {
|
|
switch rr.DomainMatcher {
|
|
case "linear":
|
|
matcher, err := NewDomainMatcher(rr.Domain)
|
|
if err != nil {
|
|
return nil, newError("failed to build domain condition").Base(err)
|
|
}
|
|
conds.Add(matcher)
|
|
case "mph", "hybrid":
|
|
fallthrough
|
|
default:
|
|
matcher, err := NewMphMatcherGroup(rr.Domain)
|
|
if err != nil {
|
|
return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err)
|
|
}
|
|
newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
|
|
conds.Add(matcher)
|
|
}
|
|
}
|
|
|
|
if len(rr.UserEmail) > 0 {
|
|
conds.Add(NewUserMatcher(rr.UserEmail))
|
|
}
|
|
|
|
if len(rr.InboundTag) > 0 {
|
|
conds.Add(NewInboundTagMatcher(rr.InboundTag))
|
|
}
|
|
|
|
if rr.PortList != nil {
|
|
conds.Add(NewPortMatcher(rr.PortList, false))
|
|
} else if rr.PortRange != nil {
|
|
conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false))
|
|
}
|
|
|
|
if rr.SourcePortList != nil {
|
|
conds.Add(NewPortMatcher(rr.SourcePortList, true))
|
|
}
|
|
|
|
if len(rr.Networks) > 0 {
|
|
conds.Add(NewNetworkMatcher(rr.Networks))
|
|
} else if rr.NetworkList != nil {
|
|
conds.Add(NewNetworkMatcher(rr.NetworkList.Network))
|
|
}
|
|
|
|
if len(rr.Geoip) > 0 {
|
|
cond, err := NewMultiGeoIPMatcher(rr.Geoip, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conds.Add(cond)
|
|
} else if len(rr.Cidr) > 0 {
|
|
cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.Cidr}}, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conds.Add(cond)
|
|
}
|
|
|
|
if len(rr.SourceGeoip) > 0 {
|
|
cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conds.Add(cond)
|
|
} else if len(rr.SourceCidr) > 0 {
|
|
cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.SourceCidr}}, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conds.Add(cond)
|
|
}
|
|
|
|
if len(rr.Protocol) > 0 {
|
|
conds.Add(NewProtocolMatcher(rr.Protocol))
|
|
}
|
|
|
|
if len(rr.Attributes) > 0 {
|
|
configuredKeys := make(map[string]string)
|
|
for key, value := range rr.Attributes {
|
|
configuredKeys[strings.ToLower(key)] = strings.ToLower(value)
|
|
}
|
|
conds.Add(&AttributeMatcher{configuredKeys})
|
|
}
|
|
|
|
if conds.Len() == 0 {
|
|
return nil, newError("this rule has no effective fields").AtWarning()
|
|
}
|
|
|
|
return conds, nil
|
|
}
|
|
|
|
func (br *BalancingRule) Build(ohm outbound.Manager) (*Balancer, error) {
|
|
switch br.Strategy {
|
|
case "leastPing":
|
|
return &Balancer{
|
|
selectors: br.OutboundSelector,
|
|
strategy: &LeastPingStrategy{},
|
|
ohm: ohm,
|
|
}, nil
|
|
case "random":
|
|
fallthrough
|
|
default:
|
|
return &Balancer{
|
|
selectors: br.OutboundSelector,
|
|
strategy: &RandomStrategy{},
|
|
ohm: ohm,
|
|
}, nil
|
|
|
|
}
|
|
}
|