[Feature]
1.Add Network rule, match network type(TCP/UDP) 2.Add logic rules(NOT,OR,AND) -AND,((DOMAIN,baidu.com),(NETWORK,UDP)),REJECT (cherry picked from commit d7092e2e37f2c48282c878edea1b2ebc2912b09a)
This commit is contained in:
parent
0867bb8b8a
commit
6a78bca9fb
24 changed files with 637 additions and 98 deletions
56
common/collections/stack.go
Normal file
56
common/collections/stack.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package collections
|
||||
|
||||
import "sync"
|
||||
|
||||
type (
|
||||
stack struct {
|
||||
top *node
|
||||
length int
|
||||
lock *sync.RWMutex
|
||||
}
|
||||
|
||||
node struct {
|
||||
value interface{}
|
||||
prev *node
|
||||
}
|
||||
)
|
||||
|
||||
// NewStack Create a new stack
|
||||
func NewStack() *stack {
|
||||
return &stack{nil, 0, &sync.RWMutex{}}
|
||||
}
|
||||
|
||||
// Len Return the number of items in the stack
|
||||
func (this *stack) Len() int {
|
||||
return this.length
|
||||
}
|
||||
|
||||
// Peek View the top item on the stack
|
||||
func (this *stack) Peek() interface{} {
|
||||
if this.length == 0 {
|
||||
return nil
|
||||
}
|
||||
return this.top.value
|
||||
}
|
||||
|
||||
// Pop the top item of the stack and return it
|
||||
func (this *stack) Pop() interface{} {
|
||||
this.lock.Lock()
|
||||
defer this.lock.Unlock()
|
||||
if this.length == 0 {
|
||||
return nil
|
||||
}
|
||||
n := this.top
|
||||
this.top = n.prev
|
||||
this.length--
|
||||
return n.value
|
||||
}
|
||||
|
||||
// Push a value onto the top of the stack
|
||||
func (this *stack) Push(value interface{}) {
|
||||
this.lock.Lock()
|
||||
defer this.lock.Unlock()
|
||||
n := &node{value, this.top}
|
||||
this.top = n
|
||||
this.length++
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"container/list"
|
||||
"errors"
|
||||
"fmt"
|
||||
R "github.com/Dreamacro/clash/rule"
|
||||
RP "github.com/Dreamacro/clash/rule/provider"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -24,7 +26,6 @@ import (
|
|||
providerTypes "github.com/Dreamacro/clash/constant/provider"
|
||||
"github.com/Dreamacro/clash/dns"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
R "github.com/Dreamacro/clash/rule"
|
||||
T "github.com/Dreamacro/clash/tunnel"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
@ -484,13 +485,13 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
|
|||
|
||||
// parse rule provider
|
||||
for name, mapping := range cfg.RuleProvider {
|
||||
rp, err := R.ParseRuleProvider(name, mapping)
|
||||
rp, err := RP.ParseRuleProvider(name, mapping)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ruleProviders[name] = &rp
|
||||
R.SetRuleProvider(rp)
|
||||
RP.SetRuleProvider(rp)
|
||||
}
|
||||
|
||||
var rules []C.Rule
|
||||
|
@ -511,24 +512,29 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, map[strin
|
|||
continue
|
||||
}
|
||||
|
||||
switch l := len(rule); {
|
||||
case l == 2:
|
||||
target = rule[1]
|
||||
case l == 3:
|
||||
if ruleName == "MATCH" {
|
||||
payload = ""
|
||||
if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" {
|
||||
payload = strings.Join(rule[1:len(rule)-1], ",")
|
||||
target = rule[len(rule)-1]
|
||||
} else {
|
||||
switch l := len(rule); {
|
||||
case l == 2:
|
||||
target = rule[1]
|
||||
params = rule[2:]
|
||||
break
|
||||
case l == 3:
|
||||
if ruleName == "MATCH" {
|
||||
payload = ""
|
||||
target = rule[1]
|
||||
params = rule[2:]
|
||||
break
|
||||
}
|
||||
payload = rule[1]
|
||||
target = rule[2]
|
||||
case l >= 4:
|
||||
payload = rule[1]
|
||||
target = rule[2]
|
||||
params = rule[3:]
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
|
||||
}
|
||||
payload = rule[1]
|
||||
target = rule[2]
|
||||
case l >= 4:
|
||||
payload = rule[1]
|
||||
target = rule[2]
|
||||
params = rule[3:]
|
||||
default:
|
||||
return nil, nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line)
|
||||
}
|
||||
|
||||
if _, ok := proxies[target]; mode != T.Script && !ok {
|
||||
|
|
|
@ -14,7 +14,12 @@ const (
|
|||
Process
|
||||
Script
|
||||
RuleSet
|
||||
Network
|
||||
Combination
|
||||
MATCH
|
||||
AND
|
||||
OR
|
||||
NOT
|
||||
)
|
||||
|
||||
type RuleType int
|
||||
|
@ -47,6 +52,14 @@ func (rt RuleType) String() string {
|
|||
return "Match"
|
||||
case RuleSet:
|
||||
return "RuleSet"
|
||||
case Network:
|
||||
return "Network"
|
||||
case AND:
|
||||
return "AND"
|
||||
case OR:
|
||||
return "OR"
|
||||
case NOT:
|
||||
return "NOT"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -22,7 +22,7 @@ func HasNoResolve(params []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func findNetwork(params []string) C.NetWork {
|
||||
func FindNetwork(params []string) C.NetWork {
|
||||
for _, p := range params {
|
||||
if p == "tcp" {
|
||||
return C.TCP
|
||||
|
@ -33,7 +33,7 @@ func findNetwork(params []string) C.NetWork {
|
|||
return C.ALLNet
|
||||
}
|
||||
|
||||
func findSourceIPs(params []string) []*net.IPNet {
|
||||
func FindSourceIPs(params []string) []*net.IPNet {
|
||||
var ips []*net.IPNet
|
||||
for _, p := range params {
|
||||
if p == noResolve || len(p) < 7 {
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"net"
|
53
rule/common/network_type.go
Normal file
53
rule/common/network_type.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NetworkType struct {
|
||||
network C.NetWork
|
||||
adapter string
|
||||
}
|
||||
|
||||
func NewNetworkType(network, adapter string) (*NetworkType, error) {
|
||||
ntType := new(NetworkType)
|
||||
ntType.adapter = adapter
|
||||
switch strings.ToUpper(network) {
|
||||
case "TCP":
|
||||
ntType.network = C.TCP
|
||||
break
|
||||
case "UDP":
|
||||
ntType.network = C.UDP
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported network type, only TCP/UDP")
|
||||
}
|
||||
|
||||
return ntType, nil
|
||||
}
|
||||
|
||||
func (n *NetworkType) RuleType() C.RuleType {
|
||||
return C.Network
|
||||
}
|
||||
|
||||
func (n *NetworkType) Match(metadata *C.Metadata) bool {
|
||||
return n.network == metadata.NetWork
|
||||
}
|
||||
|
||||
func (n *NetworkType) Adapter() string {
|
||||
return n.adapter
|
||||
}
|
||||
|
||||
func (n *NetworkType) Payload() string {
|
||||
return n.network.String()
|
||||
}
|
||||
|
||||
func (n *NetworkType) ShouldResolveIP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *NetworkType) RuleExtra() *C.RuleExtra {
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
58
rule/logic/and.go
Normal file
58
rule/logic/and.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package logic
|
||||
|
||||
import C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
type AND struct {
|
||||
rules []C.Rule
|
||||
payload string
|
||||
adapter string
|
||||
needIP bool
|
||||
}
|
||||
|
||||
func NewAND(payload string, adapter string) (*AND, error) {
|
||||
and := &AND{payload: payload, adapter: adapter}
|
||||
rules, err := parseRule(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
and.rules = rules
|
||||
for _, rule := range rules {
|
||||
if rule.ShouldResolveIP() {
|
||||
and.needIP = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return and, nil
|
||||
}
|
||||
|
||||
func (A *AND) RuleType() C.RuleType {
|
||||
return C.AND
|
||||
}
|
||||
|
||||
func (A *AND) Match(metadata *C.Metadata) bool {
|
||||
for _, rule := range A.rules {
|
||||
if !rule.Match(metadata) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (A *AND) Adapter() string {
|
||||
return A.adapter
|
||||
}
|
||||
|
||||
func (A *AND) Payload() string {
|
||||
return A.payload
|
||||
}
|
||||
|
||||
func (A *AND) ShouldResolveIP() bool {
|
||||
return A.needIP
|
||||
}
|
||||
|
||||
func (A *AND) RuleExtra() *C.RuleExtra {
|
||||
return nil
|
||||
}
|
185
rule/logic/common.go
Normal file
185
rule/logic/common.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/common/collections"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
RC "github.com/Dreamacro/clash/rule/common"
|
||||
"github.com/Dreamacro/clash/rule/provider"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseRule(payload string) ([]C.Rule, error) {
|
||||
regex, err := regexp.Compile("\\(.*\\)")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if regex.MatchString(payload) {
|
||||
subRanges, err := format(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rules := make([]C.Rule, 0, len(subRanges))
|
||||
|
||||
if len(subRanges) == 1 {
|
||||
subPayload := payload[subRanges[0].start+1 : subRanges[0].end-1]
|
||||
rule, err := payloadToRule(subPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules = append(rules, rule)
|
||||
} else {
|
||||
preStart := subRanges[0].start
|
||||
preEnd := subRanges[0].end
|
||||
for _, sr := range subRanges[1:] {
|
||||
if containRange(sr, preStart, preEnd) && sr.start-preStart > 1 {
|
||||
str := ""
|
||||
if preStart+1 <= sr.start-1 {
|
||||
str = strings.TrimSpace(payload[preStart+1 : sr.start-1])
|
||||
}
|
||||
|
||||
if str == "AND" || str == "OR" || str == "NOT" {
|
||||
subPayload := payload[preStart+1 : preEnd]
|
||||
rule, err := payloadToRule(subPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules = append(rules, rule)
|
||||
preStart = sr.start
|
||||
preEnd = sr.end
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
preStart = sr.start
|
||||
preEnd = sr.end
|
||||
|
||||
subPayload := payload[sr.start+1 : sr.end]
|
||||
rule, err := payloadToRule(subPayload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("payload format error")
|
||||
}
|
||||
|
||||
func containRange(r Range, preStart, preEnd int) bool {
|
||||
return preStart < r.start && preEnd > r.end
|
||||
}
|
||||
|
||||
func payloadToRule(subPayload string) (C.Rule, error) {
|
||||
splitStr := strings.SplitN(subPayload, ",", 2)
|
||||
tp := splitStr[0]
|
||||
payload := splitStr[1]
|
||||
if tp == "NOT" || tp == "OR" || tp == "AND" {
|
||||
return parseSubRule(tp, payload, nil)
|
||||
}
|
||||
|
||||
param := strings.Split(payload, ",")
|
||||
return parseSubRule(tp, param[0], param[1:])
|
||||
}
|
||||
|
||||
func splitSubRule(subRuleStr string) (string, string, []string, error) {
|
||||
typeAndRule := strings.Split(subRuleStr, ",")
|
||||
if len(typeAndRule) < 2 {
|
||||
return "", "", nil, fmt.Errorf("format error:[%s]", typeAndRule)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(typeAndRule[0]), strings.TrimSpace(typeAndRule[1]), typeAndRule[2:], nil
|
||||
}
|
||||
|
||||
func parseSubRule(tp, payload string, params []string) (C.Rule, error) {
|
||||
var (
|
||||
parseErr error
|
||||
parsed C.Rule
|
||||
)
|
||||
|
||||
switch tp {
|
||||
case "DOMAIN":
|
||||
parsed = RC.NewDomain(payload, "", nil)
|
||||
case "DOMAIN-SUFFIX":
|
||||
parsed = RC.NewDomainSuffix(payload, "", nil)
|
||||
case "DOMAIN-KEYWORD":
|
||||
parsed = RC.NewDomainKeyword(payload, "", nil)
|
||||
case "GEOSITE":
|
||||
parsed, parseErr = RC.NewGEOSITE(payload, "", nil)
|
||||
case "GEOIP":
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewGEOIP(payload, "", noResolve, nil)
|
||||
case "IP-CIDR", "IP-CIDR6":
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, "", nil, RC.WithIPCIDRNoResolve(noResolve))
|
||||
case "SRC-IP-CIDR":
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, "", nil, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
|
||||
case "SRC-PORT":
|
||||
parsed, parseErr = RC.NewPort(payload, "", true, nil)
|
||||
case "DST-PORT":
|
||||
parsed, parseErr = RC.NewPort(payload, "", false, nil)
|
||||
case "PROCESS-NAME":
|
||||
parsed, parseErr = RC.NewProcess(payload, "", nil)
|
||||
case "RULE-SET":
|
||||
parsed, parseErr = provider.NewRuleSet(payload, "", nil)
|
||||
case "NOT":
|
||||
parsed, parseErr = NewNOT(payload, "")
|
||||
case "AND":
|
||||
parsed, parseErr = NewAND(payload, "")
|
||||
case "OR":
|
||||
parsed, parseErr = NewOR(payload, "")
|
||||
case "NETWORK":
|
||||
parsed, parseErr = RC.NewNetworkType(payload, "")
|
||||
default:
|
||||
parseErr = fmt.Errorf("unsupported rule type %s", tp)
|
||||
}
|
||||
|
||||
return parsed, parseErr
|
||||
}
|
||||
|
||||
type Range struct {
|
||||
start int
|
||||
end int
|
||||
index int
|
||||
}
|
||||
|
||||
func format(payload string) ([]Range, error) {
|
||||
stack := collections.NewStack()
|
||||
num := 0
|
||||
subRanges := make([]Range, 0)
|
||||
for i, c := range payload {
|
||||
if c == '(' {
|
||||
sr := Range{
|
||||
start: i,
|
||||
index: num,
|
||||
}
|
||||
|
||||
num++
|
||||
stack.Push(sr)
|
||||
} else if c == ')' {
|
||||
sr := stack.Pop().(Range)
|
||||
sr.end = i
|
||||
subRanges = append(subRanges, sr)
|
||||
}
|
||||
}
|
||||
|
||||
if stack.Len() != 0 {
|
||||
return nil, fmt.Errorf("format error is missing )")
|
||||
}
|
||||
|
||||
sortResult := make([]Range, len(subRanges))
|
||||
for _, sr := range subRanges {
|
||||
sortResult[sr.index] = sr
|
||||
}
|
||||
|
||||
return sortResult, nil
|
||||
}
|
51
rule/logic/not.go
Normal file
51
rule/logic/not.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package logic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type NOT struct {
|
||||
rule C.Rule
|
||||
payload string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func NewNOT(payload string, adapter string) (*NOT, error) {
|
||||
not := &NOT{payload: payload, adapter: adapter}
|
||||
rule, err := parseRule(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(rule) < 1 {
|
||||
return nil, fmt.Errorf("the parsed rule is empty")
|
||||
}
|
||||
|
||||
not.rule = rule[0]
|
||||
return not, nil
|
||||
}
|
||||
|
||||
func (not *NOT) RuleType() C.RuleType {
|
||||
return C.NOT
|
||||
}
|
||||
|
||||
func (not *NOT) Match(metadata *C.Metadata) bool {
|
||||
return !not.rule.Match(metadata)
|
||||
}
|
||||
|
||||
func (not *NOT) Adapter() string {
|
||||
return not.adapter
|
||||
}
|
||||
|
||||
func (not *NOT) Payload() string {
|
||||
return not.payload
|
||||
}
|
||||
|
||||
func (not *NOT) ShouldResolveIP() bool {
|
||||
return not.rule.ShouldResolveIP()
|
||||
}
|
||||
|
||||
func (not *NOT) RuleExtra() *C.RuleExtra {
|
||||
return nil
|
||||
}
|
58
rule/logic/or.go
Normal file
58
rule/logic/or.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package logic
|
||||
|
||||
import C "github.com/Dreamacro/clash/constant"
|
||||
|
||||
type OR struct {
|
||||
rules []C.Rule
|
||||
payload string
|
||||
adapter string
|
||||
needIP bool
|
||||
}
|
||||
|
||||
func (or *OR) RuleType() C.RuleType {
|
||||
return C.OR
|
||||
}
|
||||
|
||||
func (or *OR) Match(metadata *C.Metadata) bool {
|
||||
for _, rule := range or.rules {
|
||||
if rule.Match(metadata) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (or *OR) Adapter() string {
|
||||
return or.adapter
|
||||
}
|
||||
|
||||
func (or *OR) Payload() string {
|
||||
return or.payload
|
||||
}
|
||||
|
||||
func (or *OR) ShouldResolveIP() bool {
|
||||
return or.needIP
|
||||
}
|
||||
|
||||
func (or *OR) RuleExtra() *C.RuleExtra {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewOR(payload string, adapter string) (*OR, error) {
|
||||
or := &OR{payload: payload, adapter: adapter}
|
||||
rules, err := parseRule(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
or.rules = rules
|
||||
for _, rule := range rules {
|
||||
if rule.ShouldResolveIP() {
|
||||
or.needIP = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return or, nil
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
package rules
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
P "github.com/Dreamacro/clash/constant/provider"
|
||||
"time"
|
||||
RC "github.com/Dreamacro/clash/rule/common"
|
||||
"github.com/Dreamacro/clash/rule/logic"
|
||||
RP "github.com/Dreamacro/clash/rule/provider"
|
||||
)
|
||||
|
||||
func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
|
||||
|
@ -16,81 +15,48 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
|
|||
)
|
||||
|
||||
ruleExtra := &C.RuleExtra{
|
||||
Network: findNetwork(params),
|
||||
SourceIPs: findSourceIPs(params),
|
||||
Network: RC.FindNetwork(params),
|
||||
SourceIPs: RC.FindSourceIPs(params),
|
||||
}
|
||||
|
||||
switch tp {
|
||||
case "DOMAIN":
|
||||
parsed = NewDomain(payload, target, ruleExtra)
|
||||
parsed = RC.NewDomain(payload, target, ruleExtra)
|
||||
case "DOMAIN-SUFFIX":
|
||||
parsed = NewDomainSuffix(payload, target, ruleExtra)
|
||||
parsed = RC.NewDomainSuffix(payload, target, ruleExtra)
|
||||
case "DOMAIN-KEYWORD":
|
||||
parsed = NewDomainKeyword(payload, target, ruleExtra)
|
||||
parsed = RC.NewDomainKeyword(payload, target, ruleExtra)
|
||||
case "GEOSITE":
|
||||
parsed, parseErr = NewGEOSITE(payload, target, ruleExtra)
|
||||
parsed, parseErr = RC.NewGEOSITE(payload, target, ruleExtra)
|
||||
case "GEOIP":
|
||||
noResolve := HasNoResolve(params)
|
||||
parsed, parseErr = NewGEOIP(payload, target, noResolve, ruleExtra)
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewGEOIP(payload, target, noResolve, ruleExtra)
|
||||
case "IP-CIDR", "IP-CIDR6":
|
||||
noResolve := HasNoResolve(params)
|
||||
parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRNoResolve(noResolve))
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, target, ruleExtra, RC.WithIPCIDRNoResolve(noResolve))
|
||||
case "SRC-IP-CIDR":
|
||||
parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, target, ruleExtra, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
|
||||
case "SRC-PORT":
|
||||
parsed, parseErr = NewPort(payload, target, true, ruleExtra)
|
||||
parsed, parseErr = RC.NewPort(payload, target, true, ruleExtra)
|
||||
case "DST-PORT":
|
||||
parsed, parseErr = NewPort(payload, target, false, ruleExtra)
|
||||
parsed, parseErr = RC.NewPort(payload, target, false, ruleExtra)
|
||||
case "PROCESS-NAME":
|
||||
parsed, parseErr = NewProcess(payload, target, ruleExtra)
|
||||
parsed, parseErr = RC.NewProcess(payload, target, ruleExtra)
|
||||
case "MATCH":
|
||||
parsed = NewMatch(target, ruleExtra)
|
||||
parsed = RC.NewMatch(target, ruleExtra)
|
||||
case "RULE-SET":
|
||||
parsed, parseErr = NewRuleSet(payload, target, ruleExtra)
|
||||
parsed, parseErr = RP.NewRuleSet(payload, target, ruleExtra)
|
||||
case "NETWORK":
|
||||
parsed, parseErr = RC.NewNetworkType(payload, target)
|
||||
case "AND":
|
||||
parsed, parseErr = logic.NewAND(payload, target)
|
||||
case "OR":
|
||||
parsed, parseErr = logic.NewOR(payload, target)
|
||||
case "NOT":
|
||||
parsed, parseErr = logic.NewNOT(payload, target)
|
||||
default:
|
||||
parseErr = fmt.Errorf("unsupported rule type %s", tp)
|
||||
}
|
||||
|
||||
return parsed, parseErr
|
||||
}
|
||||
|
||||
type ruleProviderSchema struct {
|
||||
Type string `provider:"type"`
|
||||
Behavior string `provider:"behavior"`
|
||||
Path string `provider:"path"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
}
|
||||
|
||||
func ParseRuleProvider(name string, mapping map[string]interface{}) (P.RuleProvider, error) {
|
||||
schema := &ruleProviderSchema{}
|
||||
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
|
||||
if err := decoder.Decode(mapping, schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var behavior P.RuleType
|
||||
|
||||
switch schema.Behavior {
|
||||
case "domain":
|
||||
behavior = P.Domain
|
||||
case "ipcidr":
|
||||
behavior = P.IPCIDR
|
||||
case "classical":
|
||||
behavior = P.Classical
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior)
|
||||
}
|
||||
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
var vehicle P.Vehicle
|
||||
switch schema.Type {
|
||||
case "file":
|
||||
vehicle = provider.NewFileVehicle(path)
|
||||
case "http":
|
||||
vehicle = provider.NewHTTPVehicle(schema.URL, path)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
|
||||
}
|
||||
|
||||
return NewRuleSetProvider(name, behavior, time.Duration(uint(schema.Interval))*time.Second, vehicle), nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package provider
|
||||
|
||||
import (
|
||||
"bytes"
|
93
rule/provider/parse.go
Normal file
93
rule/provider/parse.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/adapter/provider"
|
||||
"github.com/Dreamacro/clash/common/structure"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
P "github.com/Dreamacro/clash/constant/provider"
|
||||
RC "github.com/Dreamacro/clash/rule/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ruleProviderSchema struct {
|
||||
Type string `provider:"type"`
|
||||
Behavior string `provider:"behavior"`
|
||||
Path string `provider:"path"`
|
||||
URL string `provider:"url,omitempty"`
|
||||
Interval int `provider:"interval,omitempty"`
|
||||
}
|
||||
|
||||
func ParseRuleProvider(name string, mapping map[string]interface{}) (P.RuleProvider, error) {
|
||||
schema := &ruleProviderSchema{}
|
||||
decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true})
|
||||
if err := decoder.Decode(mapping, schema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var behavior P.RuleType
|
||||
|
||||
switch schema.Behavior {
|
||||
case "domain":
|
||||
behavior = P.Domain
|
||||
case "ipcidr":
|
||||
behavior = P.IPCIDR
|
||||
case "classical":
|
||||
behavior = P.Classical
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior)
|
||||
}
|
||||
|
||||
path := C.Path.Resolve(schema.Path)
|
||||
var vehicle P.Vehicle
|
||||
switch schema.Type {
|
||||
case "file":
|
||||
vehicle = provider.NewFileVehicle(path)
|
||||
case "http":
|
||||
vehicle = provider.NewHTTPVehicle(schema.URL, path)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type)
|
||||
}
|
||||
|
||||
return NewRuleSetProvider(name, behavior, time.Duration(uint(schema.Interval))*time.Second, vehicle), nil
|
||||
}
|
||||
|
||||
func parseRule(tp, payload, target string, params []string) (C.Rule, error) {
|
||||
var (
|
||||
parseErr error
|
||||
parsed C.Rule
|
||||
)
|
||||
|
||||
ruleExtra := &C.RuleExtra{
|
||||
Network: RC.FindNetwork(params),
|
||||
SourceIPs: RC.FindSourceIPs(params),
|
||||
}
|
||||
|
||||
switch tp {
|
||||
case "DOMAIN":
|
||||
parsed = RC.NewDomain(payload, target, ruleExtra)
|
||||
case "DOMAIN-SUFFIX":
|
||||
parsed = RC.NewDomainSuffix(payload, target, ruleExtra)
|
||||
case "DOMAIN-KEYWORD":
|
||||
parsed = RC.NewDomainKeyword(payload, target, ruleExtra)
|
||||
case "GEOSITE":
|
||||
parsed, parseErr = RC.NewGEOSITE(payload, target, ruleExtra)
|
||||
case "GEOIP":
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewGEOIP(payload, target, noResolve, ruleExtra)
|
||||
case "IP-CIDR", "IP-CIDR6":
|
||||
noResolve := RC.HasNoResolve(params)
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, target, ruleExtra, RC.WithIPCIDRNoResolve(noResolve))
|
||||
case "SRC-IP-CIDR":
|
||||
parsed, parseErr = RC.NewIPCIDR(payload, target, ruleExtra, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))
|
||||
case "SRC-PORT":
|
||||
parsed, parseErr = RC.NewPort(payload, target, true, ruleExtra)
|
||||
case "DST-PORT":
|
||||
parsed, parseErr = RC.NewPort(payload, target, false, ruleExtra)
|
||||
case "PROCESS-NAME":
|
||||
parsed, parseErr = RC.NewProcess(payload, target, ruleExtra)
|
||||
default:
|
||||
parseErr = fmt.Errorf("unsupported rule type %s", tp)
|
||||
}
|
||||
|
||||
return parsed, parseErr
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package provider
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -205,7 +205,7 @@ func handleClassicalRules(rules []string) (interface{}, error) {
|
|||
return nil, errors.New("error rule type")
|
||||
}
|
||||
|
||||
r, err := ParseRule(ruleType, rule, "", params)
|
||||
r, err := parseRule(ruleType, rule, "", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rules
|
||||
package provider
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -3,6 +3,7 @@ package tunnel
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
R "github.com/Dreamacro/clash/rule/common"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
@ -15,7 +16,6 @@ import (
|
|||
"github.com/Dreamacro/clash/constant/provider"
|
||||
icontext "github.com/Dreamacro/clash/context"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
R "github.com/Dreamacro/clash/rule"
|
||||
"github.com/Dreamacro/clash/tunnel/statistic"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in a new issue