2023-12-01 05:24:12 +00:00
|
|
|
package route
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"os"
|
2024-02-03 09:45:27 +00:00
|
|
|
"strings"
|
2023-12-01 05:24:12 +00:00
|
|
|
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
|
|
"github.com/sagernet/sing-box/common/srs"
|
|
|
|
C "github.com/sagernet/sing-box/constant"
|
|
|
|
"github.com/sagernet/sing-box/option"
|
2024-06-07 07:55:21 +00:00
|
|
|
"github.com/sagernet/sing/common"
|
|
|
|
"github.com/sagernet/sing/common/atomic"
|
2023-12-01 05:24:12 +00:00
|
|
|
E "github.com/sagernet/sing/common/exceptions"
|
2024-02-03 09:45:27 +00:00
|
|
|
F "github.com/sagernet/sing/common/format"
|
2023-12-01 12:15:11 +00:00
|
|
|
"github.com/sagernet/sing/common/json"
|
2024-06-07 07:55:21 +00:00
|
|
|
"github.com/sagernet/sing/common/x/list"
|
2024-10-05 01:28:58 +00:00
|
|
|
"github.com/sagernet/sing/service/filemanager"
|
2024-06-07 07:55:21 +00:00
|
|
|
|
|
|
|
"go4.org/netipx"
|
2023-12-01 05:24:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var _ adapter.RuleSet = (*LocalRuleSet)(nil)
|
|
|
|
|
|
|
|
type LocalRuleSet struct {
|
2024-06-07 07:55:21 +00:00
|
|
|
tag string
|
2023-12-01 05:24:12 +00:00
|
|
|
rules []adapter.HeadlessRule
|
|
|
|
metadata adapter.RuleSetMetadata
|
2024-06-07 07:55:21 +00:00
|
|
|
refs atomic.Int32
|
2023-12-01 05:24:12 +00:00
|
|
|
}
|
|
|
|
|
2024-10-05 01:28:58 +00:00
|
|
|
func NewLocalRuleSet(ctx context.Context, router adapter.Router, options option.RuleSet) (*LocalRuleSet, error) {
|
2023-12-01 05:24:12 +00:00
|
|
|
var plainRuleSet option.PlainRuleSet
|
|
|
|
switch options.Format {
|
|
|
|
case C.RuleSetFormatSource, "":
|
2024-10-05 01:28:58 +00:00
|
|
|
content, err := os.ReadFile(filemanager.BasePath(ctx, options.LocalOptions.Path))
|
2023-12-01 05:24:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
plainRuleSet = compat.Upgrade()
|
|
|
|
case C.RuleSetFormatBinary:
|
2024-10-05 01:28:58 +00:00
|
|
|
setFile, err := os.Open(filemanager.BasePath(ctx, options.LocalOptions.Path))
|
2023-12-01 05:24:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
plainRuleSet, err = srs.Read(setFile, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return nil, E.New("unknown rule set format: ", options.Format)
|
|
|
|
}
|
|
|
|
rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules))
|
|
|
|
var err error
|
|
|
|
for i, ruleOptions := range plainRuleSet.Rules {
|
|
|
|
rules[i], err = NewHeadlessRule(router, ruleOptions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, E.Cause(err, "parse rule_set.rules.[", i, "]")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var metadata adapter.RuleSetMetadata
|
|
|
|
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
|
|
|
|
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
|
2024-02-03 09:45:27 +00:00
|
|
|
metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
|
2024-06-07 07:55:21 +00:00
|
|
|
return &LocalRuleSet{tag: options.Tag, rules: rules, metadata: metadata}, nil
|
2023-12-01 05:24:12 +00:00
|
|
|
}
|
|
|
|
|
2024-06-07 07:55:21 +00:00
|
|
|
func (s *LocalRuleSet) Name() string {
|
|
|
|
return s.tag
|
2023-12-01 05:24:12 +00:00
|
|
|
}
|
|
|
|
|
2024-02-03 09:45:27 +00:00
|
|
|
func (s *LocalRuleSet) String() string {
|
|
|
|
return strings.Join(F.MapToString(s.rules), " ")
|
|
|
|
}
|
|
|
|
|
2023-12-01 05:24:12 +00:00
|
|
|
func (s *LocalRuleSet) StartContext(ctx context.Context, startContext adapter.RuleSetStartContext) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-06-07 07:55:21 +00:00
|
|
|
func (s *LocalRuleSet) PostStart() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-12-01 05:24:12 +00:00
|
|
|
func (s *LocalRuleSet) Metadata() adapter.RuleSetMetadata {
|
|
|
|
return s.metadata
|
|
|
|
}
|
|
|
|
|
2024-06-07 07:55:21 +00:00
|
|
|
func (s *LocalRuleSet) ExtractIPSet() []*netipx.IPSet {
|
|
|
|
return common.FlatMap(s.rules, extractIPSetFromRule)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LocalRuleSet) IncRef() {
|
|
|
|
s.refs.Add(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LocalRuleSet) DecRef() {
|
|
|
|
if s.refs.Add(-1) < 0 {
|
|
|
|
panic("rule-set: negative refs")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LocalRuleSet) Cleanup() {
|
|
|
|
if s.refs.Load() == 0 {
|
|
|
|
s.rules = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
|
|
|
|
}
|
|
|
|
|
2023-12-01 05:24:12 +00:00
|
|
|
func (s *LocalRuleSet) Close() error {
|
2024-06-07 07:55:21 +00:00
|
|
|
s.rules = nil
|
2023-12-01 05:24:12 +00:00
|
|
|
return nil
|
|
|
|
}
|
2024-06-07 07:55:21 +00:00
|
|
|
|
|
|
|
func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
|
|
|
|
for _, rule := range s.rules {
|
|
|
|
if rule.Match(metadata) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|