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
- unused
- usestdlibvars
- exhaustive
linters-settings:
gci:
@ -21,3 +22,5 @@ linters-settings:
- default
staticcheck:
go: '1.21'
exhaustive:
default-signifies-exhaustive: true

View File

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

View File

@ -1,5 +1,27 @@
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
const (
Domain RuleType = iota

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,6 +7,9 @@ import (
C "github.com/Dreamacro/clash/constant"
)
// Implements C.Rule
var _ C.Rule = (*GEOIP)(nil)
type GEOIP struct {
country 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 {
ipnet *net.IPNet
adapter string

View File

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

View File

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

View File

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

View File

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