Chore: move find process name to a single part
This commit is contained in:
parent
13efc48023
commit
e063713174
10 changed files with 171 additions and 341 deletions
21
component/process/process.go
Normal file
21
component/process/process.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package process
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidNetwork = errors.New("invalid network")
|
||||||
|
ErrPlatformNotSupport = errors.New("not support on this platform")
|
||||||
|
ErrNotFound = errors.New("process not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCP = "tcp"
|
||||||
|
UDP = "udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FindProcessName(network string, srcIP net.IP, srcPort int) (string, error) {
|
||||||
|
return findProcessName(network, srcIP, srcPort)
|
||||||
|
}
|
|
@ -1,109 +1,26 @@
|
||||||
package rules
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// store process name for when dealing with multiple PROCESS-NAME rules
|
|
||||||
var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
|
||||||
|
|
||||||
type Process struct {
|
|
||||||
adapter string
|
|
||||||
process string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *Process) RuleType() C.RuleType {
|
|
||||||
return C.Process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *Process) Match(metadata *C.Metadata) bool {
|
|
||||||
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
|
||||||
cached, hit := processCache.Get(key)
|
|
||||||
if !hit {
|
|
||||||
name, err := getExecPathFromAddress(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugln("[%s] getExecPathFromAddress error: %s", C.Process.String(), err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
processCache.Set(key, name)
|
|
||||||
|
|
||||||
cached = name
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.EqualFold(cached.(string), ps.process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Adapter() string {
|
|
||||||
return p.adapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Payload() string {
|
|
||||||
return p.process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) ShouldResolveIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (*Process, error) {
|
|
||||||
return &Process{
|
|
||||||
adapter: adapter,
|
|
||||||
process: process,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
procpidpathinfo = 0xb
|
procpidpathinfo = 0xb
|
||||||
procpidpathinfosize = 1024
|
procpidpathinfosize = 1024
|
||||||
proccallnumpidinfo = 0x2
|
proccallnumpidinfo = 0x2
|
||||||
)
|
)
|
||||||
|
|
||||||
func getExecPathFromPID(pid uint32) (string, error) {
|
func findProcessName(network string, ip net.IP, port int) (string, error) {
|
||||||
buf := make([]byte, procpidpathinfosize)
|
|
||||||
_, _, errno := syscall.Syscall6(
|
|
||||||
syscall.SYS_PROC_INFO,
|
|
||||||
proccallnumpidinfo,
|
|
||||||
uintptr(pid),
|
|
||||||
procpidpathinfo,
|
|
||||||
0,
|
|
||||||
uintptr(unsafe.Pointer(&buf[0])),
|
|
||||||
procpidpathinfosize)
|
|
||||||
if errno != 0 {
|
|
||||||
return "", errno
|
|
||||||
}
|
|
||||||
firstZero := bytes.IndexByte(buf, 0)
|
|
||||||
if firstZero <= 0 {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return filepath.Base(string(buf[:firstZero])), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
|
|
||||||
ip := metadata.SrcIP
|
|
||||||
port, err := strconv.Atoi(metadata.SrcPort)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var spath string
|
var spath string
|
||||||
switch metadata.NetWork {
|
switch network {
|
||||||
case C.TCP:
|
case TCP:
|
||||||
spath = "net.inet.tcp.pcblist_n"
|
spath = "net.inet.tcp.pcblist_n"
|
||||||
case C.UDP:
|
case UDP:
|
||||||
spath = "net.inet.udp.pcblist_n"
|
spath = "net.inet.udp.pcblist_n"
|
||||||
default:
|
default:
|
||||||
return "", ErrInvalidNetwork
|
return "", ErrInvalidNetwork
|
||||||
|
@ -123,7 +40,7 @@ func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
|
||||||
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
|
||||||
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
|
||||||
itemSize := 384
|
itemSize := 384
|
||||||
if metadata.NetWork == C.TCP {
|
if network == TCP {
|
||||||
// rup8(sizeof(xtcpcb_n))
|
// rup8(sizeof(xtcpcb_n))
|
||||||
itemSize += 208
|
itemSize += 208
|
||||||
}
|
}
|
||||||
|
@ -161,7 +78,28 @@ func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
|
||||||
return getExecPathFromPID(pid)
|
return getExecPathFromPID(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("process not found")
|
return "", ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExecPathFromPID(pid uint32) (string, error) {
|
||||||
|
buf := make([]byte, procpidpathinfosize)
|
||||||
|
_, _, errno := syscall.Syscall6(
|
||||||
|
syscall.SYS_PROC_INFO,
|
||||||
|
proccallnumpidinfo,
|
||||||
|
uintptr(pid),
|
||||||
|
procpidpathinfo,
|
||||||
|
0,
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
|
procpidpathinfosize)
|
||||||
|
if errno != 0 {
|
||||||
|
return "", errno
|
||||||
|
}
|
||||||
|
firstZero := bytes.IndexByte(buf, 0)
|
||||||
|
if firstZero <= 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Base(string(buf[:firstZero])), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readNativeUint32(b []byte) uint32 {
|
func readNativeUint32(b []byte) uint32 {
|
|
@ -1,8 +1,7 @@
|
||||||
package rules
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -12,78 +11,48 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// store process name for when dealing with multiple PROCESS-NAME rules
|
// store process name for when dealing with multiple PROCESS-NAME rules
|
||||||
var (
|
var (
|
||||||
processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
|
||||||
errNotFound = errors.New("process not found")
|
|
||||||
matchMeta = func(p *Process, m *C.Metadata) bool { return false }
|
|
||||||
|
|
||||||
defaultSearcher *searcher
|
defaultSearcher *searcher
|
||||||
|
|
||||||
once sync.Once
|
once sync.Once
|
||||||
)
|
)
|
||||||
|
|
||||||
type Process struct {
|
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
||||||
adapter string
|
|
||||||
process string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *Process) RuleType() C.RuleType {
|
|
||||||
return C.Process
|
|
||||||
}
|
|
||||||
|
|
||||||
func match(ps *Process, metadata *C.Metadata) bool {
|
|
||||||
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
|
||||||
cached, hit := processCache.Get(key)
|
|
||||||
if !hit {
|
|
||||||
name, err := getExecPathFromAddress(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugln("[%s] getExecPathFromAddress error: %s", C.Process.String(), err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
processCache.Set(key, name)
|
|
||||||
|
|
||||||
cached = name
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.EqualFold(cached.(string), ps.process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ps *Process) Match(metadata *C.Metadata) bool {
|
|
||||||
return matchMeta(ps, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Adapter() string {
|
|
||||||
return p.adapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Payload() string {
|
|
||||||
return p.process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) ShouldResolveIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (*Process, error) {
|
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
err := initSearcher()
|
if err := initSearcher(); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
|
||||||
log.Warnln("All PROCESS-NAME rules will be skipped")
|
log.Warnln("All PROCESS-NAME rules will be skipped")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
matchMeta = match
|
|
||||||
})
|
})
|
||||||
return &Process{
|
|
||||||
adapter: adapter,
|
var spath string
|
||||||
process: process,
|
isTCP := network == TCP
|
||||||
}, nil
|
switch network {
|
||||||
|
case TCP:
|
||||||
|
spath = "net.inet.tcp.pcblist"
|
||||||
|
case UDP:
|
||||||
|
spath = "net.inet.udp.pcblist"
|
||||||
|
default:
|
||||||
|
return "", ErrInvalidNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := syscall.Sysctl(spath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := []byte(value)
|
||||||
|
pid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getExecPathFromPID(pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecPathFromPID(pid uint32) (string, error) {
|
func getExecPathFromPID(pid uint32) (string, error) {
|
||||||
|
@ -107,41 +76,6 @@ func getExecPathFromPID(pid uint32) (string, error) {
|
||||||
return filepath.Base(string(buf[:size-1])), nil
|
return filepath.Base(string(buf[:size-1])), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExecPathFromAddress(metadata *C.Metadata) (string, error) {
|
|
||||||
ip := metadata.SrcIP
|
|
||||||
port, err := strconv.Atoi(metadata.SrcPort)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var spath string
|
|
||||||
var isTCP bool
|
|
||||||
switch metadata.NetWork {
|
|
||||||
case C.TCP:
|
|
||||||
spath = "net.inet.tcp.pcblist"
|
|
||||||
isTCP = true
|
|
||||||
case C.UDP:
|
|
||||||
spath = "net.inet.udp.pcblist"
|
|
||||||
isTCP = false
|
|
||||||
default:
|
|
||||||
return "", ErrInvalidNetwork
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err := syscall.Sysctl(spath)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := []byte(value)
|
|
||||||
|
|
||||||
pid, err := defaultSearcher.Search(buf, ip, uint16(port), isTCP)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return getExecPathFromPID(pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func readNativeUint32(b []byte) uint32 {
|
func readNativeUint32(b []byte) uint32 {
|
||||||
return *(*uint32)(unsafe.Pointer(&b[0]))
|
return *(*uint32)(unsafe.Pointer(&b[0]))
|
||||||
}
|
}
|
||||||
|
@ -213,7 +147,7 @@ func (s *searcher) Search(buf []byte, ip net.IP, port uint16, isTCP bool) (uint3
|
||||||
socket := binary.BigEndian.Uint64(buf[inp+s.socket : inp+s.socket+8])
|
socket := binary.BigEndian.Uint64(buf[inp+s.socket : inp+s.socket+8])
|
||||||
return s.searchSocketPid(socket)
|
return s.searchSocketPid(socket)
|
||||||
}
|
}
|
||||||
return 0, errNotFound
|
return 0, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *searcher) searchSocketPid(socket uint64) (uint32, error) {
|
func (s *searcher) searchSocketPid(socket uint64) (uint32, error) {
|
||||||
|
@ -235,7 +169,7 @@ func (s *searcher) searchSocketPid(socket uint64) (uint32, error) {
|
||||||
return pid, nil
|
return pid, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, errNotFound
|
return 0, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSearcher(major int) *searcher {
|
func newSearcher(major int) *searcher {
|
|
@ -1,4 +1,4 @@
|
||||||
package rules
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -9,15 +9,10 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
|
||||||
"github.com/Dreamacro/clash/common/pool"
|
"github.com/Dreamacro/clash/common/pool"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
|
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
|
||||||
|
@ -30,7 +25,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SocketResolver func(metadata *C.Metadata) (inode, uid int, err error)
|
type SocketResolver func(network string, ip net.IP, srcPort int) (inode, uid int, err error)
|
||||||
type ProcessNameResolver func(inode, uid int) (name string, err error)
|
type ProcessNameResolver func(inode, uid int) (name string, err error)
|
||||||
|
|
||||||
// export for android
|
// export for android
|
||||||
|
@ -39,51 +34,6 @@ var (
|
||||||
DefaultProcessNameResolver ProcessNameResolver = resolveProcessNameByProcSearch
|
DefaultProcessNameResolver ProcessNameResolver = resolveProcessNameByProcSearch
|
||||||
)
|
)
|
||||||
|
|
||||||
type Process struct {
|
|
||||||
adapter string
|
|
||||||
process string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) RuleType() C.RuleType {
|
|
||||||
return C.Process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Match(metadata *C.Metadata) bool {
|
|
||||||
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
|
||||||
cached, hit := processCache.Get(key)
|
|
||||||
if !hit {
|
|
||||||
processName, err := resolveProcessName(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugln("[%s] Resolve process of %s failure: %s", C.Process.String(), key, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
processCache.Set(key, processName)
|
|
||||||
|
|
||||||
cached = processName
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.EqualFold(cached.(string), p.process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Adapter() string {
|
|
||||||
return p.adapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Payload() string {
|
|
||||||
return p.process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) ShouldResolveIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (*Process, error) {
|
|
||||||
return &Process{
|
|
||||||
adapter: adapter,
|
|
||||||
process: process,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48
|
sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48
|
||||||
socketDiagByFamily = 20
|
socketDiagByFamily = 20
|
||||||
|
@ -92,10 +42,8 @@ const (
|
||||||
|
|
||||||
var nativeEndian binary.ByteOrder = binary.LittleEndian
|
var nativeEndian binary.ByteOrder = binary.LittleEndian
|
||||||
|
|
||||||
var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
||||||
|
inode, uid, err := DefaultSocketResolver(network, ip, srcPort)
|
||||||
func resolveProcessName(metadata *C.Metadata) (string, error) {
|
|
||||||
inode, uid, err := DefaultSocketResolver(metadata)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -103,31 +51,26 @@ func resolveProcessName(metadata *C.Metadata) (string, error) {
|
||||||
return DefaultProcessNameResolver(inode, uid)
|
return DefaultProcessNameResolver(inode, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveSocketByNetlink(metadata *C.Metadata) (int, int, error) {
|
func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int, int, error) {
|
||||||
var family byte
|
var family byte
|
||||||
var protocol byte
|
var protocol byte
|
||||||
|
|
||||||
switch metadata.NetWork {
|
switch network {
|
||||||
case C.TCP:
|
case TCP:
|
||||||
protocol = syscall.IPPROTO_TCP
|
protocol = syscall.IPPROTO_TCP
|
||||||
case C.UDP:
|
case UDP:
|
||||||
protocol = syscall.IPPROTO_UDP
|
protocol = syscall.IPPROTO_UDP
|
||||||
default:
|
default:
|
||||||
return 0, 0, ErrInvalidNetwork
|
return 0, 0, ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
if metadata.SrcIP.To4() != nil {
|
if ip.To4() != nil {
|
||||||
family = syscall.AF_INET
|
family = syscall.AF_INET
|
||||||
} else {
|
} else {
|
||||||
family = syscall.AF_INET6
|
family = syscall.AF_INET6
|
||||||
}
|
}
|
||||||
|
|
||||||
srcPort, err := strconv.Atoi(metadata.SrcPort)
|
req := packSocketDiagRequest(family, protocol, ip, uint16(srcPort))
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req := packSocketDiagRequest(family, protocol, metadata.SrcIP, uint16(srcPort))
|
|
||||||
|
|
||||||
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
|
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
|
||||||
if err != nil {
|
if err != nil {
|
10
component/process/process_other.go
Normal file
10
component/process/process_other.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// +build !darwin,!linux,!windows
|
||||||
|
// +build !freebsd !amd64
|
||||||
|
|
||||||
|
package process
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
||||||
|
return "", ErrPlatformNotSupport
|
||||||
|
}
|
|
@ -1,18 +1,13 @@
|
||||||
package rules
|
package process
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/cache"
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
"github.com/Dreamacro/clash/log"
|
"github.com/Dreamacro/clash/log"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
|
@ -27,10 +22,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
|
||||||
errNotFound = errors.New("process not found")
|
|
||||||
matchMeta = func(p *Process, m *C.Metadata) bool { return false }
|
|
||||||
|
|
||||||
getExTcpTable uintptr
|
getExTcpTable uintptr
|
||||||
getExUdpTable uintptr
|
getExUdpTable uintptr
|
||||||
queryProcName uintptr
|
queryProcName uintptr
|
||||||
|
@ -67,47 +58,7 @@ func initWin32API() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Process struct {
|
func findProcessName(network string, ip net.IP, srcPort int) (string, error) {
|
||||||
adapter string
|
|
||||||
process string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) RuleType() C.RuleType {
|
|
||||||
return C.Process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Adapter() string {
|
|
||||||
return p.adapter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Payload() string {
|
|
||||||
return p.process
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) ShouldResolveIP() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func match(p *Process, metadata *C.Metadata) bool {
|
|
||||||
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
|
||||||
cached, hit := processCache.Get(key)
|
|
||||||
if !hit {
|
|
||||||
processName, err := resolveProcessName(metadata)
|
|
||||||
if err != nil {
|
|
||||||
log.Debugln("[%s] Resolve process of %s failed: %s", C.Process.String(), key, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
processCache.Set(key, processName)
|
|
||||||
cached = processName
|
|
||||||
}
|
|
||||||
return strings.EqualFold(cached.(string), p.process)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Process) Match(metadata *C.Metadata) bool {
|
|
||||||
return matchMeta(p, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (*Process, error) {
|
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
err := initWin32API()
|
err := initWin32API()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -115,16 +66,7 @@ func NewProcess(process string, adapter string) (*Process, error) {
|
||||||
log.Warnln("All PROCESS-NAMES rules will be skiped")
|
log.Warnln("All PROCESS-NAMES rules will be skiped")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
matchMeta = match
|
|
||||||
})
|
})
|
||||||
return &Process{
|
|
||||||
adapter: adapter,
|
|
||||||
process: process,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveProcessName(metadata *C.Metadata) (string, error) {
|
|
||||||
ip := metadata.SrcIP
|
|
||||||
family := windows.AF_INET
|
family := windows.AF_INET
|
||||||
if ip.To4() == nil {
|
if ip.To4() == nil {
|
||||||
family = windows.AF_INET6
|
family = windows.AF_INET6
|
||||||
|
@ -132,28 +74,23 @@ func resolveProcessName(metadata *C.Metadata) (string, error) {
|
||||||
|
|
||||||
var class int
|
var class int
|
||||||
var fn uintptr
|
var fn uintptr
|
||||||
switch metadata.NetWork {
|
switch network {
|
||||||
case C.TCP:
|
case TCP:
|
||||||
fn = getExTcpTable
|
fn = getExTcpTable
|
||||||
class = tcpTablePidConn
|
class = tcpTablePidConn
|
||||||
case C.UDP:
|
case UDP:
|
||||||
fn = getExUdpTable
|
fn = getExUdpTable
|
||||||
class = udpTablePid
|
class = udpTablePid
|
||||||
default:
|
default:
|
||||||
return "", ErrInvalidNetwork
|
return "", ErrInvalidNetwork
|
||||||
}
|
}
|
||||||
|
|
||||||
srcPort, err := strconv.Atoi(metadata.SrcPort)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := getTransportTable(fn, family, class)
|
buf, err := getTransportTable(fn, family, class)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s := newSearcher(family == windows.AF_INET, metadata.NetWork == C.TCP)
|
s := newSearcher(family == windows.AF_INET, network == TCP)
|
||||||
|
|
||||||
pid, err := s.Search(buf, ip, uint16(srcPort))
|
pid, err := s.Search(buf, ip, uint16(srcPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -203,7 +140,7 @@ func (s *searcher) Search(b []byte, ip net.IP, port uint16) (uint32, error) {
|
||||||
pid := readNativeUint32(row[s.pid : s.pid+4])
|
pid := readNativeUint32(row[s.pid : s.pid+4])
|
||||||
return pid, nil
|
return pid, nil
|
||||||
}
|
}
|
||||||
return 0, errNotFound
|
return 0, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSearcher(isV4, isTCP bool) *searcher {
|
func newSearcher(isV4, isTCP bool) *searcher {
|
|
@ -395,10 +395,6 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy) ([]C.Rule, error) {
|
||||||
|
|
||||||
parsed, parseErr := R.ParseRule(rule[0], payload, target, params)
|
parsed, parseErr := R.ParseRule(rule[0], payload, target, params)
|
||||||
if parseErr != nil {
|
if parseErr != nil {
|
||||||
if parseErr == R.ErrPlatformNotSupport {
|
|
||||||
log.Warnln("Rules[%d] [%s] don't support current OS, skip", idx, line)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error())
|
return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errPayload = errors.New("payload error")
|
errPayload = errors.New("payload error")
|
||||||
ErrPlatformNotSupport = errors.New("not support on this platform")
|
|
||||||
ErrInvalidNetwork = errors.New("invalid network")
|
|
||||||
|
|
||||||
noResolve = "no-resolve"
|
noResolve = "no-resolve"
|
||||||
)
|
)
|
||||||
|
|
65
rules/process.go
Normal file
65
rules/process.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/cache"
|
||||||
|
"github.com/Dreamacro/clash/component/process"
|
||||||
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var processCache = cache.NewLRUCache(cache.WithAge(2), cache.WithSize(64))
|
||||||
|
|
||||||
|
type Process struct {
|
||||||
|
adapter string
|
||||||
|
process string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *Process) RuleType() C.RuleType {
|
||||||
|
return C.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *Process) Match(metadata *C.Metadata) bool {
|
||||||
|
key := fmt.Sprintf("%s:%s:%s", metadata.NetWork.String(), metadata.SrcIP.String(), metadata.SrcPort)
|
||||||
|
cached, hit := processCache.Get(key)
|
||||||
|
if !hit {
|
||||||
|
srcPort, err := strconv.Atoi(metadata.SrcPort)
|
||||||
|
if err != nil {
|
||||||
|
processCache.Set(key, "")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := process.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugln("[Rule] find process name %s error: %s", C.Process.String(), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
processCache.Set(key, name)
|
||||||
|
|
||||||
|
cached = name
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.EqualFold(cached.(string), ps.process)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Process) Adapter() string {
|
||||||
|
return p.adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Process) Payload() string {
|
||||||
|
return p.process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Process) ShouldResolveIP() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProcess(process string, adapter string) (*Process, error) {
|
||||||
|
return &Process{
|
||||||
|
adapter: adapter,
|
||||||
|
process: process,
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
// +build !darwin,!linux,!windows
|
|
||||||
// +build !freebsd !amd64
|
|
||||||
|
|
||||||
package rules
|
|
||||||
|
|
||||||
import (
|
|
||||||
C "github.com/Dreamacro/clash/constant"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewProcess(process string, adapter string) (C.Rule, error) {
|
|
||||||
return nil, ErrPlatformNotSupport
|
|
||||||
}
|
|
Loading…
Reference in a new issue