From b866f06414585c2c73ce2aef7a2f7b3a0dda2abe Mon Sep 17 00:00:00 2001 From: Kr328 Date: Sat, 12 Mar 2022 19:07:53 +0800 Subject: [PATCH] Chore: move find connection process to tunnel (#2016) --- component/process/process_darwin.go | 3 +- component/process/process_freebsd_amd64.go | 3 +- component/process/process_linux.go | 21 ++-------- component/process/process_windows.go | 3 +- constant/metadata.go | 19 ++++----- constant/rule.go | 4 ++ rule/domain.go | 4 ++ rule/domain_keyword.go | 4 ++ rule/domain_suffix.go | 4 ++ rule/final.go | 4 ++ rule/geoip.go | 4 ++ rule/ipcidr.go | 4 ++ rule/parser.go | 4 +- rule/port.go | 4 ++ rule/process.go | 45 ++++++++-------------- tunnel/tunnel.go | 18 +++++++++ 16 files changed, 84 insertions(+), 64 deletions(-) diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index 7e4baf74..7870b227 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -3,7 +3,6 @@ package process import ( "encoding/binary" "net" - "path/filepath" "syscall" "unsafe" @@ -96,7 +95,7 @@ func getExecPathFromPID(pid uint32) (string, error) { return "", errno } - return filepath.Base(unix.ByteSliceToString(buf)), nil + return unix.ByteSliceToString(buf), nil } func readNativeUint32(b []byte) uint32 { diff --git a/component/process/process_freebsd_amd64.go b/component/process/process_freebsd_amd64.go index bf84ce5b..f3e64646 100644 --- a/component/process/process_freebsd_amd64.go +++ b/component/process/process_freebsd_amd64.go @@ -4,7 +4,6 @@ import ( "encoding/binary" "fmt" "net" - "path/filepath" "strconv" "strings" "sync" @@ -77,7 +76,7 @@ func getExecPathFromPID(pid uint32) (string, error) { return "", errno } - return filepath.Base(string(buf[:size-1])), nil + return string(buf[:size-1]), nil } func readNativeUint32(b []byte) uint32 { diff --git a/component/process/process_linux.go b/component/process/process_linux.go index c718dc2f..d70a2922 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -7,7 +7,6 @@ import ( "net" "os" "path" - "path/filepath" "strings" "syscall" "unicode" @@ -68,9 +67,8 @@ func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int3 } defer syscall.Close(socket) - syscall.SetNonblock(socket, true) - syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 50}) - syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 50}) + syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100}) + syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100}) if err := syscall.Connect(socket, &syscall.SockaddrNetlink{ Family: syscall.AF_NETLINK, @@ -198,12 +196,7 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { } if bytes.Equal(buffer[:n], socket) { - cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) - if err != nil { - return "", err - } - - return splitCmdline(cmdline), nil + return os.Readlink(path.Join(processPath, "exe")) } } } @@ -211,14 +204,6 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) } -func splitCmdline(cmdline []byte) string { - idx := bytes.IndexFunc(cmdline, func(r rune) bool { - return unicode.IsControl(r) || unicode.IsSpace(r) - }) - - return filepath.Base(string(cmdline[:idx])) -} - func isPid(s string) bool { return strings.IndexFunc(s, func(r rune) bool { return !unicode.IsDigit(r) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index 834bc824..e2fb96ca 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -3,7 +3,6 @@ package process import ( "fmt" "net" - "path/filepath" "sync" "syscall" "unsafe" @@ -220,5 +219,5 @@ func getExecPathFromPID(pid uint32) (string, error) { if r1 == 0 { return "", err } - return filepath.Base(syscall.UTF16ToString(buf[:size])), nil + return syscall.UTF16ToString(buf[:size]), nil } diff --git a/constant/metadata.go b/constant/metadata.go index a911b5e8..6789c913 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -63,15 +63,16 @@ func (t Type) MarshalJSON() ([]byte, error) { // Metadata is used to store connection address type Metadata struct { - NetWork NetWork `json:"network"` - Type Type `json:"type"` - SrcIP net.IP `json:"sourceIP"` - DstIP net.IP `json:"destinationIP"` - SrcPort string `json:"sourcePort"` - DstPort string `json:"destinationPort"` - AddrType int `json:"-"` - Host string `json:"host"` - DNSMode DNSMode `json:"dnsMode"` + NetWork NetWork `json:"network"` + Type Type `json:"type"` + SrcIP net.IP `json:"sourceIP"` + DstIP net.IP `json:"destinationIP"` + SrcPort string `json:"sourcePort"` + DstPort string `json:"destinationPort"` + AddrType int `json:"-"` + Host string `json:"host"` + DNSMode DNSMode `json:"dnsMode"` + ProcessPath string `json:"processPath"` } func (m *Metadata) RemoteAddress() string { diff --git a/constant/rule.go b/constant/rule.go index d7c43416..9e1908f2 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -11,6 +11,7 @@ const ( SrcPort DstPort Process + ProcessPath MATCH ) @@ -36,6 +37,8 @@ func (rt RuleType) String() string { return "DstPort" case Process: return "Process" + case ProcessPath: + return "ProcessPath" case MATCH: return "Match" default: @@ -49,4 +52,5 @@ type Rule interface { Adapter() string Payload() string ShouldResolveIP() bool + ShouldFindProcess() bool } diff --git a/rule/domain.go b/rule/domain.go index f23ab18d..2b2a076b 100644 --- a/rule/domain.go +++ b/rule/domain.go @@ -34,6 +34,10 @@ func (d *Domain) ShouldResolveIP() bool { return false } +func (d *Domain) ShouldFindProcess() bool { + return false +} + func NewDomain(domain string, adapter string) *Domain { return &Domain{ domain: strings.ToLower(domain), diff --git a/rule/domain_keyword.go b/rule/domain_keyword.go index b3d6fbaa..046eafe4 100644 --- a/rule/domain_keyword.go +++ b/rule/domain_keyword.go @@ -35,6 +35,10 @@ func (dk *DomainKeyword) ShouldResolveIP() bool { return false } +func (dk *DomainKeyword) ShouldFindProcess() bool { + return false +} + func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { return &DomainKeyword{ keyword: strings.ToLower(keyword), diff --git a/rule/domain_suffix.go b/rule/domain_suffix.go index a1f9f28f..09219338 100644 --- a/rule/domain_suffix.go +++ b/rule/domain_suffix.go @@ -35,6 +35,10 @@ func (ds *DomainSuffix) ShouldResolveIP() bool { return false } +func (ds *DomainSuffix) ShouldFindProcess() bool { + return false +} + func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { return &DomainSuffix{ suffix: strings.ToLower(suffix), diff --git a/rule/final.go b/rule/final.go index 91c0b9d9..80a38f03 100644 --- a/rule/final.go +++ b/rule/final.go @@ -28,6 +28,10 @@ func (f *Match) ShouldResolveIP() bool { return false } +func (f *Match) ShouldFindProcess() bool { + return false +} + func NewMatch(adapter string) *Match { return &Match{ adapter: adapter, diff --git a/rule/geoip.go b/rule/geoip.go index 3b6fbe6b..152d4e63 100644 --- a/rule/geoip.go +++ b/rule/geoip.go @@ -42,6 +42,10 @@ func (g *GEOIP) ShouldResolveIP() bool { return !g.noResolveIP } +func (g *GEOIP) ShouldFindProcess() bool { + return false +} + func NewGEOIP(country string, adapter string, noResolveIP bool) *GEOIP { geoip := &GEOIP{ country: country, diff --git a/rule/ipcidr.go b/rule/ipcidr.go index ff3b83c4..42c0c9fb 100644 --- a/rule/ipcidr.go +++ b/rule/ipcidr.go @@ -54,6 +54,10 @@ func (i *IPCIDR) ShouldResolveIP() bool { return !i.noResolveIP } +func (i *IPCIDR) ShouldFindProcess() bool { + return false +} + func NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) { _, ipnet, err := net.ParseCIDR(s) if err != nil { diff --git a/rule/parser.go b/rule/parser.go index 6e78b8fa..212029bf 100644 --- a/rule/parser.go +++ b/rule/parser.go @@ -32,7 +32,9 @@ func ParseRule(tp, payload, target string, params []string) (C.Rule, error) { case "DST-PORT": parsed, parseErr = NewPort(payload, target, false) case "PROCESS-NAME": - parsed, parseErr = NewProcess(payload, target) + parsed, parseErr = NewProcess(payload, target, true) + case "PROCESS-PATH": + parsed, parseErr = NewProcess(payload, target, false) case "MATCH": parsed = NewMatch(target) default: diff --git a/rule/port.go b/rule/port.go index b6e8abb1..2cc7a7a2 100644 --- a/rule/port.go +++ b/rule/port.go @@ -38,6 +38,10 @@ func (p *Port) ShouldResolveIP() bool { return false } +func (p *Port) ShouldFindProcess() bool { + return false +} + func NewPort(port string, adapter string, isSource bool) (*Port, error) { _, err := strconv.ParseUint(port, 10, 16) if err != nil { diff --git a/rule/process.go b/rule/process.go index 638ec723..52f0cec2 100644 --- a/rule/process.go +++ b/rule/process.go @@ -1,21 +1,16 @@ package rules import ( - "fmt" - "strconv" + "path/filepath" "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 + adapter string + process string + nameOnly bool } func (ps *Process) RuleType() C.RuleType { @@ -23,26 +18,11 @@ func (ps *Process) RuleType() C.RuleType { } 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 + if ps.nameOnly { + return strings.EqualFold(filepath.Base(metadata.ProcessPath), ps.process) } - return strings.EqualFold(cached.(string), ps.process) + return strings.EqualFold(metadata.ProcessPath, ps.process) } func (ps *Process) Adapter() string { @@ -57,9 +37,14 @@ func (ps *Process) ShouldResolveIP() bool { return false } -func NewProcess(process string, adapter string) (*Process, error) { +func (ps *Process) ShouldFindProcess() bool { + return true +} + +func NewProcess(process string, adapter string, nameOnly bool) (*Process, error) { return &Process{ - adapter: adapter, - process: process, + adapter: adapter, + process: process, + nameOnly: nameOnly, }, nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f00a7bd0..07284880 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -5,11 +5,13 @@ import ( "fmt" "net" "runtime" + "strconv" "sync" "time" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/nat" + P "github.com/Dreamacro/clash/component/process" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -308,6 +310,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { defer configMux.RUnlock() var resolved bool + var processFound bool if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { ip := node.Data.(net.IP) @@ -327,6 +330,21 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { resolved = true } + if !processFound && rule.ShouldFindProcess() { + processFound = true + + srcPort, err := strconv.Atoi(metadata.SrcPort) + if err == nil { + path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort) + if err != nil { + log.Debugln("[Process] find process %s: %v", metadata.String(), err) + } else { + log.Debugln("[Process] %s from process %s", metadata.String(), path) + metadata.ProcessPath = path + } + } + } + if rule.Match(metadata) { adapter, ok := proxies[rule.Adapter()] if !ok {