1
0

Refactor(constant): added rule string enums in configs as RuleConfig for better outside integrations (#2878)

This commit is contained in:
Neko Ayaka 2023-08-16 11:06:30 +08:00 committed by GitHub
parent cb8c732375
commit 218c3b4e89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 249 additions and 16 deletions

View File

@ -11,6 +11,7 @@ linters:
- unconvert - unconvert
- unused - unused
- usestdlibvars - usestdlibvars
- exhaustive
linters-settings: linters-settings:
gci: gci:
@ -21,3 +22,5 @@ linters-settings:
- default - default
staticcheck: staticcheck:
go: '1.21' go: '1.21'
exhaustive:
default-signifies-exhaustive: true

View File

@ -52,6 +52,8 @@ func (t Type) String() string {
return "Redir" return "Redir"
case TPROXY: case TPROXY:
return "TProxy" return "TProxy"
case TUNNEL:
return "Tunnel"
default: default:
return "Unknown" return "Unknown"
} }

View File

@ -1,5 +1,27 @@
package constant package constant
const (
RuleConfigDomain RuleConfig = "DOMAIN"
RuleConfigDomainSuffix RuleConfig = "DOMAIN-SUFFIX"
RuleConfigDomainKeyword RuleConfig = "DOMAIN-KEYWORD"
RuleConfigGeoIP RuleConfig = "GEOIP"
RuleConfigIPCIDR RuleConfig = "IP-CIDR"
RuleConfigIPCIDR6 RuleConfig = "IP-CIDR6"
RuleConfigSrcIPCIDR RuleConfig = "SRC-IP-CIDR"
RuleConfigSrcPort RuleConfig = "SRC-PORT"
RuleConfigDstPort RuleConfig = "DST-PORT"
RuleConfigInboundPort RuleConfig = "INBOUND-PORT"
RuleConfigProcessName RuleConfig = "PROCESS-NAME"
RuleConfigProcessPath RuleConfig = "PROCESS-PATH"
RuleConfigIPSet RuleConfig = "IPSET"
RuleConfigRuleSet RuleConfig = "RULE-SET"
RuleConfigScript RuleConfig = "SCRIPT"
RuleConfigMatch RuleConfig = "MATCH"
)
// Rule Config Type String represents a rule type in configuration files.
type RuleConfig string
// Rule Type // Rule Type
const ( const (
Domain RuleType = iota Domain RuleType = iota

View File

@ -88,6 +88,8 @@ func print(data Event) {
log.Errorln(data.Payload) log.Errorln(data.Payload)
case DEBUG: case DEBUG:
log.Debugln(data.Payload) log.Debugln(data.Payload)
case SILENT:
return
} }
} }

View File

