chore: 调整目录与包名一致
This commit is contained in:
88
rules/common/base.go
Normal file
88
rules/common/base.go
Normal file
@ -0,0 +1,88 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
var (
|
||||
errPayload = errors.New("payload error")
|
||||
initFlag bool
|
||||
noResolve = "no-resolve"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
ruleExtra *C.RuleExtra
|
||||
}
|
||||
|
||||
func (b *Base) RuleExtra() *C.RuleExtra {
|
||||
return b.ruleExtra
|
||||
}
|
||||
|
||||
func (b *Base) SetRuleExtra(re *C.RuleExtra) {
|
||||
b.ruleExtra = re
|
||||
}
|
||||
|
||||
func (b *Base) ShouldFindProcess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Base) ShouldResolveIP() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func HasNoResolve(params []string) bool {
|
||||
for _, p := range params {
|
||||
if p == noResolve {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func FindNetwork(params []string) C.NetWork {
|
||||
for _, p := range params {
|
||||
if strings.EqualFold(p, "tcp") {
|
||||
return C.TCP
|
||||
} else if strings.EqualFold(p, "udp") {
|
||||
return C.UDP
|
||||
}
|
||||
}
|
||||
return C.ALLNet
|
||||
}
|
||||
|
||||
func FindSourceIPs(params []string) []*netip.Prefix {
|
||||
var ips []*netip.Prefix
|
||||
for _, p := range params {
|
||||
if p == noResolve || len(p) < 7 {
|
||||
continue
|
||||
}
|
||||
ipnet, err := netip.ParsePrefix(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ips = append(ips, &ipnet)
|
||||
}
|
||||
|
||||
if len(ips) > 0 {
|
||||
return ips
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindProcessName(params []string) []string {
|
||||
var processNames []string
|
||||
for _, p := range params {
|
||||
if strings.HasPrefix(p, "P:") {
|
||||
processNames = append(processNames, strings.TrimPrefix(p, "P:"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(processNames) > 0 {
|
||||
return processNames
|
||||
}
|
||||
return nil
|
||||
}
|
42
rules/common/domain.go
Normal file
42
rules/common/domain.go
Normal file
@ -0,0 +1,42 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Domain struct {
|
||||
*Base
|
||||
domain string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (d *Domain) RuleType() C.RuleType {
|
||||
return C.Domain
|
||||
}
|
||||
|
||||
func (d *Domain) Match(metadata *C.Metadata) bool {
|
||||
if metadata.AddrType != C.AtypDomainName {
|
||||
return false
|
||||
}
|
||||
return metadata.Host == d.domain
|
||||
}
|
||||
|
||||
func (d *Domain) Adapter() string {
|
||||
return d.adapter
|
||||
}
|
||||
|
||||
func (d *Domain) Payload() string {
|
||||
return d.domain
|
||||
}
|
||||
|
||||
func NewDomain(domain string, adapter string) *Domain {
|
||||
return &Domain{
|
||||
Base: &Base{},
|
||||
domain: strings.ToLower(domain),
|
||||
adapter: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
var _ C.Rule = (*Domain)(nil)
|
43
rules/common/domain_keyword.go
Normal file
43
rules/common/domain_keyword.go
Normal file
@ -0,0 +1,43 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type DomainKeyword struct {
|
||||
*Base
|
||||
keyword string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) RuleType() C.RuleType {
|
||||
return C.DomainKeyword
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) Match(metadata *C.Metadata) bool {
|
||||
if metadata.AddrType != C.AtypDomainName {
|
||||
return false
|
||||
}
|
||||
domain := metadata.Host
|
||||
return strings.Contains(domain, dk.keyword)
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) Adapter() string {
|
||||
return dk.adapter
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) Payload() string {
|
||||
return dk.keyword
|
||||
}
|
||||
|
||||
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
||||
return &DomainKeyword{
|
||||
Base: &Base{},
|
||||
keyword: strings.ToLower(keyword),
|
||||
adapter: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
var _ C.Rule = (*DomainKeyword)(nil)
|
43
rules/common/domain_suffix.go
Normal file
43
rules/common/domain_suffix.go
Normal file
@ -0,0 +1,43 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type DomainSuffix struct {
|
||||
*Base
|
||||
suffix string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) RuleType() C.RuleType {
|
||||
return C.DomainSuffix
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) Match(metadata *C.Metadata) bool {
|
||||
if metadata.AddrType != C.AtypDomainName {
|
||||
return false
|
||||
}
|
||||
domain := metadata.Host
|
||||
return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) Adapter() string {
|
||||
return ds.adapter
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) Payload() string {
|
||||
return ds.suffix
|
||||
}
|
||||
|
||||
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
|
||||
return &DomainSuffix{
|
||||
Base: &Base{},
|
||||
suffix: strings.ToLower(suffix),
|
||||
adapter: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
var _ C.Rule = (*DomainSuffix)(nil)
|
35
rules/common/final.go
Normal file
35
rules/common/final.go
Normal file
@ -0,0 +1,35 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Match struct {
|
||||
*Base
|
||||
adapter string
|
||||
}
|
||||
|
||||
func (f *Match) RuleType() C.RuleType {
|
||||
return C.MATCH
|
||||
}
|
||||
|
||||
func (f *Match) Match(metadata *C.Metadata) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *Match) Adapter() string {
|
||||
return f.adapter
|
||||
}
|
||||
|
||||
func (f *Match) Payload() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func NewMatch(adapter string) *Match {
|
||||
return &Match{
|
||||
Base: &Base{},
|
||||
adapter: adapter,
|
||||
}
|
||||
}
|
||||
|
||||
var _ C.Rule = (*Match)(nil)
|
101
rules/common/geoip.go
Normal file
101
rules/common/geoip.go
Normal file
@ -0,0 +1,101 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/component/geodata"
|
||||
"github.com/Dreamacro/clash/component/geodata/router"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/component/mmdb"
|
||||
"github.com/Dreamacro/clash/component/resolver"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
)
|
||||
|
||||
type GEOIP struct {
|
||||
*Base
|
||||
country string
|
||||
adapter string
|
||||
noResolveIP bool
|
||||
geoIPMatcher *router.GeoIPMatcher
|
||||
recodeSize int
|
||||
}
|
||||
|
||||
func (g *GEOIP) RuleType() C.RuleType {
|
||||
return C.GEOIP
|
||||
}
|
||||
|
||||
func (g *GEOIP) Match(metadata *C.Metadata) bool {
|
||||
ip := metadata.DstIP
|
||||
if !ip.IsValid() {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.EqualFold(g.country, "LAN") {
|
||||
return ip.IsPrivate() ||
|
||||
ip.IsUnspecified() ||
|
||||
ip.IsLoopback() ||
|
||||
ip.IsMulticast() ||
|
||||
ip.IsLinkLocalUnicast() ||
|
||||
resolver.IsFakeBroadcastIP(ip)
|
||||
}
|
||||
if !C.GeodataMode {
|
||||
record, _ := mmdb.Instance().Country(ip.AsSlice())
|
||||
return strings.EqualFold(record.Country.IsoCode, g.country)
|
||||
}
|
||||
return g.geoIPMatcher.Match(ip.AsSlice())
|
||||
}
|
||||
|
||||
func (g *GEOIP) Adapter() string {
|
||||
return g.adapter
|
||||
}
|
||||
|
||||
func (g *GEOIP) Payload() string {
|
||||
return g.country
|
||||
}
|
||||
|
||||
func (g *GEOIP) ShouldResolveIP() bool {
|
||||
return !g.noResolveIP
|
||||
}
|
||||
|
||||
func (g *GEOIP) GetCountry() string {
|
||||
return g.country
|
||||
}
|
||||
|
||||
func (g *GEOIP) GetIPMatcher() *router.GeoIPMatcher {
|
||||
return g.geoIPMatcher
|
||||
}
|
||||
|
||||
func (g *GEOIP) GetRecodeSize() int {
|
||||
return g.recodeSize
|
||||
}
|
||||
|
||||
func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) {
|
||||
if !C.GeodataMode {
|
||||
geoip := &GEOIP{
|
||||
Base: &Base{},
|
||||
country: country,
|
||||
adapter: adapter,
|
||||
noResolveIP: noResolveIP,
|
||||
}
|
||||
return geoip, nil
|
||||
}
|
||||
|
||||
geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[GeoIP] %s", err.Error())
|
||||
}
|
||||
|
||||
log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size)
|
||||
geoip := &GEOIP{
|
||||
Base: &Base{},
|
||||
country: country,
|
||||
adapter: adapter,
|
||||
noResolveIP: noResolveIP,
|
||||
geoIPMatcher: geoIPMatcher,
|
||||
recodeSize: size,
|
||||
}
|
||||
return geoip, nil
|
||||
}
|
||||
|
||||
var _ C.Rule = (*GEOIP)(nil)
|
81
rules/common/geosite.go
Normal file
81
rules/common/geosite.go
Normal file
@ -0,0 +1,81 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/component/geodata"
|
||||
_ "github.com/Dreamacro/clash/component/geodata/memconservative"
|
||||
"github.com/Dreamacro/clash/component/geodata/router"
|
||||
_ "github.com/Dreamacro/clash/component/geodata/standard"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
//go:linkname initGeoSite github.com/Dreamacro/clash/config.initGeoSite
|
||||
func initGeoSite() error
|
||||
|
||||
type GEOSITE struct {
|
||||
*Base
|
||||
country string
|
||||
adapter string
|
||||
matcher *router.DomainMatcher
|
||||
recodeSize int
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) RuleType() C.RuleType {
|
||||
return C.GEOSITE
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) Match(metadata *C.Metadata) bool {
|
||||
if metadata.AddrType != C.AtypDomainName {
|
||||
return false
|
||||
}
|
||||
|
||||
domain := metadata.Host
|
||||
return gs.matcher.ApplyDomain(domain)
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) Adapter() string {
|
||||
return gs.adapter
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) Payload() string {
|
||||
return gs.country
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) GetDomainMatcher() *router.DomainMatcher {
|
||||
return gs.matcher
|
||||
}
|
||||
|
||||
func (gs *GEOSITE) GetRecodeSize() int {
|
||||
return gs.recodeSize
|
||||
}
|
||||
|
||||
func NewGEOSITE(country string, adapter string) (*GEOSITE, error) {
|
||||
if !initFlag {
|
||||
if err := initGeoSite(); err != nil {
|
||||
log.Errorln("can't initial GeoSite: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
initFlag = true
|
||||
}
|
||||
|
||||
matcher, size, err := geodata.LoadGeoSiteMatcher(country)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load GeoSite data error, %s", err.Error())
|
||||
}
|
||||
|
||||
log.Infoln("Start initial GeoSite rule %s => %s, records: %d", country, adapter, size)
|
||||
|
||||
geoSite := &GEOSITE{
|
||||
Base: &Base{},
|
||||
country: country,
|
||||
adapter: adapter,
|
||||
matcher: matcher,
|
||||
recodeSize: size,
|
||||
}
|
||||
|
||||
return geoSite, nil
|
||||
}
|
||||
|
||||
var _ C.Rule = (*GEOSITE)(nil)
|
74
rules/common/in_type.go
Normal file
74
rules/common/in_type.go
Normal file
@ -0,0 +1,74 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type InType struct {
|
||||
*Base
|
||||
types []C.Type
|
||||
adapter string
|
||||
payload string
|
||||
}
|
||||
|
||||
func (u *InType) Match(metadata *C.Metadata) bool {
|
||||
for _, tp := range u.types {
|
||||
if metadata.Type == tp {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *InType) RuleType() C.RuleType {
|
||||
return C.INTYPE
|
||||
}
|
||||
|
||||
func (u *InType) Adapter() string {
|
||||
return u.adapter
|
||||
}
|
||||
|
||||
func (u *InType) Payload() string {
|
||||
return u.payload
|
||||
}
|
||||
|
||||
func NewInType(iTypes, adapter string) (*InType, error) {
|
||||
types := strings.Split(iTypes, "/")
|
||||
if len(types) == 0 {
|
||||
return nil, fmt.Errorf("in type could be empty")
|
||||
}
|
||||
|
||||
tps, err := parseInTypes(types)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &InType{
|
||||
Base: &Base{},
|
||||
types: tps,
|
||||
adapter: adapter,
|
||||
payload: strings.ToUpper(iTypes),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseInTypes(tps []string) (res []C.Type, err error) {
|
||||
for _, tp := range tps {
|
||||
utp := strings.ToUpper(tp)
|
||||
var r *C.Type
|
||||
if utp == "SOCKS" {
|
||||
r, _ = C.ParseType("SOCKS4")
|
||||
res = append(res, *r)
|
||||
r, _ = C.ParseType("SOCKS5")
|
||||
res = append(res, *r)
|
||||
} else {
|
||||
r, err = C.ParseType(utp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res = append(res, *r)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
77
rules/common/ipcidr.go
Normal file
77
rules/common/ipcidr.go
Normal file
@ -0,0 +1,77 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type IPCIDROption func(*IPCIDR)
|
||||
|
||||
func WithIPCIDRSourceIP(b bool) IPCIDROption {
|
||||
return func(i *IPCIDR) {
|
||||
i.isSourceIP = b
|
||||
}
|
||||
}
|
||||
|
||||
func WithIPCIDRNoResolve(noResolve bool) IPCIDROption {
|
||||
return func(i *IPCIDR) {
|
||||
i.noResolveIP = noResolve
|
||||
}
|
||||
}
|
||||
|
||||
type IPCIDR struct {
|
||||
*Base
|
||||
ipnet *netip.Prefix
|
||||
adapter string
|
||||
isSourceIP bool
|
||||
noResolveIP bool
|
||||
}
|
||||
|
||||
func (i *IPCIDR) RuleType() C.RuleType {
|
||||
if i.isSourceIP {
|
||||
return C.SrcIPCIDR
|
||||
}
|
||||
return C.IPCIDR
|
||||
}
|
||||
|
||||
func (i *IPCIDR) Match(metadata *C.Metadata) bool {
|
||||
ip := metadata.DstIP
|
||||
if i.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
}
|
||||
return ip.IsValid() && i.ipnet.Contains(ip)
|
||||
}
|
||||
|
||||
func (i *IPCIDR) Adapter() string {
|
||||
return i.adapter
|
||||
}
|
||||
|
||||
func (i *IPCIDR) Payload() string {
|
||||
return i.ipnet.String()
|
||||
}
|
||||
|
||||
func (i *IPCIDR) ShouldResolveIP() bool {
|
||||
return !i.noResolveIP
|
||||
}
|
||||
|
||||
func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {
|
||||
ipnet, err := netip.ParsePrefix(s)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
ipcidr := &IPCIDR{
|
||||
Base: &Base{},
|
||||
ipnet: &ipnet,
|
||||
adapter: adapter,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(ipcidr)
|
||||
}
|
||||
|
||||
return ipcidr, nil
|
||||
}
|
||||
|
||||
var _ C.Rule = (*IPCIDR)(nil)
|
79
rules/common/ipsuffix.go
Normal file
79
rules/common/ipsuffix.go
Normal file
@ -0,0 +1,79 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type IPSuffix struct {
|
||||
*Base
|
||||
ipBytes []byte
|
||||
bits int
|
||||
payload string
|
||||
adapter string
|
||||
isSourceIP bool
|
||||
noResolveIP bool
|
||||
}
|
||||
|
||||
func (is *IPSuffix) RuleType() C.RuleType {
|
||||
if is.isSourceIP {
|
||||
return C.SrcIPSuffix
|
||||
}
|
||||
return C.IPSuffix
|
||||
}
|
||||
|
||||
func (is *IPSuffix) Match(metadata *C.Metadata) bool {
|
||||
ip := metadata.DstIP
|
||||
if is.isSourceIP {
|
||||
ip = metadata.SrcIP
|
||||
}
|
||||
|
||||
mIPBytes := ip.AsSlice()
|
||||
if len(is.ipBytes) != len(mIPBytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
size := len(mIPBytes)
|
||||
bits := is.bits
|
||||
|
||||
for i := bits / 8; i > 0; i-- {
|
||||
if is.ipBytes[size-i] != mIPBytes[size-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (is.ipBytes[size-bits/8-1] << (8 - bits%8)) != (mIPBytes[size-bits/8-1] << (8 - bits%8)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (is *IPSuffix) Adapter() string {
|
||||
return is.adapter
|
||||
}
|
||||
|
||||
func (is *IPSuffix) Payload() string {
|
||||
return is.payload
|
||||
}
|
||||
|
||||
func (is *IPSuffix) ShouldResolveIP() bool {
|
||||
return !is.noResolveIP
|
||||
}
|
||||
|
||||
func NewIPSuffix(payload, adapter string, isSrc, noResolveIP bool) (*IPSuffix, error) {
|
||||
ipnet, err := netip.ParsePrefix(payload)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
return &IPSuffix{
|
||||
Base: &Base{},
|
||||
payload: payload,
|
||||
ipBytes: ipnet.Addr().AsSlice(),
|
||||
bits: ipnet.Bits(),
|
||||
adapter: adapter,
|
||||
isSourceIP: isSrc,
|
||||
noResolveIP: noResolveIP,
|
||||
}, nil
|
||||
}
|
49
rules/common/network_type.go
Normal file
49
rules/common/network_type.go
Normal file
@ -0,0 +1,49 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type NetworkType struct {
|
||||
*Base
|
||||
network C.NetWork
|
||||
adapter string
|
||||
}
|
||||
|
||||
func NewNetworkType(network, adapter string) (*NetworkType, error) {
|
||||
ntType := NetworkType{
|
||||
Base: &Base{},
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
103
rules/common/port.go
Normal file
103
rules/common/port.go
Normal file
@ -0,0 +1,103 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/Dreamacro/clash/common/utils"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Port struct {
|
||||
*Base
|
||||
adapter string
|
||||
port string
|
||||
isSource bool
|
||||
portList []utils.Range[uint16]
|
||||
}
|
||||
|
||||
func (p *Port) RuleType() C.RuleType {
|
||||
if p.isSource {
|
||||
return C.SrcPort
|
||||
}
|
||||
return C.DstPort
|
||||
}
|
||||
|
||||
func (p *Port) Match(metadata *C.Metadata) bool {
|
||||
if p.isSource {
|
||||
return p.matchPortReal(metadata.SrcPort)
|
||||
}
|
||||
return p.matchPortReal(metadata.DstPort)
|
||||
}
|
||||
|
||||
func (p *Port) Adapter() string {
|
||||
return p.adapter
|
||||
}
|
||||
|
||||
func (p *Port) Payload() string {
|
||||
return p.port
|
||||
}
|
||||
|
||||
func (p *Port) matchPortReal(portRef string) bool {
|
||||
port, _ := strconv.Atoi(portRef)
|
||||
|
||||
for _, pr := range p.portList {
|
||||
if pr.Contains(uint16(port)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func NewPort(port string, adapter string, isSource bool) (*Port, error) {
|
||||
ports := strings.Split(port, "/")
|
||||
if len(ports) > 28 {
|
||||
return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error())
|
||||
}
|
||||
|
||||
var portRange []utils.Range[uint16]
|
||||
for _, p := range ports {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
subPorts := strings.Split(p, "-")
|
||||
subPortsLen := len(subPorts)
|
||||
if subPortsLen > 2 {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
portStart, err := strconv.ParseUint(strings.Trim(subPorts[0], "[ ]"), 10, 16)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
switch subPortsLen {
|
||||
case 1:
|
||||
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portStart)))
|
||||
case 2:
|
||||
portEnd, err := strconv.ParseUint(strings.Trim(subPorts[1], "[ ]"), 10, 16)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
portRange = append(portRange, *utils.NewRange(uint16(portStart), uint16(portEnd)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(portRange) == 0 {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
return &Port{
|
||||
Base: &Base{},
|
||||
adapter: adapter,
|
||||
port: port,
|
||||
isSource: isSource,
|
||||
portList: portRange,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ C.Rule = (*Port)(nil)
|
42
rules/common/process.go
Normal file
42
rules/common/process.go
Normal file
@ -0,0 +1,42 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
*Base
|
||||
adapter string
|
||||
process string
|
||||
nameOnly bool
|
||||
}
|
||||
|
||||
func (ps *Process) RuleType() C.RuleType {
|
||||
return C.Process
|
||||
}
|
||||
|
||||
func (ps *Process) Match(metadata *C.Metadata) bool {
|
||||
if ps.nameOnly {
|
||||
return strings.EqualFold(metadata.Process, ps.process)
|
||||
}
|
||||
return strings.EqualFold(metadata.ProcessPath, ps.process)
|
||||
}
|
||||
|
||||
func (ps *Process) Adapter() string {
|
||||
return ps.adapter
|
||||
}
|
||||
|
||||
func (ps *Process) Payload() string {
|
||||
return ps.process
|
||||
}
|
||||
|
||||
func NewProcess(process string, adapter string, nameOnly bool) (*Process, error) {
|
||||
return &Process{
|
||||
Base: &Base{},
|
||||
adapter: adapter,
|
||||
process: process,
|
||||
nameOnly: nameOnly,
|
||||
}, nil
|
||||
}
|
103
rules/common/uid.go
Normal file
103
rules/common/uid.go
Normal file
@ -0,0 +1,103 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Dreamacro/clash/common/utils"
|
||||
"github.com/Dreamacro/clash/component/process"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Uid struct {
|
||||
*Base
|
||||
uids []utils.Range[int32]
|
||||
oUid string
|
||||
adapter string
|
||||
}
|
||||
|
||||
func NewUid(oUid, adapter string) (*Uid, error) {
|
||||
//if len(_uids) > 28 {
|
||||
// return nil, fmt.Errorf("%s, too many uid to use, maximum support 28 uid", errPayload.Error())
|
||||
//}
|
||||
if !(runtime.GOOS == "linux" || runtime.GOOS == "android") {
|
||||
return nil, fmt.Errorf("uid rule not support this platform")
|
||||
}
|
||||
|
||||
var uidRange []utils.Range[int32]
|
||||
for _, u := range strings.Split(oUid, "/") {
|
||||
if u == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
subUids := strings.Split(u, "-")
|
||||
subUidsLen := len(subUids)
|
||||
if subUidsLen > 2 {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
uidStart, err := strconv.ParseUint(strings.Trim(subUids[0], "[ ]"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
switch subUidsLen {
|
||||
case 1:
|
||||
uidRange = append(uidRange, *utils.NewRange(int32(uidStart), int32(uidStart)))
|
||||
case 2:
|
||||
uidEnd, err := strconv.ParseUint(strings.Trim(subUids[1], "[ ]"), 10, 32)
|
||||
if err != nil {
|
||||
return nil, errPayload
|
||||
}
|
||||
|
||||
uidRange = append(uidRange, *utils.NewRange(int32(uidStart), int32(uidEnd)))
|
||||
}
|
||||
}
|
||||
|
||||
if len(uidRange) == 0 {
|
||||
return nil, errPayload
|
||||
}
|
||||
return &Uid{
|
||||
Base: &Base{},
|
||||
adapter: adapter,
|
||||
oUid: oUid,
|
||||
uids: uidRange,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *Uid) RuleType() C.RuleType {
|
||||
return C.Uid
|
||||
}
|
||||
|
||||
func (u *Uid) Match(metadata *C.Metadata) bool {
|
||||
srcPort, err := strconv.Atoi(metadata.SrcPort)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
var uid int32
|
||||
if metadata.Uid != nil {
|
||||
uid = *metadata.Uid
|
||||
} else if uid, err = process.FindUid(metadata.NetWork.String(), metadata.SrcIP, srcPort); err == nil {
|
||||
metadata.Uid = &uid
|
||||
} else {
|
||||
log.Warnln("[UID] could not get uid from %s", metadata.String())
|
||||
return false
|
||||
}
|
||||
|
||||
for _, _uid := range u.uids {
|
||||
if _uid.Contains(uid) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *Uid) Adapter() string {
|
||||
return u.adapter
|
||||
}
|
||||
|
||||
func (u *Uid) Payload() string {
|
||||
return u.oUid
|
||||
}
|
Reference in New Issue
Block a user