Files
sing-box-extended-mirror/route/reference_rule.go
T

169 lines
4.6 KiB
Go

package route
import (
"reflect"
"strings"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
)
type staticMatch uint8
const (
staticMatchUnknown staticMatch = iota
staticMatchNever
staticMatchAlways
)
func (m staticMatch) invert() staticMatch {
switch m {
case staticMatchNever:
return staticMatchAlways
case staticMatchAlways:
return staticMatchNever
default:
return staticMatchUnknown
}
}
func evaluateClashMode(clashMode string, mode string, invert bool, otherConditions bool) staticMatch {
match := staticMatchUnknown
if clashMode != "" && !strings.EqualFold(clashMode, mode) {
match = staticMatchNever
} else if !otherConditions {
match = staticMatchAlways
}
if invert {
match = match.invert()
}
return match
}
func evaluateLogical(matches []staticMatch, logicalMode string, invert bool) staticMatch {
var match staticMatch
if logicalMode == C.LogicalTypeAnd {
match = staticMatchAlways
} else {
match = staticMatchNever
}
for _, ruleMatch := range matches {
if logicalMode == C.LogicalTypeAnd && ruleMatch == staticMatchNever {
match = staticMatchNever
break
}
if logicalMode == C.LogicalTypeOr && ruleMatch == staticMatchAlways {
match = staticMatchAlways
break
}
if ruleMatch == staticMatchUnknown {
match = staticMatchUnknown
}
}
if invert {
match = match.invert()
}
return match
}
func evaluateRule(rule option.Rule, mode string) staticMatch {
switch rule.Type {
case C.RuleTypeDefault:
conditions := rule.DefaultOptions.RawDefaultRule
conditions.ClashMode = ""
conditions.Invert = false
return evaluateClashMode(rule.DefaultOptions.ClashMode, mode, rule.DefaultOptions.Invert, !reflect.DeepEqual(conditions, option.RawDefaultRule{}))
case C.RuleTypeLogical:
matches := make([]staticMatch, 0, len(rule.LogicalOptions.Rules))
for _, subRule := range rule.LogicalOptions.Rules {
matches = append(matches, evaluateRule(subRule, mode))
}
return evaluateLogical(matches, rule.LogicalOptions.Mode, rule.LogicalOptions.Invert)
default:
return staticMatchUnknown
}
}
func evaluateDNSRule(rule option.DNSRule, mode string) staticMatch {
switch rule.Type {
case C.RuleTypeDefault:
conditions := rule.DefaultOptions.RawDefaultDNSRule
conditions.ClashMode = ""
conditions.Invert = false
return evaluateClashMode(rule.DefaultOptions.ClashMode, mode, rule.DefaultOptions.Invert, !reflect.DeepEqual(conditions, option.RawDefaultDNSRule{}))
case C.RuleTypeLogical:
matches := make([]staticMatch, 0, len(rule.LogicalOptions.Rules))
for _, subRule := range rule.LogicalOptions.Rules {
matches = append(matches, evaluateDNSRule(subRule, mode))
}
return evaluateLogical(matches, rule.LogicalOptions.Mode, rule.LogicalOptions.Invert)
default:
return staticMatchUnknown
}
}
func collectRuleReferences(rules []option.Rule, mode string, outbounds *[]string, transports *[]string) (shadowed bool) {
for _, rule := range rules {
match := evaluateRule(rule, mode)
if match == staticMatchNever {
continue
}
var action option.RuleAction
switch rule.Type {
case C.RuleTypeDefault:
action = rule.DefaultOptions.RuleAction
case C.RuleTypeLogical:
action = rule.LogicalOptions.RuleAction
}
var final bool
switch action.Action {
case C.RuleActionTypeRoute:
*outbounds = append(*outbounds, action.RouteOptions.Outbound)
final = true
case C.RuleActionTypeBypass:
if action.BypassOptions.Outbound != "" {
*outbounds = append(*outbounds, action.BypassOptions.Outbound)
final = true
}
case C.RuleActionTypeDirect, C.RuleActionTypeReject, C.RuleActionTypeHijackDNS:
final = true
case C.RuleActionTypeResolve:
*transports = append(*transports, action.ResolveOptions.Server)
}
if final && match == staticMatchAlways {
return true
}
}
return false
}
func collectDNSRuleReferences(rules []option.DNSRule, mode string, transports *[]string) (shadowed bool) {
for _, rule := range rules {
match := evaluateDNSRule(rule, mode)
if match == staticMatchNever {
continue
}
var action option.DNSRuleAction
switch rule.Type {
case C.RuleTypeDefault:
action = rule.DefaultOptions.DNSRuleAction
case C.RuleTypeLogical:
action = rule.LogicalOptions.DNSRuleAction
}
var final bool
switch action.Action {
case C.RuleActionTypeRoute:
*transports = append(*transports, action.RouteOptions.Server)
final = true
case C.RuleActionTypeEvaluate:
*transports = append(*transports, action.EvaluateOptions.Server)
case C.RuleActionTypeRespond, C.RuleActionTypeReject, C.RuleActionTypePredefined:
final = true
}
if final && match == staticMatchAlways {
return true
}
}
return false
}