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