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  }