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