github.com/metacubex/mihomo@v1.18.5/rules/logic/logic.go (about) 1 package logic 2 3 import ( 4 "fmt" 5 list "github.com/bahlo/generic-list-go" 6 "regexp" 7 "strings" 8 9 C "github.com/metacubex/mihomo/constant" 10 "github.com/metacubex/mihomo/rules/common" 11 ) 12 13 type Logic struct { 14 *common.Base 15 payload string 16 adapter string 17 ruleType C.RuleType 18 rules []C.Rule 19 subRules map[string][]C.Rule 20 needIP bool 21 needProcess bool 22 } 23 24 type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) 25 26 func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule ParseRuleFunc) (*Logic, error) { 27 logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules} 28 err := logic.parsePayload(fmt.Sprintf("(%s)", payload), parseRule) 29 if err != nil { 30 return nil, err 31 } 32 33 if len(logic.rules) != 1 { 34 return nil, fmt.Errorf("Sub-Rule rule must contain one rule") 35 } 36 for _, rule := range subRules[adapter] { 37 if rule.ShouldResolveIP() { 38 logic.needIP = true 39 } 40 if rule.ShouldFindProcess() { 41 logic.needProcess = true 42 } 43 } 44 logic.subRules = subRules 45 return logic, nil 46 } 47 48 func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { 49 logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.NOT} 50 err := logic.parsePayload(payload, parseRule) 51 if err != nil { 52 return nil, err 53 } 54 55 if len(logic.rules) != 1 { 56 return nil, fmt.Errorf("not rule must contain one rule") 57 } 58 logic.needIP = logic.rules[0].ShouldResolveIP() 59 logic.needProcess = logic.rules[0].ShouldFindProcess() 60 logic.payload = fmt.Sprintf("(!(%s,%s))", logic.rules[0].RuleType(), logic.rules[0].Payload()) 61 return logic, nil 62 } 63 64 func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { 65 logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.OR} 66 err := logic.parsePayload(payload, parseRule) 67 if err != nil { 68 return nil, err 69 } 70 71 payloads := make([]string, 0, len(logic.rules)) 72 for _, rule := range logic.rules { 73 payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) 74 if rule.ShouldResolveIP() { 75 logic.needIP = true 76 } 77 if rule.ShouldFindProcess() { 78 logic.needProcess = true 79 } 80 } 81 logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) 82 83 return logic, nil 84 } 85 func NewAND(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { 86 logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND} 87 err := logic.parsePayload(payload, parseRule) 88 if err != nil { 89 return nil, err 90 } 91 92 payloads := make([]string, 0, len(logic.rules)) 93 for _, rule := range logic.rules { 94 payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) 95 if rule.ShouldResolveIP() { 96 logic.needIP = true 97 } 98 if rule.ShouldFindProcess() { 99 logic.needProcess = true 100 } 101 } 102 logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) 103 104 return logic, nil 105 } 106 107 type Range struct { 108 start int 109 end int 110 index int 111 } 112 113 func (r Range) containRange(preStart, preEnd int) bool { 114 return preStart < r.start && preEnd > r.end 115 } 116 117 func (logic *Logic) payloadToRule(subPayload string, parseRule ParseRuleFunc) (C.Rule, error) { 118 splitStr := strings.SplitN(subPayload, ",", 2) 119 if len(splitStr) < 2 { 120 return nil, fmt.Errorf("[%s] format is error", subPayload) 121 } 122 123 tp := splitStr[0] 124 payload := splitStr[1] 125 switch tp { 126 case "MATCH", "SUB-RULE": 127 return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp) 128 case "NOT", "OR", "AND": 129 return parseRule(tp, payload, "", nil, nil) 130 } 131 param := strings.Split(payload, ",") 132 return parseRule(tp, param[0], "", param[1:], nil) 133 } 134 135 func (logic *Logic) format(payload string) ([]Range, error) { 136 stack := list.New[Range]() 137 num := 0 138 subRanges := make([]Range, 0) 139 for i, c := range payload { 140 if c == '(' { 141 sr := Range{ 142 start: i, 143 index: num, 144 } 145 146 num++ 147 stack.PushBack(sr) 148 } else if c == ')' { 149 if stack.Len() == 0 { 150 return nil, fmt.Errorf("missing '('") 151 } 152 153 sr := stack.Back() 154 stack.Remove(sr) 155 sr.Value.end = i 156 subRanges = append(subRanges, sr.Value) 157 } 158 } 159 160 if stack.Len() != 0 { 161 return nil, fmt.Errorf("format error is missing )") 162 } 163 164 sortResult := make([]Range, len(subRanges)) 165 for _, sr := range subRanges { 166 sortResult[sr.index] = sr 167 } 168 169 return sortResult, nil 170 } 171 172 func (logic *Logic) findSubRuleRange(payload string, ruleRanges []Range) []Range { 173 payloadLen := len(payload) 174 subRuleRange := make([]Range, 0) 175 for _, rr := range ruleRanges { 176 if rr.start == 0 && rr.end == payloadLen-1 { 177 // 最大范围跳过 178 continue 179 } 180 181 containInSub := false 182 for _, r := range subRuleRange { 183 if rr.containRange(r.start, r.end) { 184 // The subRuleRange contains a range of rr, which is the next level node of the tree 185 containInSub = true 186 break 187 } 188 } 189 190 if !containInSub { 191 subRuleRange = append(subRuleRange, rr) 192 } 193 } 194 195 return subRuleRange 196 } 197 198 func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error { 199 regex, err := regexp.Compile("\\(.*\\)") 200 if err != nil { 201 return err 202 } 203 204 if regex.MatchString(payload) { 205 subAllRanges, err := logic.format(payload) 206 if err != nil { 207 return err 208 } 209 rules := make([]C.Rule, 0, len(subAllRanges)) 210 211 subRanges := logic.findSubRuleRange(payload, subAllRanges) 212 for _, subRange := range subRanges { 213 subPayload := payload[subRange.start+1 : subRange.end] 214 215 rule, err := logic.payloadToRule(subPayload, parseRule) 216 if err != nil { 217 return err 218 } 219 220 if rule.ShouldResolveIP() { 221 logic.needIP = true 222 } 223 if rule.ShouldFindProcess() { 224 logic.needProcess = true 225 } 226 227 rules = append(rules, rule) 228 } 229 230 logic.rules = rules 231 232 return nil 233 } 234 235 return fmt.Errorf("payload format error") 236 } 237 238 func (logic *Logic) RuleType() C.RuleType { 239 return logic.ruleType 240 } 241 242 func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { 243 for _, rule := range subRules[name] { 244 if m, a := rule.Match(metadata); m { 245 if rule.RuleType() == C.SubRules { 246 matchSubRules(metadata, rule.Adapter(), subRules) 247 } else { 248 return m, a 249 } 250 } 251 } 252 return false, "" 253 } 254 255 func (logic *Logic) Match(metadata *C.Metadata) (bool, string) { 256 switch logic.ruleType { 257 case C.SubRules: 258 if m, _ := logic.rules[0].Match(metadata); m { 259 return matchSubRules(metadata, logic.adapter, logic.subRules) 260 } 261 return false, "" 262 case C.NOT: 263 if m, _ := logic.rules[0].Match(metadata); !m { 264 return true, logic.adapter 265 } 266 return false, "" 267 case C.OR: 268 for _, rule := range logic.rules { 269 if m, _ := rule.Match(metadata); m { 270 return true, logic.adapter 271 } 272 } 273 return false, "" 274 case C.AND: 275 for _, rule := range logic.rules { 276 if m, _ := rule.Match(metadata); !m { 277 return false, logic.adapter 278 } 279 } 280 return true, logic.adapter 281 } 282 283 return false, "" 284 } 285 286 func (logic *Logic) Adapter() string { 287 return logic.adapter 288 } 289 290 func (logic *Logic) Payload() string { 291 return logic.payload 292 } 293 294 func (logic *Logic) ShouldResolveIP() bool { 295 return logic.needIP 296 } 297 298 func (logic *Logic) ShouldFindProcess() bool { 299 return logic.needProcess 300 }