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  }