@ -6,6 +6,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*Domain)(nil)
type Domain struct { type Domain struct {
domain string domain string
adapter string adapter string

View File

@ -6,6 +6,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*DomainKeyword)(nil)
type DomainKeyword struct { type DomainKeyword struct {
keyword string keyword string
adapter string adapter string

View File

@ -6,6 +6,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*DomainSuffix)(nil)
type DomainSuffix struct { type DomainSuffix struct {
suffix string suffix string
adapter string adapter string

View File

@ -4,6 +4,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*Match)(nil)
type Match struct { type Match struct {
adapter string adapter string
} }

View File

@ -7,6 +7,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*GEOIP)(nil)
type GEOIP struct { type GEOIP struct {
country string country string
adapter string adapter string

View File

@ -20,6 +20,9 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
} }
} }
// Implements C.Rule
var _ C.Rule = (*IPCIDR)(nil)
type IPCIDR struct { type IPCIDR struct {
ipnet *net.IPNet ipnet *net.IPNet
adapter string adapter string

View File

@ -6,6 +6,9 @@ import (
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
) )
// Implements C.Rule
var _ C.Rule = (*IPSet)(nil)
type IPSet struct { type IPSet struct {
name string name string
adapter string adapter string

View File

@ -12,36 +12,40 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
parsed C.Rule parsed C.Rule
) )
switch tp { ruleConfigType := C.RuleConfig(tp)
case "DOMAIN":
switch ruleConfigType {
case C.RuleConfigDomain:
parsed = NewDomain(payload, target) parsed = NewDomain(payload, target)
case "DOMAIN-SUFFIX": case C.RuleConfigDomainSuffix:
parsed = NewDomainSuffix(payload, target) parsed = NewDomainSuffix(payload, target)
case "DOMAIN-KEYWORD": case C.RuleConfigDomainKeyword:
parsed = NewDomainKeyword(payload, target) parsed = NewDomainKeyword(payload, target)
case "GEOIP": case C.RuleConfigGeoIP:
noResolve := HasNoResolve(params) noResolve := HasNoResolve(params)
parsed = NewGEOIP(payload, target, noResolve) parsed = NewGEOIP(payload, target, noResolve)
case "IP-CIDR", "IP-CIDR6": case C.RuleConfigIPCIDR, C.RuleConfigIPCIDR6:
noResolve := HasNoResolve(params) noResolve := HasNoResolve(params)
parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRNoResolve(noResolve)) parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRNoResolve(noResolve))
case "SRC-IP-CIDR": case C.RuleConfigSrcIPCIDR:
parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true)) parsed, parseErr = NewIPCIDR(payload, target, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
case "SRC-PORT": case C.RuleConfigSrcPort:
parsed, parseErr = NewPort(payload, target, PortTypeSrc) parsed, parseErr = NewPort(payload, target, PortTypeSrc)
case "DST-PORT": case C.RuleConfigDstPort:
parsed, parseErr = NewPort(payload, target, PortTypeDest) parsed, parseErr = NewPort(payload, target, PortTypeDest)
case "INBOUND-PORT": case C.RuleConfigInboundPort:
parsed, parseErr = NewPort(payload, target, PortTypeInbound) parsed, parseErr = NewPort(payload, target, PortTypeInbound)
case "PROCESS-NAME": case C.RuleConfigProcessName:
parsed, parseErr = NewProcess(payload, target, true) parsed, parseErr = NewProcess(payload, target, true)
case "PROCESS-PATH": case C.RuleConfigProcessPath:
parsed, parseErr = NewProcess(payload, target, false) parsed, parseErr = NewProcess(payload, target, false)
case "IPSET": case C.RuleConfigIPSet:
noResolve := HasNoResolve(params) noResolve := HasNoResolve(params)
parsed, parseErr = NewIPSet(payload, target, noResolve) parsed, parseErr = NewIPSet(payload, target, noResolve)
case "MATCH": case C.RuleConfigMatch:
parsed = NewMatch(target) parsed = NewMatch(target)
case C.RuleConfigRuleSet, C.RuleConfigScript:
parseErr = fmt.Errorf("unsupported rule type %s", tp)
default: default:
parseErr = fmt.Errorf("unsupported rule type %s", tp) parseErr = fmt.Errorf("unsupported rule type %s", tp)
} }

171
rule/parser_test.go Normal file
View File

