diff --git a/cmd/sing-box/cmd_rule_set_compile.go b/cmd/sing-box/cmd_rule_set_compile.go index 4fae4d99..0c44a2a1 100644 --- a/cmd/sing-box/cmd_rule_set_compile.go +++ b/cmd/sing-box/cmd_rule_set_compile.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/sagernet/sing-box/common/srs" - C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/json" @@ -56,10 +55,6 @@ func compileRuleSet(sourcePath string) error { if err != nil { return err } - ruleSet, err := plainRuleSet.Upgrade() - if err != nil { - return err - } var outputPath string if flagRuleSetCompileOutput == flagRuleSetCompileDefaultOutput { if strings.HasSuffix(sourcePath, ".json") { @@ -74,7 +69,7 @@ func compileRuleSet(sourcePath string) error { if err != nil { return err } - err = srs.Write(outputFile, ruleSet, plainRuleSet.Version == C.RuleSetVersion2) + err = srs.Write(outputFile, plainRuleSet.Options, plainRuleSet.Version) if err != nil { outputFile.Close() os.Remove(outputPath) diff --git a/cmd/sing-box/cmd_rule_set_convert.go b/cmd/sing-box/cmd_rule_set_convert.go index 8d2092ea..320149cf 100644 --- a/cmd/sing-box/cmd_rule_set_convert.go +++ b/cmd/sing-box/cmd_rule_set_convert.go @@ -7,6 +7,7 @@ import ( "github.com/sagernet/sing-box/cmd/sing-box/internal/convertor/adguard" "github.com/sagernet/sing-box/common/srs" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" @@ -77,7 +78,7 @@ func convertRuleSet(sourcePath string) error { return err } defer outputFile.Close() - err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, true) + err = srs.Write(outputFile, option.PlainRuleSet{Rules: rules}, C.RuleSetVersion2) if err != nil { outputFile.Close() os.Remove(outputPath) diff --git a/cmd/sing-box/cmd_rule_set_decompile.go b/cmd/sing-box/cmd_rule_set_decompile.go index 02af03dd..10a67bcf 100644 --- a/cmd/sing-box/cmd_rule_set_decompile.go +++ b/cmd/sing-box/cmd_rule_set_decompile.go @@ -6,9 +6,7 @@ import ( "strings" "github.com/sagernet/sing-box/common/srs" - C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common/json" "github.com/spf13/cobra" @@ -48,14 +46,10 @@ func decompileRuleSet(sourcePath string) error { return err } } - plainRuleSet, err := srs.Read(reader, true) + ruleSet, err := srs.Read(reader, true) if err != nil { return err } - ruleSet := option.PlainRuleSetCompat{ - Version: C.RuleSetVersion1, - Options: plainRuleSet, - } var outputPath string if flagRuleSetDecompileOutput == flagRuleSetDecompileDefaultOutput { if strings.HasSuffix(sourcePath, ".srs") { diff --git a/cmd/sing-box/cmd_rule_set_match.go b/cmd/sing-box/cmd_rule_set_match.go index 937458f2..7cded86e 100644 --- a/cmd/sing-box/cmd_rule_set_match.go +++ b/cmd/sing-box/cmd_rule_set_match.go @@ -55,26 +55,25 @@ func ruleSetMatch(sourcePath string, domain string) error { if err != nil { return E.Cause(err, "read rule-set") } - var plainRuleSet option.PlainRuleSet + var ruleSet option.PlainRuleSetCompat switch flagRuleSetMatchFormat { case C.RuleSetFormatSource: - var compat option.PlainRuleSetCompat - compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) - if err != nil { - return err - } - plainRuleSet, err = compat.Upgrade() + ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) if err != nil { return err } case C.RuleSetFormatBinary: - plainRuleSet, err = srs.Read(bytes.NewReader(content), false) + ruleSet, err = srs.Read(bytes.NewReader(content), false) if err != nil { return err } default: return E.New("unknown rule-set format: ", flagRuleSetMatchFormat) } + plainRuleSet, err := ruleSet.Upgrade() + if err != nil { + return err + } ipAddress := M.ParseAddr(domain) var metadata adapter.InboundContext if ipAddress.IsValid() { diff --git a/common/srs/binary.go b/common/srs/binary.go index 489dc22e..dd670fc8 100644 --- a/common/srs/binary.go +++ b/common/srs/binary.go @@ -41,7 +41,7 @@ const ( ruleItemFinal uint8 = 0xFF ) -func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err error) { +func Read(reader io.Reader, recover bool) (ruleSetCompat option.PlainRuleSetCompat, err error) { var magicBytes [3]byte _, err = io.ReadFull(reader, magicBytes[:]) if err != nil { @@ -54,10 +54,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro var version uint8 err = binary.Read(reader, binary.BigEndian, &version) if err != nil { - return ruleSet, err + return ruleSetCompat, err } - if version > C.RuleSetVersion2 { - return ruleSet, E.New("unsupported version: ", version) + if version > C.RuleSetVersionCurrent { + return ruleSetCompat, E.New("unsupported version: ", version) } compressReader, err := zlib.NewReader(reader) if err != nil { @@ -68,9 +68,10 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro if err != nil { return } - ruleSet.Rules = make([]option.HeadlessRule, length) + ruleSetCompat.Version = version + ruleSetCompat.Options.Rules = make([]option.HeadlessRule, length) for i := uint64(0); i < length; i++ { - ruleSet.Rules[i], err = readRule(bReader, recover) + ruleSetCompat.Options.Rules[i], err = readRule(bReader, recover) if err != nil { err = E.Cause(err, "read rule[", i, "]") return @@ -79,18 +80,12 @@ func Read(reader io.Reader, recover bool) (ruleSet option.PlainRuleSet, err erro return } -func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool) error { +func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateVersion uint8) error { _, err := writer.Write(MagicBytes[:]) if err != nil { return err } - var version uint8 - if generateUnstable { - version = C.RuleSetVersion2 - } else { - version = C.RuleSetVersion1 - } - err = binary.Write(writer, binary.BigEndian, version) + err = binary.Write(writer, binary.BigEndian, generateVersion) if err != nil { return err } @@ -104,7 +99,7 @@ func Write(writer io.Writer, ruleSet option.PlainRuleSet, generateUnstable bool) return err } for _, rule := range ruleSet.Rules { - err = writeRule(bWriter, rule, generateUnstable) + err = writeRule(bWriter, rule, generateVersion) if err != nil { return err } @@ -135,12 +130,12 @@ func readRule(reader varbin.Reader, recover bool) (rule option.HeadlessRule, err return } -func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateUnstable bool) error { +func writeRule(writer varbin.Writer, rule option.HeadlessRule, generateVersion uint8) error { switch rule.Type { case C.RuleTypeDefault: - return writeDefaultRule(writer, rule.DefaultOptions, generateUnstable) + return writeDefaultRule(writer, rule.DefaultOptions, generateVersion) case C.RuleTypeLogical: - return writeLogicalRule(writer, rule.LogicalOptions, generateUnstable) + return writeLogicalRule(writer, rule.LogicalOptions, generateVersion) default: panic("unknown rule type: " + rule.Type) } @@ -240,7 +235,7 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea } } -func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateUnstable bool) error { +func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, generateVersion uint8) error { err := binary.Write(writer, binary.BigEndian, uint8(0)) if err != nil { return err @@ -264,7 +259,7 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen if err != nil { return err } - err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, !generateUnstable).Write(writer) + err = domain.NewMatcher(rule.Domain, rule.DomainSuffix, generateVersion == C.RuleSetVersion1).Write(writer) if err != nil { return err } @@ -354,6 +349,9 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen } } if len(rule.AdGuardDomain) > 0 { + if generateVersion < C.RuleSetVersion2 { + return E.New("AdGuard rule items is only supported in version 2 or later") + } err = binary.Write(writer, binary.BigEndian, ruleItemAdGuardDomain) if err != nil { return err @@ -457,7 +455,7 @@ func readLogicalRule(reader varbin.Reader, recovery bool) (logicalRule option.Lo return } -func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateUnstable bool) error { +func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRule, generateVersion uint8) error { err := binary.Write(writer, binary.BigEndian, uint8(1)) if err != nil { return err @@ -478,7 +476,7 @@ func writeLogicalRule(writer varbin.Writer, logicalRule option.LogicalHeadlessRu return err } for _, rule := range logicalRule.Rules { - err = writeRule(writer, rule, generateUnstable) + err = writeRule(writer, rule, generateVersion) if err != nil { return err } diff --git a/constant/rule.go b/constant/rule.go index 718e79a5..086b95a0 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -21,4 +21,5 @@ const ( const ( RuleSetVersion1 = 1 + iota RuleSetVersion2 + RuleSetVersionCurrent = RuleSetVersion2 ) diff --git a/option/rule_set.go b/option/rule_set.go index d4368de3..e0f10bf1 100644 --- a/option/rule_set.go +++ b/option/rule_set.go @@ -189,7 +189,7 @@ func (r LogicalHeadlessRule) IsValid() bool { } type _PlainRuleSetCompat struct { - Version int `json:"version"` + Version uint8 `json:"version"` Options PlainRuleSet `json:"-"` } diff --git a/route/rule_set_local.go b/route/rule_set_local.go index 4485fbad..c3ecf9ca 100644 --- a/route/rule_set_local.go +++ b/route/rule_set_local.go @@ -95,33 +95,34 @@ func (s *LocalRuleSet) StartContext(ctx context.Context, startContext *adapter.H } func (s *LocalRuleSet) reloadFile(path string) error { - var plainRuleSet option.PlainRuleSet + var ruleSet option.PlainRuleSetCompat switch s.fileFormat { case C.RuleSetFormatSource, "": content, err := os.ReadFile(path) if err != nil { return err } - compat, err := json.UnmarshalExtended[option.PlainRuleSetCompat](content) - if err != nil { - return err - } - plainRuleSet, err = compat.Upgrade() + ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) if err != nil { return err } + case C.RuleSetFormatBinary: setFile, err := os.Open(path) if err != nil { return err } - plainRuleSet, err = srs.Read(setFile, false) + ruleSet, err = srs.Read(setFile, false) if err != nil { return err } default: return E.New("unknown rule-set format: ", s.fileFormat) } + plainRuleSet, err := ruleSet.Upgrade() + if err != nil { + return err + } return s.reloadRules(plainRuleSet.Rules) } diff --git a/route/rule_set_remote.go b/route/rule_set_remote.go index 11541609..5045bf2d 100644 --- a/route/rule_set_remote.go +++ b/route/rule_set_remote.go @@ -159,28 +159,27 @@ func (s *RemoteRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSet func (s *RemoteRuleSet) loadBytes(content []byte) error { var ( - plainRuleSet option.PlainRuleSet - err error + ruleSet option.PlainRuleSetCompat + err error ) switch s.options.Format { case C.RuleSetFormatSource: - var compat option.PlainRuleSetCompat - compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) - if err != nil { - return err - } - plainRuleSet, err = compat.Upgrade() + ruleSet, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content) if err != nil { return err } case C.RuleSetFormatBinary: - plainRuleSet, err = srs.Read(bytes.NewReader(content), false) + ruleSet, err = srs.Read(bytes.NewReader(content), false) if err != nil { return err } default: return E.New("unknown rule-set format: ", s.options.Format) } + plainRuleSet, err := ruleSet.Upgrade() + if err != nil { + return err + } rules := make([]adapter.HeadlessRule, len(plainRuleSet.Rules)) for i, ruleOptions := range plainRuleSet.Rules { rules[i], err = NewHeadlessRule(s.router, ruleOptions)