Routing config: Replace processName with process (full-name/abs-path/abs-folder) (#5496)

About `self/` & `xray/`: https://github.com/XTLS/Xray-core/pull/5496#issuecomment-3714620380

Replaces https://github.com/XTLS/Xray-core/pull/5489
This commit is contained in:
风扇滑翔翼
2026-01-06 21:57:11 +08:00
committed by RPRX
parent d9025857fe
commit 446df149bd
9 changed files with 187 additions and 141 deletions

View File

@@ -13,13 +13,13 @@ import (
"github.com/xtls/xray-core/common/errors"
)
func FindProcess(dest Destination) (int, string, error) {
func FindProcess(dest Destination) (PID int, Name string, AbsolutePath string, err error) {
isLocal, err := IsLocal(dest.Address.IP())
if err != nil {
return 0, "", errors.New("failed to determine if address is local: ", err)
return 0, "", "", errors.New("failed to determine if address is local: ", err)
}
if !isLocal {
return 0, "", ErrNotLocal
return 0, "", "", ErrNotLocal
}
if dest.Network != Network_TCP && dest.Network != Network_UDP {
panic("Unsupported network type for process lookup.")
@@ -51,36 +51,39 @@ func FindProcess(dest Destination) (int, string, error) {
targetHexAddr, err := formatLittleEndianString(dest.Address, dest.Port)
if err != nil {
return 0, "", errors.New("failed to format address: ", err)
return 0, "", "", errors.New("failed to format address: ", err)
}
inode, err := findInodeInFile(procFile, targetHexAddr)
if err != nil {
return 0, "", errors.New("could not search in ", procFile).Base(err)
return 0, "", "", errors.New("could not search in ", procFile).Base(err)
}
if inode == "" {
return 0, "", errors.New("connection for ", dest.Address, ":", dest.Port, " not found in ", procFile)
return 0, "", "", errors.New("connection for ", dest.Address, ":", dest.Port, " not found in ", procFile)
}
pidStr, err := findPidByInode(inode)
if err != nil {
return 0, "", errors.New("could not find PID for inode ", inode, ": ", err)
return 0, "", "", errors.New("could not find PID for inode ", inode, ": ", err)
}
if pidStr == "" {
return 0, "", errors.New("no process found for inode ", inode)
return 0, "", "", errors.New("no process found for inode ", inode)
}
procName, err := getProcessName(pidStr)
absPath, err := getAbsPath(pidStr)
if err != nil {
return 0, "", fmt.Errorf("could not get process name for PID %s: %w", pidStr, err)
return 0, "", "", errors.New("could not get process name for PID ", pidStr, ":", err)
}
nameSplit := strings.Split(absPath, "/")
procName := nameSplit[len(nameSplit)-1]
pid, err := strconv.Atoi(pidStr)
if err != nil {
return 0, "", errors.New("failed to parse PID: ", err)
return 0, "", "", errors.New("failed to parse PID: ", err)
}
return pid, procName, nil
return pid, procName, absPath, nil
}
func formatLittleEndianString(addr Address, port Port) (string, error) {
@@ -167,12 +170,7 @@ func findPidByInode(inode string) (string, error) {
return "", nil
}
func getProcessName(pid string) (string, error) {
path := fmt.Sprintf("/proc/%s/comm", pid)
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
// remove trailing \n
return strings.TrimSpace(string(content)), nil
func getAbsPath(pid string) (string, error) {
path := fmt.Sprintf("/proc/%s/exe", pid)
return os.Readlink(path)
}

View File

@@ -6,6 +6,6 @@ import (
"github.com/xtls/xray-core/common/errors"
)
func FindProcess(dest Destination) (int, string, error) {
return 0, "", errors.New("process lookup is not supported on this platform")
func FindProcess(dest Destination) (int, string, string, error) {
return 0, "", "", errors.New("process lookup is not supported on this platform")
}

View File

@@ -4,6 +4,7 @@ package net
import (
"net/netip"
"path/filepath"
"strings"
"sync"
"syscall"
@@ -48,19 +49,19 @@ func initWin32API() error {
return nil
}
func FindProcess(dest Destination) (int, string, error) {
func FindProcess(dest Destination) (PID int, Name string, AbsolutePath string, err error) {
once.Do(func() {
initErr = initWin32API()
})
if initErr != nil {
return 0, "", initErr
return 0, "", "", initErr
}
isLocal, err := IsLocal(dest.Address.IP())
if err != nil {
return 0, "", errors.New("failed to determine if address is local: ", err)
return 0, "", "", errors.New("failed to determine if address is local: ", err)
}
if !isLocal {
return 0, "", ErrNotLocal
return 0, "", "", ErrNotLocal
}
if dest.Network != Network_TCP && dest.Network != Network_UDP {
panic("Unsupported network type for process lookup.")
@@ -86,7 +87,7 @@ func FindProcess(dest Destination) (int, string, error) {
addr, ok := netip.AddrFromSlice(ip)
if !ok {
return 0, "", errors.New("invalid IP address")
return 0, "", "", errors.New("invalid IP address")
}
addr = addr.Unmap()
@@ -97,19 +98,23 @@ func FindProcess(dest Destination) (int, string, error) {
buf, err := getTransportTable(fn, family, class)
if err != nil {
return 0, "", err
return 0, "", "", err
}
s := newSearcher(dest.Network, dest.Address.Family())
pid, err := s.Search(buf, addr, uint16(port))
if err != nil {
return 0, "", err
return 0, "", "", err
}
name, err := getExecPathFromPID(pid)
// drop .exe
name = strings.TrimSuffix(name, ".exe")
return int(pid), name, err
NameWithPath, err := getExecPathFromPID(pid)
NameWithPath = filepath.ToSlash(NameWithPath)
// drop .exe and path
nameSplit := strings.Split(NameWithPath, "/")
procName := nameSplit[len(nameSplit)-1]
procName = strings.TrimSuffix(procName, ".exe")
return int(pid), procName, NameWithPath, err
}
type searcher struct {
@@ -234,10 +239,5 @@ func getExecPathFromPID(pid uint32) (string, error) {
if err != nil {
return "", err
}
// full path will like: C:\Windows\System32\curl.exe
// we only need the executable name
fullPathName := syscall.UTF16ToString(buf[:size])
nameSplit := strings.Split(fullPathName, "\\")
name := nameSplit[len(nameSplit)-1]
return name, nil
return syscall.UTF16ToString(buf[:size]), nil
}

View File

@@ -21,22 +21,22 @@ const ChromeH2KeepAlivePeriod = 45 * time.Second
var ErrNotLocal = errors.New("the source address is not from local machine.")
type localIPCahceEntry struct {
type localIPCacheEntry struct {
addrs []net.Addr
lastUpdate time.Time
}
var localIPCahce = atomic.Pointer[localIPCahceEntry]{}
var localIPCache = atomic.Pointer[localIPCacheEntry]{}
func IsLocal(ip net.IP) (bool, error) {
var addrs []net.Addr
if entry := localIPCahce.Load(); entry == nil || time.Since(entry.lastUpdate) > time.Minute {
if entry := localIPCache.Load(); entry == nil || time.Since(entry.lastUpdate) > time.Minute {
var err error
addrs, err = net.InterfaceAddrs()
if err != nil {
return false, err
}
localIPCahce.Store(&localIPCahceEntry{
localIPCache.Store(&localIPCacheEntry{
addrs: addrs,
lastUpdate: time.Now(),
})