forked from MetaCubeX/mihomo
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: 安卓恢复进程规则,可通过enable-process开关,默认true
- Loading branch information
adlyq
committed
May 19, 2022
1 parent
fe25ae8
commit cc1c134
Showing
11 changed files
with
280 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
package process | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"fmt" | ||
"net" | ||
"net/netip" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
"unicode" | ||
"unsafe" | ||
|
||
"github.com/Dreamacro/clash/common/pool" | ||
) | ||
|
||
// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62 | ||
var nativeEndian = func() binary.ByteOrder { | ||
var x uint32 = 0x01020304 | ||
if *(*byte)(unsafe.Pointer(&x)) == 0x01 { | ||
return binary.BigEndian | ||
} | ||
|
||
return binary.LittleEndian | ||
}() | ||
|
||
const ( | ||
sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48 | ||
socketDiagByFamily = 20 | ||
pathProc = "/proc" | ||
) | ||
|
||
func findProcessName(network string, ip netip.Addr, srcPort int) (int32, string, error) { | ||
inode, uid, err := resolveSocketByNetlink(network, ip, srcPort) | ||
if err != nil { | ||
return -1, "", err | ||
} | ||
|
||
pp, err := resolveProcessNameByProcSearch(inode, uid) | ||
return uid, pp, err | ||
} | ||
|
||
func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (int32, int32, error) { | ||
var family byte | ||
var protocol byte | ||
|
||
switch network { | ||
case TCP: | ||
protocol = syscall.IPPROTO_TCP | ||
case UDP: | ||
protocol = syscall.IPPROTO_UDP | ||
default: | ||
return 0, 0, ErrInvalidNetwork | ||
} | ||
|
||
if ip.Is4() { | ||
family = syscall.AF_INET | ||
} else { | ||
family = syscall.AF_INET6 | ||
} | ||
|
||
req := packSocketDiagRequest(family, protocol, ip, uint16(srcPort)) | ||
|
||
socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG) | ||
if err != nil { | ||
return 0, 0, fmt.Errorf("dial netlink: %w", err) | ||
} | ||
defer syscall.Close(socket) | ||
|
||
_ = 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, | ||
Pad: 0, | ||
Pid: 0, | ||
Groups: 0, | ||
}); err != nil { | ||
return 0, 0, err | ||
} | ||
|
||
if _, err := syscall.Write(socket, req); err != nil { | ||
return 0, 0, fmt.Errorf("write request: %w", err) | ||
} | ||
|
||
rb := pool.Get(pool.RelayBufferSize) | ||
defer pool.Put(rb) | ||
|
||
n, err := syscall.Read(socket, rb) | ||
if err != nil { | ||
return 0, 0, fmt.Errorf("read response: %w", err) | ||
} | ||
|
||
messages, err := syscall.ParseNetlinkMessage(rb[:n]) | ||
if err != nil { | ||
return 0, 0, fmt.Errorf("parse netlink message: %w", err) | ||
} else if len(messages) == 0 { | ||
return 0, 0, fmt.Errorf("unexcepted netlink response") | ||
} | ||
|
||
message := messages[0] | ||
if message.Header.Type&syscall.NLMSG_ERROR != 0 { | ||
return 0, 0, fmt.Errorf("netlink message: NLMSG_ERROR") | ||
} | ||
|
||
uid, inode := unpackSocketDiagResponse(&messages[0]) | ||
if uid < 0 || inode < 0 { | ||
return 0, 0, fmt.Errorf("invalid uid(%d) or inode(%d)", uid, inode) | ||
} | ||
|
||
return uid, inode, nil | ||
} | ||
|
||
func packSocketDiagRequest(family, protocol byte, source netip.Addr, sourcePort uint16) []byte { | ||
s := make([]byte, 16) | ||
|
||
copy(s, source.AsSlice()) | ||
|
||
buf := make([]byte, sizeOfSocketDiagRequest) | ||
|
||
nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest) | ||
nativeEndian.PutUint16(buf[4:6], socketDiagByFamily) | ||
nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP) | ||
nativeEndian.PutUint32(buf[8:12], 0) | ||
nativeEndian.PutUint32(buf[12:16], 0) | ||
|
||
buf[16] = family | ||
buf[17] = protocol | ||
buf[18] = 0 | ||
buf[19] = 0 | ||
nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF) | ||
|
||
binary.BigEndian.PutUint16(buf[24:26], sourcePort) | ||
binary.BigEndian.PutUint16(buf[26:28], 0) | ||
|
||
copy(buf[28:44], s) | ||
copy(buf[44:60], net.IPv6zero) | ||
|
||
nativeEndian.PutUint32(buf[60:64], 0) | ||
nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF) | ||
|
||
return buf | ||
} | ||
|
||
func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid int32) { | ||
if len(msg.Data) < 72 { | ||
return 0, 0 | ||
} | ||
|
||
data := msg.Data | ||
|
||
uid = int32(nativeEndian.Uint32(data[64:68])) | ||
inode = int32(nativeEndian.Uint32(data[68:72])) | ||
|
||
return | ||
} | ||
|
||
func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { | ||
files, err := os.ReadDir(pathProc) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
buffer := make([]byte, syscall.PathMax) | ||
socket := []byte(fmt.Sprintf("socket:[%d]", inode)) | ||
|
||
for _, f := range files { | ||
if !f.IsDir() || !isPid(f.Name()) { | ||
continue | ||
} | ||
|
||
info, err := f.Info() | ||
if err != nil { | ||
return "", err | ||
} | ||
if info.Sys().(*syscall.Stat_t).Uid != uint32(uid) { | ||
continue | ||
} | ||
|
||
processPath := path.Join(pathProc, f.Name()) | ||
fdPath := path.Join(processPath, "fd") | ||
|
||
fds, err := os.ReadDir(fdPath) | ||
if err != nil { | ||
continue | ||
} | ||
|
||
for _, fd := range fds { | ||
n, err := syscall.Readlink(path.Join(fdPath, fd.Name()), buffer) | ||
if err != nil { | ||
continue | ||
} | ||
|
||
if bytes.Equal(buffer[:n], socket) { | ||
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return splitCmdline(cmdline), nil | ||
} | ||
} | ||
} | ||
|
||
return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) | ||
} | ||
|
||
func splitCmdline(cmdline []byte) string { | ||
cmdline = bytes.Trim(cmdline, " ") | ||
|
||
idx := bytes.IndexFunc(cmdline, func(r rune) bool { | ||
return unicode.IsControl(r) || unicode.IsSpace(r) | ||
}) | ||
|
||
if idx == -1 { | ||
return filepath.Base(string(cmdline)) | ||
} | ||
return filepath.Base(string(cmdline[:idx])) | ||
} | ||
|
||
func isPid(s string) bool { | ||
return strings.IndexFunc(s, func(r rune) bool { | ||
return !unicode.IsDigit(r) | ||
}) == -1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.