@ -0,0 +1,171 @@
package rules
import (
"errors"
"fmt"
"testing"
C "github.com/Dreamacro/clash/constant"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParseRule(t *testing.T) {
type testCase struct {
tp C.RuleConfig
payload string
target string
params []string
expectedRule C.Rule
expectedError error
}
policy := "DIRECT"
testCases := []testCase{
{
tp: C.RuleConfigDomain,
payload: "example.com",
target: policy,
expectedRule: NewDomain("example.com", policy),
},
{
tp: C.RuleConfigDomainSuffix,
payload: "example.com",
target: policy,
expectedRule: NewDomainSuffix("example.com", policy),
},
{
tp: C.RuleConfigDomainKeyword,
payload: "example.com",
target: policy,
expectedRule: NewDomainKeyword("example.com", policy),
},
{
tp: C.RuleConfigGeoIP,
payload: "CN",
target: policy, params: []string{noResolve},
expectedRule: NewGEOIP("CN", policy, true),
},
{
tp: C.RuleConfigIPCIDR,
payload: "127.0.0.0/8",
target: policy,
expectedRule: lo.Must(NewIPCIDR("127.0.0.0/8", policy, WithIPCIDRNoResolve(false))),
},
{
tp: C.RuleConfigIPCIDR,
payload: "127.0.0.0/8",
target: policy, params: []string{noResolve},
expectedRule: lo.Must(NewIPCIDR("127.0.0.0/8", policy, WithIPCIDRNoResolve(true))),
},
{
tp: C.RuleConfigIPCIDR6,
payload: "2001:db8::/32",
target: policy,
expectedRule: lo.Must(NewIPCIDR("2001:db8::/32", policy, WithIPCIDRNoResolve(false))),
},
{
tp: C.RuleConfigIPCIDR6,
payload: "2001:db8::/32",
target: policy, params: []string{noResolve},
expectedRule: lo.Must(NewIPCIDR("2001:db8::/32", policy, WithIPCIDRNoResolve(true))),
},
{
tp: C.RuleConfigSrcIPCIDR,
payload: "192.168.1.201/32",
target: policy,
expectedRule: lo.Must(NewIPCIDR("192.168.1.201/32", policy, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))),
},
{
tp: C.RuleConfigSrcPort,
payload: "80",
target: policy,
expectedRule: lo.Must(NewPort("80", policy, PortTypeSrc)),
},
{
tp: C.RuleConfigDstPort,
payload: "80",
target: policy,
expectedRule: lo.Must(NewPort("80", policy, PortTypeDest)),
},
{
tp: C.RuleConfigInboundPort,
payload: "80",
target: policy,
expectedRule: lo.Must(NewPort("80", policy, PortTypeInbound)),
},
{
tp: C.RuleConfigProcessName,
payload: "example.exe",
target: policy,
expectedRule: lo.Must(NewProcess("example.exe", policy, true)),
},
{
tp: C.RuleConfigProcessPath,
payload: "C:\\Program Files\\example.exe",
target: policy,
expectedRule: lo.Must(NewProcess("C:\\Program Files\\example.exe", policy, false)),
},
{
tp: C.RuleConfigProcessPath,
payload: "/opt/example/example",
target: policy,
expectedRule: lo.Must(NewProcess("/opt/example/example", policy, false)),
},
{
tp: C.RuleConfigIPSet,
payload: "example",
target: policy,
expectedRule: lo.Must(NewIPSet("example", policy, true)),
},
{
tp: C.RuleConfigIPSet,
payload: "example",
target: policy, params: []string{noResolve},
expectedRule: lo.Must(NewIPSet("example", policy, false)),
},
{
tp: C.RuleConfigMatch,
payload: "example",
target: policy,
expectedRule: NewMatch(policy),
},
{
tp: C.RuleConfigRuleSet,
payload: "example",
target: policy,
expectedError: fmt.Errorf("unsupported rule type %s", C.RuleConfigRuleSet),
},
{
tp: C.RuleConfigScript,
payload: "example",
target: policy,
expectedError: fmt.Errorf("unsupported rule type %s", C.RuleConfigScript),
},
{
tp: "UNKNOWN",
payload: "example",
target: policy,
expectedError: errors.New("unsupported rule type UNKNOWN"),
},
{
tp: "ABCD",
payload: "example",
target: policy,
expectedError: errors.New("unsupported rule type ABCD"),
},
}
for _, tc := range testCases {
_, err := ParseRule(string(tc.tp), tc.payload, tc.target, tc.params)
if tc.expectedError != nil {
require.Error(t, err)
assert.EqualError(t, err, tc.expectedError.Error())
} else {
require.NoError(t, err)
}
}
}

View File

@ -15,6 +15,9 @@ const (
PortTypeInbound PortTypeInbound
) )
// Implements C.Rule
var _ C.Rule = (*Port)(nil)
type Port struct { type Port struct {
adapter string adapter string
port C.Port port C.Port

View File

@ -7,6 +7,9 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
) )
// Implements C.Rule
var _ C.Rule = (*Process)(nil)
type Process struct { type Process struct {
adapter string adapter string
process string process string

View File

@ -165,10 +165,12 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r
proxy = proxies["DIRECT"] proxy = proxies["DIRECT"]
case Global: case Global:
proxy = proxies["GLOBAL"] proxy = proxies["GLOBAL"]
// Rule case Rule:
default:
proxy, rule, err = match(metadata) proxy, rule, err = match(metadata)
default:
panic(fmt.Sprintf("unknown mode: %s", mode))
} }
return return
} }