github.com/sagernet/sing-box@v1.2.7/route/rule.go (about) 1 package route 2 3 import ( 4 "strings" 5 6 "github.com/sagernet/sing-box/adapter" 7 C "github.com/sagernet/sing-box/constant" 8 "github.com/sagernet/sing-box/log" 9 "github.com/sagernet/sing-box/option" 10 "github.com/sagernet/sing/common" 11 E "github.com/sagernet/sing/common/exceptions" 12 F "github.com/sagernet/sing/common/format" 13 N "github.com/sagernet/sing/common/network" 14 ) 15 16 func NewRule(router adapter.Router, logger log.ContextLogger, options option.Rule) (adapter.Rule, error) { 17 switch options.Type { 18 case "", C.RuleTypeDefault: 19 if !options.DefaultOptions.IsValid() { 20 return nil, E.New("missing conditions") 21 } 22 if options.DefaultOptions.Outbound == "" { 23 return nil, E.New("missing outbound field") 24 } 25 return NewDefaultRule(router, logger, options.DefaultOptions) 26 case C.RuleTypeLogical: 27 if !options.LogicalOptions.IsValid() { 28 return nil, E.New("missing conditions") 29 } 30 if options.LogicalOptions.Outbound == "" { 31 return nil, E.New("missing outbound field") 32 } 33 return NewLogicalRule(router, logger, options.LogicalOptions) 34 default: 35 return nil, E.New("unknown rule type: ", options.Type) 36 } 37 } 38 39 var _ adapter.Rule = (*DefaultRule)(nil) 40 41 type DefaultRule struct { 42 items []RuleItem 43 sourceAddressItems []RuleItem 44 sourcePortItems []RuleItem 45 destinationAddressItems []RuleItem 46 destinationPortItems []RuleItem 47 allItems []RuleItem 48 invert bool 49 outbound string 50 } 51 52 type RuleItem interface { 53 Match(metadata *adapter.InboundContext) bool 54 String() string 55 } 56 57 func NewDefaultRule(router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) { 58 rule := &DefaultRule{ 59 invert: options.Invert, 60 outbound: options.Outbound, 61 } 62 if len(options.Inbound) > 0 { 63 item := NewInboundRule(options.Inbound) 64 rule.items = append(rule.items, item) 65 rule.allItems = append(rule.allItems, item) 66 } 67 if options.IPVersion > 0 { 68 switch options.IPVersion { 69 case 4, 6: 70 item := NewIPVersionItem(options.IPVersion == 6) 71 rule.items = append(rule.items, item) 72 rule.allItems = append(rule.allItems, item) 73 default: 74 return nil, E.New("invalid ip version: ", options.IPVersion) 75 } 76 } 77 if options.Network != "" { 78 switch options.Network { 79 case N.NetworkTCP, N.NetworkUDP: 80 item := NewNetworkItem(options.Network) 81 rule.items = append(rule.items, item) 82 rule.allItems = append(rule.allItems, item) 83 default: 84 return nil, E.New("invalid network: ", options.Network) 85 } 86 } 87 if len(options.AuthUser) > 0 { 88 item := NewAuthUserItem(options.AuthUser) 89 rule.items = append(rule.items, item) 90 rule.allItems = append(rule.allItems, item) 91 } 92 if len(options.Protocol) > 0 { 93 item := NewProtocolItem(options.Protocol) 94 rule.items = append(rule.items, item) 95 rule.allItems = append(rule.allItems, item) 96 } 97 if len(options.Domain) > 0 || len(options.DomainSuffix) > 0 { 98 item := NewDomainItem(options.Domain, options.DomainSuffix) 99 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 100 rule.allItems = append(rule.allItems, item) 101 } 102 if len(options.DomainKeyword) > 0 { 103 item := NewDomainKeywordItem(options.DomainKeyword) 104 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 105 rule.allItems = append(rule.allItems, item) 106 } 107 if len(options.DomainRegex) > 0 { 108 item, err := NewDomainRegexItem(options.DomainRegex) 109 if err != nil { 110 return nil, E.Cause(err, "domain_regex") 111 } 112 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 113 rule.allItems = append(rule.allItems, item) 114 } 115 if len(options.Geosite) > 0 { 116 item := NewGeositeItem(router, logger, options.Geosite) 117 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 118 rule.allItems = append(rule.allItems, item) 119 } 120 if len(options.SourceGeoIP) > 0 { 121 item := NewGeoIPItem(router, logger, true, options.SourceGeoIP) 122 rule.sourceAddressItems = append(rule.sourceAddressItems, item) 123 rule.allItems = append(rule.allItems, item) 124 } 125 if len(options.GeoIP) > 0 { 126 item := NewGeoIPItem(router, logger, false, options.GeoIP) 127 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 128 rule.allItems = append(rule.allItems, item) 129 } 130 if len(options.SourceIPCIDR) > 0 { 131 item, err := NewIPCIDRItem(true, options.SourceIPCIDR) 132 if err != nil { 133 return nil, E.Cause(err, "source_ipcidr") 134 } 135 rule.sourceAddressItems = append(rule.sourceAddressItems, item) 136 rule.allItems = append(rule.allItems, item) 137 } 138 if len(options.IPCIDR) > 0 { 139 item, err := NewIPCIDRItem(false, options.IPCIDR) 140 if err != nil { 141 return nil, E.Cause(err, "ipcidr") 142 } 143 rule.destinationAddressItems = append(rule.destinationAddressItems, item) 144 rule.allItems = append(rule.allItems, item) 145 } 146 if len(options.SourcePort) > 0 { 147 item := NewPortItem(true, options.SourcePort) 148 rule.sourcePortItems = append(rule.sourcePortItems, item) 149 rule.allItems = append(rule.allItems, item) 150 } 151 if len(options.SourcePortRange) > 0 { 152 item, err := NewPortRangeItem(true, options.SourcePortRange) 153 if err != nil { 154 return nil, E.Cause(err, "source_port_range") 155 } 156 rule.sourcePortItems = append(rule.sourcePortItems, item) 157 rule.allItems = append(rule.allItems, item) 158 } 159 if len(options.Port) > 0 { 160 item := NewPortItem(false, options.Port) 161 rule.destinationPortItems = append(rule.destinationPortItems, item) 162 rule.allItems = append(rule.allItems, item) 163 } 164 if len(options.PortRange) > 0 { 165 item, err := NewPortRangeItem(false, options.PortRange) 166 if err != nil { 167 return nil, E.Cause(err, "port_range") 168 } 169 rule.destinationPortItems = append(rule.destinationPortItems, item) 170 rule.allItems = append(rule.allItems, item) 171 } 172 if len(options.ProcessName) > 0 { 173 item := NewProcessItem(options.ProcessName) 174 rule.items = append(rule.items, item) 175 rule.allItems = append(rule.allItems, item) 176 } 177 if len(options.ProcessPath) > 0 { 178 item := NewProcessPathItem(options.ProcessPath) 179 rule.items = append(rule.items, item) 180 rule.allItems = append(rule.allItems, item) 181 } 182 if len(options.PackageName) > 0 { 183 item := NewPackageNameItem(options.PackageName) 184 rule.items = append(rule.items, item) 185 rule.allItems = append(rule.allItems, item) 186 } 187 if len(options.User) > 0 { 188 item := NewUserItem(options.User) 189 rule.items = append(rule.items, item) 190 rule.allItems = append(rule.allItems, item) 191 } 192 if len(options.UserID) > 0 { 193 item := NewUserIDItem(options.UserID) 194 rule.items = append(rule.items, item) 195 rule.allItems = append(rule.allItems, item) 196 } 197 if options.ClashMode != "" { 198 item := NewClashModeItem(router, options.ClashMode) 199 rule.items = append(rule.items, item) 200 rule.allItems = append(rule.allItems, item) 201 } 202 return rule, nil 203 } 204 205 func (r *DefaultRule) Type() string { 206 return C.RuleTypeDefault 207 } 208 209 func (r *DefaultRule) Start() error { 210 for _, item := range r.allItems { 211 err := common.Start(item) 212 if err != nil { 213 return err 214 } 215 } 216 return nil 217 } 218 219 func (r *DefaultRule) Close() error { 220 for _, item := range r.allItems { 221 err := common.Close(item) 222 if err != nil { 223 return err 224 } 225 } 226 return nil 227 } 228 229 func (r *DefaultRule) UpdateGeosite() error { 230 for _, item := range r.allItems { 231 if geositeItem, isSite := item.(*GeositeItem); isSite { 232 err := geositeItem.Update() 233 if err != nil { 234 return err 235 } 236 } 237 } 238 return nil 239 } 240 241 func (r *DefaultRule) Match(metadata *adapter.InboundContext) bool { 242 for _, item := range r.items { 243 if !item.Match(metadata) { 244 return r.invert 245 } 246 } 247 248 if len(r.sourceAddressItems) > 0 { 249 var sourceAddressMatch bool 250 for _, item := range r.sourceAddressItems { 251 if item.Match(metadata) { 252 sourceAddressMatch = true 253 break 254 } 255 } 256 if !sourceAddressMatch { 257 return r.invert 258 } 259 } 260 261 if len(r.sourcePortItems) > 0 { 262 var sourcePortMatch bool 263 for _, item := range r.sourcePortItems { 264 if item.Match(metadata) { 265 sourcePortMatch = true 266 break 267 } 268 } 269 if !sourcePortMatch { 270 return r.invert 271 } 272 } 273 274 if len(r.destinationAddressItems) > 0 { 275 var destinationAddressMatch bool 276 for _, item := range r.destinationAddressItems { 277 if item.Match(metadata) { 278 destinationAddressMatch = true 279 break 280 } 281 } 282 if !destinationAddressMatch { 283 return r.invert 284 } 285 } 286 287 if len(r.destinationPortItems) > 0 { 288 var destinationPortMatch bool 289 for _, item := range r.destinationPortItems { 290 if item.Match(metadata) { 291 destinationPortMatch = true 292 break 293 } 294 } 295 if !destinationPortMatch { 296 return r.invert 297 } 298 } 299 300 return !r.invert 301 } 302 303 func (r *DefaultRule) Outbound() string { 304 return r.outbound 305 } 306 307 func (r *DefaultRule) String() string { 308 if !r.invert { 309 return strings.Join(F.MapToString(r.allItems), " ") 310 } else { 311 return "!(" + strings.Join(F.MapToString(r.allItems), " ") + ")" 312 } 313 } 314 315 var _ adapter.Rule = (*LogicalRule)(nil) 316 317 type LogicalRule struct { 318 mode string 319 rules []*DefaultRule 320 invert bool 321 outbound string 322 } 323 324 func NewLogicalRule(router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) { 325 r := &LogicalRule{ 326 rules: make([]*DefaultRule, len(options.Rules)), 327 invert: options.Invert, 328 outbound: options.Outbound, 329 } 330 switch options.Mode { 331 case C.LogicalTypeAnd: 332 r.mode = C.LogicalTypeAnd 333 case C.LogicalTypeOr: 334 r.mode = C.LogicalTypeOr 335 default: 336 return nil, E.New("unknown logical mode: ", options.Mode) 337 } 338 for i, subRule := range options.Rules { 339 rule, err := NewDefaultRule(router, logger, subRule) 340 if err != nil { 341 return nil, E.Cause(err, "sub rule[", i, "]") 342 } 343 r.rules[i] = rule 344 } 345 return r, nil 346 } 347 348 func (r *LogicalRule) Type() string { 349 return C.RuleTypeLogical 350 } 351 352 func (r *LogicalRule) UpdateGeosite() error { 353 for _, rule := range r.rules { 354 err := rule.UpdateGeosite() 355 if err != nil { 356 return err 357 } 358 } 359 return nil 360 } 361 362 func (r *LogicalRule) Start() error { 363 for _, rule := range r.rules { 364 err := rule.Start() 365 if err != nil { 366 return err 367 } 368 } 369 return nil 370 } 371 372 func (r *LogicalRule) Close() error { 373 for _, rule := range r.rules { 374 err := rule.Close() 375 if err != nil { 376 return err 377 } 378 } 379 return nil 380 } 381 382 func (r *LogicalRule) Match(metadata *adapter.InboundContext) bool { 383 if r.mode == C.LogicalTypeAnd { 384 return common.All(r.rules, func(it *DefaultRule) bool { 385 return it.Match(metadata) 386 }) != r.invert 387 } else { 388 return common.Any(r.rules, func(it *DefaultRule) bool { 389 return it.Match(metadata) 390 }) != r.invert 391 } 392 } 393 394 func (r *LogicalRule) Outbound() string { 395 return r.outbound 396 } 397 398 func (r *LogicalRule) String() string { 399 var op string 400 switch r.mode { 401 case C.LogicalTypeAnd: 402 op = "&&" 403 case C.LogicalTypeOr: 404 op = "||" 405 } 406 if !r.invert { 407 return strings.Join(F.MapToString(r.rules), " "+op+" ") 408 } else { 409 return "!(" + strings.Join(F.MapToString(r.rules), " "+op+" ") + ")" 410 } 411 }