rule_set_local.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package route
  2. import (
  3. "context"
  4. "os"
  5. "strings"
  6. "github.com/sagernet/sing-box/adapter"
  7. "github.com/sagernet/sing-box/common/srs"
  8. C "github.com/sagernet/sing-box/constant"
  9. "github.com/sagernet/sing-box/option"
  10. "github.com/sagernet/sing/common"
  11. "github.com/sagernet/sing/common/atomic"
  12. E "github.com/sagernet/sing/common/exceptions"
  13. F "github.com/sagernet/sing/common/format"
  14. "github.com/sagernet/sing/common/json"
  15. "github.com/sagernet/sing/common/x/list"
  16. "github.com/sagernet/sing/service/filemanager"
  17. "go4.org/netipx"
  18. )
  19. var _ adapter.RuleSet = (*LocalRuleSet)(nil)
  20. type LocalRuleSet struct {
  21. tag string
  22. rules []adapter.HeadlessRule
  23. metadata adapter.RuleSetMetadata
  24. refs atomic.Int32
  25. }
  26. func NewLocalRuleSet(ctx context.Context, router adapter.Router, options option.RuleSet) (*LocalRuleSet, error) {
  27. var plainRuleSet option.PlainRuleSet
  28. switch options.Format {
  29. case C.RuleSetFormatSource, "":
  30. content, err := os.ReadFile(filemanager.BasePath(ctx, options.LocalOptions.Path))
  31. if err != nil {
  32. return nil, err
  33. }
  34. compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
  35. if err != nil {
  36. return nil, err
  37. }
  38. plainRuleSet = compat.Upgrade()
  39. case C.RuleSetFormatBinary:
  40. setFile, err := os.Open(filemanager.BasePath(ctx, options.LocalOptions.Path))
  41. if err != nil {
  42. return nil, err
  43. }
  44. plainRuleSet, err = srs.Read(setFile, false)
  45. if err != nil {
  46. return nil, err
  47. }
  48. default:
  49. return nil, E.New("unknown rule set format: ", options.Format)
  50. }
  51. rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
  52. var err error
  53. for i, ruleOptions := range plainRuleSet.Rules {
  54. rules[i], err = NewHeadlessRule(router, ruleOptions)
  55. if err != nil {
  56. return nil, E.Cause(err, "parse rule_set.rules.[", i, "]")
  57. }
  58. }
  59. var metadata adapter.RuleSetMetadata
  60. metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
  61. metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
  62. metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
  63. return &LocalRuleSet{tag: options.Tag, rules: rules, metadata: metadata}, nil
  64. }
  65. func (s *LocalRuleSet) Name() string {
  66. return s.tag
  67. }
  68. func (s *LocalRuleSet) String() string {
  69. return strings.Join(F.MapToString(s.rules), " ")
  70. }
  71. func (s *LocalRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
  72. return nil
  73. }
  74. func (s *LocalRuleSet) PostStart() error {
  75. return nil
  76. }
  77. func (s *LocalRuleSet) Metadata() adapter.RuleSetMetadata {
  78. return s.metadata
  79. }
  80. func (s *LocalRuleSet) ExtractIPSet() []*netipx.IPSet {
  81. return common.FlatMap(s.rules, extractIPSetFromRule)
  82. }
  83. func (s *LocalRuleSet) IncRef() {
  84. s.refs.Add(1)
  85. }
  86. func (s *LocalRuleSet) DecRef() {
  87. if s.refs.Add(-1) < 0 {
  88. panic("rule-set: negative refs")
  89. }
  90. }
  91. func (s *LocalRuleSet) Cleanup() {
  92. if s.refs.Load() == 0 {
  93. s.rules = nil
  94. }
  95. }
  96. func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
  97. return nil
  98. }
  99. func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
  100. }
  101. func (s *LocalRuleSet) Close() error {
  102. s.rules = nil
  103. return nil
  104. }
  105. func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
  106. for _, rule := range s.rules {
  107. if rule.Match(metadata) {
  108. return true
  109. }
  110. }
  111. return false
  112. }