|
@@ -0,0 +1,86 @@
|
|
|
|
+package main
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "bytes"
|
|
|
|
+ "io"
|
|
|
|
+ "os"
|
|
|
|
+
|
|
|
|
+ "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/log"
|
|
|
|
+ "github.com/sagernet/sing-box/option"
|
|
|
|
+ "github.com/sagernet/sing-box/route"
|
|
|
|
+ E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
+ "github.com/sagernet/sing/common/json"
|
|
|
|
+
|
|
|
|
+ "github.com/spf13/cobra"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+var flagRuleSetMatchFormat string
|
|
|
|
+
|
|
|
|
+var commandRuleSetMatch = &cobra.Command{
|
|
|
|
+ Use: "match <rule-set path> <domain>",
|
|
|
|
+ Short: "Check if a domain matches the rule set",
|
|
|
|
+ Args: cobra.ExactArgs(2),
|
|
|
|
+ Run: func(cmd *cobra.Command, args []string) {
|
|
|
|
+ err := ruleSetMatch(args[0], args[1])
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Fatal(err)
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func init() {
|
|
|
|
+ commandRuleSetMatch.Flags().StringVarP(&flagRuleSetMatchFormat, "format", "f", "source", "rule-set format")
|
|
|
|
+ commandRuleSet.AddCommand(commandRuleSetMatch)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func ruleSetMatch(sourcePath string, domain string) error {
|
|
|
|
+ var (
|
|
|
|
+ reader io.Reader
|
|
|
|
+ err error
|
|
|
|
+ )
|
|
|
|
+ if sourcePath == "stdin" {
|
|
|
|
+ reader = os.Stdin
|
|
|
|
+ } else {
|
|
|
|
+ reader, err = os.Open(sourcePath)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return E.Cause(err, "read rule-set")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ content, err := io.ReadAll(reader)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return E.Cause(err, "read rule-set")
|
|
|
|
+ }
|
|
|
|
+ var plainRuleSet option.PlainRuleSet
|
|
|
|
+ switch flagRuleSetMatchFormat {
|
|
|
|
+ case C.RuleSetFormatSource:
|
|
|
|
+ var compat option.PlainRuleSetCompat
|
|
|
|
+ compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ plainRuleSet = compat.Upgrade()
|
|
|
|
+ case C.RuleSetFormatBinary:
|
|
|
|
+ plainRuleSet, err = srs.Read(bytes.NewReader(content), false)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ default:
|
|
|
|
+ return E.New("unknown rule set format: ", flagRuleSetMatchFormat)
|
|
|
|
+ }
|
|
|
|
+ for i, ruleOptions := range plainRuleSet.Rules {
|
|
|
|
+ var currentRule adapter.HeadlessRule
|
|
|
|
+ currentRule, err = route.NewHeadlessRule(nil, ruleOptions)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return E.Cause(err, "parse rule_set.rules.[", i, "]")
|
|
|
|
+ }
|
|
|
|
+ if currentRule.Match(&adapter.InboundContext{
|
|
|
|
+ Domain: domain,
|
|
|
|
+ }) {
|
|
|
|
+ println("match rules.[", i, "]: "+currentRule.String())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|