package logic import ( "fmt" "github.com/Dreamacro/clash/common/collections" C "github.com/Dreamacro/clash/constant" RC "github.com/Dreamacro/clash/rule/common" RP "github.com/Dreamacro/clash/rule/provider" "github.com/Dreamacro/clash/rule/ruleparser" "regexp" "strings" ) func parseRuleByPayload(payload string) ([]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) 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) (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 parseRule(tp, payload string, params []string) (parsed C.Rule, parseErr error) { parsed, parseErr = ruleparser.ParseSameRule(tp, payload, "", params) if ruleparser.IsUnsupported(parseErr) { switch tp { case "AND": parsed, parseErr = NewAND(payload, "") case "OR": parsed, parseErr = NewOR(payload, "") case "NOT": parsed, parseErr = NewNOT(payload, "") case "RULE-SET": noResolve := RC.HasNoResolve(params) parsed, parseErr = RP.NewRuleSet(payload, "", noResolve) default: parseErr = ruleparser.NewUnsupportedError(tp) } } if parseErr != nil { return nil, parseErr } ruleExtra := &C.RuleExtra{ Network: RC.FindNetwork(params), SourceIPs: RC.FindSourceIPs(params), ProcessNames: RC.FindProcessName(params), } parsed.SetRuleExtra(ruleExtra) return parsed, 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 }