Feature: add source ipcidr condition for all rules

This commit is contained in:
yaling888 2021-08-31 21:46:04 +08:00
parent 83c9664c17
commit 4cc16e0136
14 changed files with 155 additions and 93 deletions

View file

@ -52,5 +52,5 @@ type Rule interface {
Adapter() string
Payload() string
ShouldResolveIP() bool
NetWork() NetWork
RuleExtra() *RuleExtra
}

25
constant/rule_extra.go Normal file
View file

@ -0,0 +1,25 @@
package constant
import "net"
type RuleExtra struct {
Network NetWork
SourceIPs []*net.IPNet
}
func (re *RuleExtra) NotMatchNetwork(network NetWork) bool {
return re.Network != ALLNet && re.Network != network
}
func (re *RuleExtra) NotMatchSourceIP(srcIP net.IP) bool {
if re.SourceIPs == nil {
return false
}
for _, ips := range re.SourceIPs {
if ips.Contains(srcIP) {
return false
}
}
return true
}

View file

@ -2,6 +2,8 @@ package rules
import (
"errors"
"net"
"strings"
C "github.com/Dreamacro/clash/constant"
)
@ -14,6 +16,7 @@ var (
func HasNoResolve(params []string) bool {
for _, p := range params {
p = strings.Trim(p, " ")
if p == noResolve {
return true
}
@ -23,6 +26,7 @@ func HasNoResolve(params []string) bool {
func findNetwork(params []string) C.NetWork {
for _, p := range params {
p = strings.Trim(p, " ")
if p == "tcp" {
return C.TCP
} else if p == "udp" {
@ -31,3 +35,23 @@ func findNetwork(params []string) C.NetWork {
}
return C.ALLNet
}
func findSourceIPs(params []string) []*net.IPNet {
var ips []*net.IPNet
for _, p := range params {
p = strings.Trim(p, " ")
if p == noResolve || len(p) < 7 {
continue
}
_, ipnet, err := net.ParseCIDR(p)
if err != nil {
continue
}
ips = append(ips, ipnet)
}
if len(ips) > 0 {
return ips
}
return nil
}

View file

@ -7,9 +7,9 @@ import (
)
type Domain struct {
domain string
adapter string
network C.NetWork
domain string
adapter string
ruleExtra *C.RuleExtra
}
func (d *Domain) RuleType() C.RuleType {
@ -35,14 +35,14 @@ func (d *Domain) ShouldResolveIP() bool {
return false
}
func (d *Domain) NetWork() C.NetWork {
return d.network
func (d *Domain) RuleExtra() *C.RuleExtra {
return d.ruleExtra
}
func NewDomain(domain string, adapter string, network C.NetWork) *Domain {
func NewDomain(domain string, adapter string, ruleExtra *C.RuleExtra) *Domain {
return &Domain{
domain: strings.ToLower(domain),
adapter: adapter,
network: network,
domain: strings.ToLower(domain),
adapter: adapter,
ruleExtra: ruleExtra,
}
}

View file

@ -7,9 +7,9 @@ import (
)
type DomainKeyword struct {
keyword string
adapter string
network C.NetWork
keyword string
adapter string
ruleExtra *C.RuleExtra
}
func (dk *DomainKeyword) RuleType() C.RuleType {
@ -36,14 +36,14 @@ func (dk *DomainKeyword) ShouldResolveIP() bool {
return false
}
func (dk *DomainKeyword) NetWork() C.NetWork {
return dk.network
func (dk *DomainKeyword) RuleExtra() *C.RuleExtra {
return dk.ruleExtra
}
func NewDomainKeyword(keyword string, adapter string, network C.NetWork) *DomainKeyword {
func NewDomainKeyword(keyword string, adapter string, ruleExtra *C.RuleExtra) *DomainKeyword {
return &DomainKeyword{
keyword: strings.ToLower(keyword),
adapter: adapter,
network: network,
keyword: strings.ToLower(keyword),
adapter: adapter,
ruleExtra: ruleExtra,
}
}

View file

@ -7,9 +7,9 @@ import (
)
type DomainSuffix struct {
suffix string
adapter string
network C.NetWork
suffix string
adapter string
ruleExtra *C.RuleExtra
}
func (ds *DomainSuffix) RuleType() C.RuleType {
@ -36,14 +36,14 @@ func (ds *DomainSuffix) ShouldResolveIP() bool {
return false
}
func (ds *DomainSuffix) NetWork() C.NetWork {
return ds.network
func (ds *DomainSuffix) RuleExtra() *C.RuleExtra {
return ds.ruleExtra
}
func NewDomainSuffix(suffix string, adapter string, network C.NetWork) *DomainSuffix {
func NewDomainSuffix(suffix string, adapter string, ruleExtra *C.RuleExtra) *DomainSuffix {
return &DomainSuffix{
suffix: strings.ToLower(suffix),
adapter: adapter,
network: network,
suffix: strings.ToLower(suffix),
adapter: adapter,
ruleExtra: ruleExtra,
}
}

View file

@ -28,8 +28,8 @@ func (f *Match) ShouldResolveIP() bool {
return false
}
func (f *Match) NetWork() C.NetWork {
return C.ALLNet
func (f *Match) RuleExtra() *C.RuleExtra {
return nil
}
func NewMatch(adapter string) *Match {

View file

@ -15,7 +15,7 @@ type GEOIP struct {
country string
adapter string
noResolveIP bool
network C.NetWork
ruleExtra *C.RuleExtra
geoIPMatcher *router.GeoIPMatcher
}
@ -43,11 +43,11 @@ func (g *GEOIP) ShouldResolveIP() bool {
return !g.noResolveIP
}
func (g *GEOIP) NetWork() C.NetWork {
return g.network
func (g *GEOIP) RuleExtra() *C.RuleExtra {
return g.ruleExtra
}
func NewGEOIP(country string, adapter string, noResolveIP bool, network C.NetWork) (*GEOIP, error) {
func NewGEOIP(country string, adapter string, noResolveIP bool, ruleExtra *C.RuleExtra) (*GEOIP, error) {
geoLoaderName := "standard"
//geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
@ -78,7 +78,7 @@ func NewGEOIP(country string, adapter string, noResolveIP bool, network C.NetWor
country: country,
adapter: adapter,
noResolveIP: noResolveIP,
network: network,
ruleExtra: ruleExtra,
geoIPMatcher: geoIPMatcher,
}

View file

@ -12,10 +12,10 @@ import (
)
type GEOSITE struct {
country string
adapter string
network C.NetWork
matcher *router.DomainMatcher
country string
adapter string
ruleExtra *C.RuleExtra
matcher *router.DomainMatcher
}
func (gs *GEOSITE) RuleType() C.RuleType {
@ -43,11 +43,11 @@ func (gs *GEOSITE) ShouldResolveIP() bool {
return false
}
func (gs *GEOSITE) NetWork() C.NetWork {
return gs.network
func (gs *GEOSITE) RuleExtra() *C.RuleExtra {
return gs.ruleExtra
}
func NewGEOSITE(country string, adapter string, network C.NetWork) (*GEOSITE, error) {
func NewGEOSITE(country string, adapter string, ruleExtra *C.RuleExtra) (*GEOSITE, error) {
geoLoaderName := "standard"
//geoLoaderName := "memconservative"
geoLoader, err := geodata.GetGeoDataLoader(geoLoaderName)
@ -72,10 +72,10 @@ func NewGEOSITE(country string, adapter string, network C.NetWork) (*GEOSITE, er
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, len(domains))
geoSite := &GEOSITE{
country: country,
adapter: adapter,
network: network,
matcher: matcher,
country: country,
adapter: adapter,
ruleExtra: ruleExtra,
matcher: matcher,
}
return geoSite, nil

View file

@ -23,7 +23,7 @@ func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
type IPCIDR struct {
ipnet *net.IPNet
adapter string
network C.NetWork
ruleExtra *C.RuleExtra
isSourceIP bool
noResolveIP bool
}
@ -55,20 +55,20 @@ func (i *IPCIDR) ShouldResolveIP() bool {
return !i.noResolveIP
}
func (i *IPCIDR) NetWork() C.NetWork {
return i.network
func (i *IPCIDR) RuleExtra() *C.RuleExtra {
return i.ruleExtra
}
func NewIPCIDR(s string, adapter string, network C.NetWork, opts ...IPCIDROption) (*IPCIDR, error) {
func NewIPCIDR(s string, adapter string, ruleExtra *C.RuleExtra, opts ...IPCIDROption) (*IPCIDR, error) {
_, ipnet, err := net.ParseCIDR(s)
if err != nil {
return nil, errPayload
}
ruleExtra.SourceIPs = nil
ipcidr := &IPCIDR{
ipnet: ipnet,
adapter: adapter,
network: network,
ipnet: ipnet,
adapter: adapter,
ruleExtra: ruleExtra,
}
for _, o := range opts {

View file

@ -10,32 +10,36 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) {
var (
parseErr error
parsed C.Rule
network = findNetwork(params)
)
ruleExtra := &C.RuleExtra{
Network: findNetwork(params),
SourceIPs: findSourceIPs(params),
}
switch tp {
case "DOMAIN":
parsed = NewDomain(payload, target, network)
parsed = NewDomain(payload, target, ruleExtra)
case "DOMAIN-SUFFIX":
parsed = NewDomainSuffix(payload, target, network)
parsed = NewDomainSuffix(payload, target, ruleExtra)
case "DOMAIN-KEYWORD":
parsed = NewDomainKeyword(payload, target, network)
parsed = NewDomainKeyword(payload, target, ruleExtra)
case "GEOSITE":
parsed, parseErr = NewGEOSITE(payload, target, network)
parsed, parseErr = NewGEOSITE(payload, target, ruleExtra)
case "GEOIP":
noResolve := HasNoResolve(params)
parsed, parseErr = NewGEOIP(payload, target, noResolve, network)
parsed, parseErr = NewGEOIP(payload, target, noResolve, ruleExtra)
case "IP-CIDR", "IP-CIDR6":
noResolve := HasNoResolve(params)
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRNoResolve(noResolve))
parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRNoResolve(noResolve))
case "SRC-IP-CIDR":
parsed, parseErr = NewIPCIDR(payload, target, network, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
parsed, parseErr = NewIPCIDR(payload, target, ruleExtra, WithIPCIDRSourceIP(true), WithIPCIDRNoResolve(true))
case "SRC-PORT":
parsed, parseErr = NewPort(payload, target, true, network)
parsed, parseErr = NewPort(payload, target, true, ruleExtra)
case "DST-PORT":
parsed, parseErr = NewPort(payload, target, false, network)
parsed, parseErr = NewPort(payload, target, false, ruleExtra)
case "PROCESS-NAME":
parsed, parseErr = NewProcess(payload, target, network)
parsed, parseErr = NewProcess(payload, target, ruleExtra)
case "MATCH":
parsed = NewMatch(target)
default:

View file

@ -14,11 +14,11 @@ type portReal struct {
}
type Port struct {
adapter string
port string
isSource bool
portList []portReal
network C.NetWork
adapter string
port string
isSource bool
portList []portReal
ruleExtra *C.RuleExtra
}
func (p *Port) RuleType() C.RuleType {
@ -47,8 +47,8 @@ func (p *Port) ShouldResolveIP() bool {
return false
}
func (p *Port) NetWork() C.NetWork {
return p.network
func (p *Port) RuleExtra() *C.RuleExtra {
return p.ruleExtra
}
func (p *Port) matchPortReal(portRef string) bool {
@ -67,7 +67,7 @@ func (p *Port) matchPortReal(portRef string) bool {
return false
}
func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Port, error) {
func NewPort(port string, adapter string, isSource bool, ruleExtra *C.RuleExtra) (*Port, error) {
//the port format should be like this: "123/136/137-139" or "[123]/[136-139]"
ports := strings.Split(port, "/")
if len(ports) > 28 {
@ -114,10 +114,10 @@ func NewPort(port string, adapter string, isSource bool, network C.NetWork) (*Po
}
return &Port{
adapter: adapter,
port: port,
isSource: isSource,
portList: portList,
network: network,
adapter: adapter,
port: port,
isSource: isSource,
portList: portList,
ruleExtra: ruleExtra,
}, nil
}

View file

@ -14,9 +14,9 @@ import (
var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
type Process struct {
adapter string
process string
network C.NetWork
adapter string
process string
ruleExtra *C.RuleExtra
}
func (ps *Process) RuleType() C.RuleType {
@ -71,14 +71,14 @@ func (ps *Process) ShouldResolveIP() bool {
return false
}
func (ps *Process) NetWork() C.NetWork {
return ps.network
func (ps *Process) RuleExtra() *C.RuleExtra {
return ps.ruleExtra
}
func NewProcess(process string, adapter string, network C.NetWork) (*Process, error) {
func NewProcess(process string, adapter string, ruleExtra *C.RuleExtra) (*Process, error) {
return &Process{
adapter: adapter,
process: process,
network: network,
adapter: adapter,
process: process,
ruleExtra: ruleExtra,
}, nil
}

View file

@ -33,7 +33,7 @@ var (
// default timeout for UDP session
udpTimeout = 60 * time.Second
preProcessCacheFinder, _ = R.NewProcess("", "", C.ALLNet)
preProcessCacheFinder, _ = R.NewProcess("", "", nil)
tunBroadcastAddr = net.IPv4(198, 18, 255, 255)
)
@ -235,7 +235,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) {
switch true {
case rule != nil:
log.Infoln("[UDP] %s(%s) --> %s:%s match %s(%s) %s using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rule.NetWork().String(), rawPc.Chains().String())
log.Infoln("[UDP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rawPc.Chains().String())
case mode == Global:
log.Infoln("[UDP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct:
@ -285,7 +285,7 @@ func handleTCPConn(ctx C.ConnContext) {
switch true {
case rule != nil:
log.Infoln("[TCP] %s(%s) --> %s:%s match %s(%s) %s using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), rule.NetWork().String(), remoteConn.Chains().String())
log.Infoln("[TCP] %s(%s) --> %s:%s match %s(%s) using %s", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress(), metadata.DstPort, rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String())
case mode == Global:
log.Infoln("[TCP] %s(%s) --> %s using GLOBAL", metadata.SourceAddress(), metadata.Process, metadata.RemoteAddress())
case mode == Direct:
@ -339,12 +339,21 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) {
continue
}
if rule.NetWork() != C.ALLNet && rule.NetWork() != metadata.NetWork {
continue
extra := rule.RuleExtra()
if extra != nil {
if extra.NotMatchNetwork(metadata.NetWork) {
continue
}
if extra.NotMatchSourceIP(metadata.SrcIP) {
continue
}
}
return adapter, rule, nil
}
}
return proxies["DIRECT"], nil, nil
//return proxies["DIRECT"], nil, nil
return proxies["REJECT"], nil, nil
}