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 }