From 1a4c0db682671bc8ba5cea6bb5f93503dc1bdc54 Mon Sep 17 00:00:00 2001 From: gVisor bot Date: Mon, 5 Dec 2022 23:51:38 +0800 Subject: [PATCH] chore: cleanup rules/logic code --- rules/logic/and.go | 68 --------- rules/logic/common.go | 139 ------------------- rules/logic/logic.go | 289 +++++++++++++++++++++++++++++++++++++++ rules/logic/not.go | 63 --------- rules/logic/or.go | 67 --------- rules/logic/sub_rules.go | 91 ------------ 6 files changed, 289 insertions(+), 428 deletions(-) delete mode 100644 rules/logic/and.go delete mode 100644 rules/logic/common.go create mode 100644 rules/logic/logic.go delete mode 100644 rules/logic/not.go delete mode 100644 rules/logic/or.go delete mode 100644 rules/logic/sub_rules.go diff --git a/rules/logic/and.go b/rules/logic/and.go deleted file mode 100644 index ffd7c262..00000000 --- a/rules/logic/and.go +++ /dev/null @@ -1,68 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" - "strings" -) - -type AND struct { - *common.Base - rules []C.Rule - payload string - adapter string - needIP bool -} - -func (A *AND) ShouldFindProcess() bool { - return false -} - -func NewAND(payload string, adapter string, - parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*AND, error) { - and := &AND{Base: &common.Base{}, payload: payload, adapter: adapter} - rules, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - and.rules = rules - payloads := make([]string, 0, len(rules)) - for _, rule := range rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) - if rule.ShouldResolveIP() { - and.needIP = true - break - } - } - - and.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) - return and, nil -} - -func (A *AND) RuleType() C.RuleType { - return C.AND -} - -func (A *AND) Match(metadata *C.Metadata) (bool, string) { - for _, rule := range A.rules { - if m, _ := rule.Match(metadata); !m { - return false, "" - } - } - - return true, A.adapter -} - -func (A *AND) Adapter() string { - return A.adapter -} - -func (A *AND) Payload() string { - return A.payload -} - -func (A *AND) ShouldResolveIP() bool { - return A.needIP -} diff --git a/rules/logic/common.go b/rules/logic/common.go deleted file mode 100644 index 1385463b..00000000 --- a/rules/logic/common.go +++ /dev/null @@ -1,139 +0,0 @@ -package logic - -import ( - "fmt" - "github.com/Dreamacro/clash/common/collections" - C "github.com/Dreamacro/clash/constant" - "regexp" - "strings" - _ "unsafe" -) - -func ParseRuleByPayload(payload string, parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ([]C.Rule, error) { - regex, err := regexp.Compile("\\(.*\\)") - if err != nil { - return nil, err - } - - if regex.MatchString(payload) { - subAllRanges, err := format(payload) - if err != nil { - return nil, err - } - rules := make([]C.Rule, 0, len(subAllRanges)) - - subRanges := findSubRuleRange(payload, subAllRanges) - for _, subRange := range subRanges { - subPayload := payload[subRange.start+1 : subRange.end] - - rule, err := payloadToRule(subPayload, parseLogicSubRule(parseRule)) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - } - - return rules, nil - } - - return nil, fmt.Errorf("payload format error") -} - -func containRange(r Range, preStart, preEnd int) bool { - return preStart < r.start && preEnd > r.end -} - -func payloadToRule(subPayload string, parseRule func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)) (C.Rule, error) { - splitStr := strings.SplitN(subPayload, ",", 2) - if len(splitStr) < 2 { - return nil, fmt.Errorf("[%s] format is error", subPayload) - } - - tp := splitStr[0] - payload := splitStr[1] - if tp == "NOT" || tp == "OR" || tp == "AND" { - return parseRule(tp, payload, "", nil) - } - param := strings.Split(payload, ",") - return parseRule(tp, param[0], "", param[1:]) -} - -func parseLogicSubRule(parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { - return func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { - switch tp { - case "MATCH", "SUB-RULE": - return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp) - default: - return parseRule(tp, payload, target, params, nil) - } - } -} - -type Range struct { - start int - end int - index int -} - -func format(payload string) ([]Range, error) { - stack := collections.NewStack() - num := 0 - subRanges := make([]Range, 0) - for i, c := range payload { - if c == '(' { - sr := Range{ - start: i, - index: num, - } - - num++ - stack.Push(sr) - } else if c == ')' { - if stack.Len() == 0 { - return nil, fmt.Errorf("missing '('") - } - - sr := stack.Pop().(Range) - sr.end = i - subRanges = append(subRanges, sr) - } - } - - if stack.Len() != 0 { - return nil, fmt.Errorf("format error is missing )") - } - - sortResult := make([]Range, len(subRanges)) - for _, sr := range subRanges { - sortResult[sr.index] = sr - } - - return sortResult, nil -} - -func findSubRuleRange(payload string, ruleRanges []Range) []Range { - payloadLen := len(payload) - subRuleRange := make([]Range, 0) - for _, rr := range ruleRanges { - if rr.start == 0 && rr.end == payloadLen-1 { - // 最大范围跳过 - continue - } - - containInSub := false - for _, r := range subRuleRange { - if containRange(rr, r.start, r.end) { - // The subRuleRange contains a range of rr, which is the next level node of the tree - containInSub = true - break - } - } - - if !containInSub { - subRuleRange = append(subRuleRange, rr) - } - } - - return subRuleRange -} diff --git a/rules/logic/logic.go b/rules/logic/logic.go new file mode 100644 index 00000000..4a0bd7e8 --- /dev/null +++ b/rules/logic/logic.go @@ -0,0 +1,289 @@ +package logic + +import ( + "fmt" + "regexp" + "strings" + + "github.com/Dreamacro/clash/common/collections" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/rules/common" +) + +type Logic struct { + *common.Base + payload string + adapter string + ruleType C.RuleType + rules []C.Rule + subRules map[string][]C.Rule + needIP bool + needProcess bool +} + +type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) + +func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules} + err := logic.parsePayload(fmt.Sprintf("(%s)", payload), parseRule) + if err != nil { + return nil, err + } + + if len(logic.rules) != 1 { + return nil, fmt.Errorf("Sub-Rule rule must contain one rule") + } + for _, rule := range subRules[adapter] { + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.subRules = subRules + return logic, nil +} + +func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.NOT} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + if len(logic.rules) != 1 { + return nil, fmt.Errorf("not rule must contain one rule") + } + logic.needIP = logic.rules[0].ShouldResolveIP() + logic.needProcess = logic.rules[0].ShouldFindProcess() + logic.payload = fmt.Sprintf("(!(%s,%s))", logic.rules[0].RuleType(), logic.rules[0].Payload()) + return logic, nil +} + +func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.OR} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) + + return logic, nil +} +func NewAND(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) + + return logic, nil +} + +type Range struct { + start int + end int + index int +} + +func (r Range) containRange(preStart, preEnd int) bool { + return preStart < r.start && preEnd > r.end +} + +func (logic *Logic) payloadToRule(subPayload string, parseRule ParseRuleFunc) (C.Rule, error) { + splitStr := strings.SplitN(subPayload, ",", 2) + if len(splitStr) < 2 { + return nil, fmt.Errorf("[%s] format is error", subPayload) + } + + tp := splitStr[0] + payload := splitStr[1] + switch tp { + case "MATCH", "SUB-RULE": + return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp) + case "NOT", "OR", "AND": + return parseRule(tp, payload, "", nil, nil) + } + param := strings.Split(payload, ",") + return parseRule(tp, param[0], "", param[1:], nil) +} + +func (logic *Logic) format(payload string) ([]Range, error) { + stack := collections.NewStack() + num := 0 + subRanges := make([]Range, 0) + for i, c := range payload { + if c == '(' { + sr := Range{ + start: i, + index: num, + } + + num++ + stack.Push(sr) + } else if c == ')' { + if stack.Len() == 0 { + return nil, fmt.Errorf("missing '('") + } + + sr := stack.Pop().(Range) + sr.end = i + subRanges = append(subRanges, sr) + } + } + + if stack.Len() != 0 { + return nil, fmt.Errorf("format error is missing )") + } + + sortResult := make([]Range, len(subRanges)) + for _, sr := range subRanges { + sortResult[sr.index] = sr + } + + return sortResult, nil +} + +func (logic *Logic) findSubRuleRange(payload string, ruleRanges []Range) []Range { + payloadLen := len(payload) + subRuleRange := make([]Range, 0) + for _, rr := range ruleRanges { + if rr.start == 0 && rr.end == payloadLen-1 { + // 最大范围跳过 + continue + } + + containInSub := false + for _, r := range subRuleRange { + if rr.containRange(r.start, r.end) { + // The subRuleRange contains a range of rr, which is the next level node of the tree + containInSub = true + break + } + } + + if !containInSub { + subRuleRange = append(subRuleRange, rr) + } + } + + return subRuleRange +} + +func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error { + regex, err := regexp.Compile("\\(.*\\)") + if err != nil { + return err + } + + if regex.MatchString(payload) { + subAllRanges, err := logic.format(payload) + if err != nil { + return err + } + rules := make([]C.Rule, 0, len(subAllRanges)) + + subRanges := logic.findSubRuleRange(payload, subAllRanges) + for _, subRange := range subRanges { + subPayload := payload[subRange.start+1 : subRange.end] + + rule, err := logic.payloadToRule(subPayload, parseRule) + if err != nil { + return err + } + + rules = append(rules, rule) + } + + logic.rules = rules + + return nil + } + + return fmt.Errorf("payload format error") +} + +func (logic *Logic) RuleType() C.RuleType { + return logic.ruleType +} + +func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { + for _, rule := range subRules[name] { + if m, a := rule.Match(metadata); m { + if rule.RuleType() == C.SubRules { + matchSubRules(metadata, rule.Adapter(), subRules) + } else { + return m, a + } + } + } + return false, "" +} + +func (logic *Logic) Match(metadata *C.Metadata) (bool, string) { + switch logic.ruleType { + case C.SubRules: + return matchSubRules(metadata, logic.adapter, logic.subRules) + case C.NOT: + if m, _ := logic.rules[0].Match(metadata); !m { + return true, logic.adapter + } + return false, "" + case C.OR: + for _, rule := range logic.rules { + if m, _ := rule.Match(metadata); m { + return true, logic.adapter + } + } + return false, "" + case C.AND: + for _, rule := range logic.rules { + if m, _ := rule.Match(metadata); !m { + return false, logic.adapter + } + } + return true, logic.adapter + } + + return false, "" +} + +func (logic *Logic) Adapter() string { + return logic.adapter +} + +func (logic *Logic) Payload() string { + return logic.payload +} + +func (logic *Logic) ShouldResolveIP() bool { + return logic.needIP +} + +func (logic *Logic) ShouldFindProcess() bool { + return logic.needProcess +} diff --git a/rules/logic/not.go b/rules/logic/not.go deleted file mode 100644 index c109a2c9..00000000 --- a/rules/logic/not.go +++ /dev/null @@ -1,63 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" -) - -type NOT struct { - *common.Base - rule C.Rule - payload string - adapter string -} - -func (not *NOT) ShouldFindProcess() bool { - return false -} - -func NewNOT(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*NOT, error) { - not := &NOT{Base: &common.Base{}, adapter: adapter} - rule, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - if len(rule) != 1 { - return nil, fmt.Errorf("not rule must contain one rule") - } - - not.rule = rule[0] - not.payload = fmt.Sprintf("(!(%s,%s))", rule[0].RuleType(), rule[0].Payload()) - - return not, nil -} - -func (not *NOT) RuleType() C.RuleType { - return C.NOT -} - -func (not *NOT) Match(metadata *C.Metadata) (bool, string) { - if not.rule == nil { - return true, not.adapter - } - - if m, _ := not.rule.Match(metadata); !m { - return true, not.adapter - } - - return false, "" -} - -func (not *NOT) Adapter() string { - return not.adapter -} - -func (not *NOT) Payload() string { - return not.payload -} - -func (not *NOT) ShouldResolveIP() bool { - return not.rule != nil && not.rule.ShouldResolveIP() -} diff --git a/rules/logic/or.go b/rules/logic/or.go deleted file mode 100644 index f63bae4b..00000000 --- a/rules/logic/or.go +++ /dev/null @@ -1,67 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" - "strings" -) - -type OR struct { - *common.Base - rules []C.Rule - payload string - adapter string - needIP bool -} - -func (or *OR) ShouldFindProcess() bool { - return false -} - -func (or *OR) RuleType() C.RuleType { - return C.OR -} - -func (or *OR) Match(metadata *C.Metadata) (bool, string) { - for _, rule := range or.rules { - if m, _ := rule.Match(metadata); m { - return true, or.adapter - } - } - - return false, "" -} - -func (or *OR) Adapter() string { - return or.adapter -} - -func (or *OR) Payload() string { - return or.payload -} - -func (or *OR) ShouldResolveIP() bool { - return or.needIP -} - -func NewOR(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*OR, error) { - or := &OR{Base: &common.Base{}, payload: payload, adapter: adapter} - rules, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - or.rules = rules - payloads := make([]string, 0, len(rules)) - for _, rule := range rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType(), rule.Payload())) - if rule.ShouldResolveIP() { - or.needIP = true - break - } - } - - or.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) - return or, nil -} diff --git a/rules/logic/sub_rules.go b/rules/logic/sub_rules.go deleted file mode 100644 index 75852979..00000000 --- a/rules/logic/sub_rules.go +++ /dev/null @@ -1,91 +0,0 @@ -package logic - -import ( - "fmt" - - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" -) - -type SubRule struct { - *common.Base - payload string - payloadRule C.Rule - subName string - subRules map[string][]C.Rule - shouldFindProcess *bool - shouldResolveIP *bool -} - -func NewSubRule(payload, subName string, sub map[string][]C.Rule, - parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*SubRule, error) { - payloadRule, err := ParseRuleByPayload(fmt.Sprintf("(%s)", payload), parse) - if err != nil { - return nil, err - } - if len(payloadRule) != 1 { - return nil, fmt.Errorf("Sub-Rule rule must contain one rule") - } - - return &SubRule{ - Base: &common.Base{}, - payload: payload, - payloadRule: payloadRule[0], - subName: subName, - subRules: sub, - }, nil -} - -func (r *SubRule) RuleType() C.RuleType { - return C.SubRules -} - -func (r *SubRule) Match(metadata *C.Metadata) (bool, string) { - - return match(metadata, r.subName, r.subRules) -} - -func match(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { - for _, rule := range subRules[name] { - if m, a := rule.Match(metadata); m { - if rule.RuleType() == C.SubRules { - match(metadata, rule.Adapter(), subRules) - } else { - return m, a - } - } - } - return false, "" -} - -func (r *SubRule) ShouldResolveIP() bool { - if r.shouldResolveIP == nil { - s := false - for _, rule := range r.subRules[r.subName] { - s = s || rule.ShouldResolveIP() - } - r.shouldResolveIP = &s - } - - return *r.shouldResolveIP -} - -func (r *SubRule) ShouldFindProcess() bool { - if r.shouldFindProcess == nil { - s := false - for _, rule := range r.subRules[r.subName] { - s = s || rule.ShouldFindProcess() - } - r.shouldFindProcess = &s - } - - return *r.shouldFindProcess -} - -func (r *SubRule) Adapter() string { - return r.subName -} - -func (r *SubRule) Payload() string { - return r.payload -}