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