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 }