Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: missing package name on Android bare core, switch from netlink t… #1722

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 22 additions & 170 deletions component/process/process_linux.go
Original file line number Diff line number Diff line change
@@ -1,200 +1,52 @@
package process

import (
"bytes"
"encoding/binary"
"fmt"
"github.com/shirou/gopsutil/v4/net"
"github.com/shirou/gopsutil/v4/process"
"net/netip"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"syscall"
"unicode"
"unsafe"

"github.com/mdlayher/netlink"
"golang.org/x/sys/unix"
)

const (
SOCK_DIAG_BY_FAMILY = 20
inetDiagRequestSize = int(unsafe.Sizeof(inetDiagRequest{}))
inetDiagResponseSize = int(unsafe.Sizeof(inetDiagResponse{}))
)

type inetDiagRequest struct {
Family byte
Protocol byte
Ext byte
Pad byte
States uint32

SrcPort [2]byte
DstPort [2]byte
Src [16]byte
Dst [16]byte
If uint32
Cookie [2]uint32
}

type inetDiagResponse struct {
Family byte
State byte
Timer byte
ReTrans byte

SrcPort [2]byte
DstPort [2]byte
Src [16]byte
Dst [16]byte
If uint32
Cookie [2]uint32

Expires uint32
RQueue uint32
WQueue uint32
UID uint32
INode uint32
}

func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {
uid, inode, err := resolveSocketByNetlink(network, ip, srcPort)
uid, err := resolveSocketUID(ip, srcPort)
if err != nil {
return 0, "", err
}
pp, err := resolveProcessNameByProcSearch(inode, uid)
return uid, pp, err
name, err := resolveProcessNameByUID(uid)
return uid, name, err
}

func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {
request := &inetDiagRequest{
States: 0xffffffff,
Cookie: [2]uint32{0xffffffff, 0xffffffff},
}

if ip.Is4() {
request.Family = unix.AF_INET
} else {
request.Family = unix.AF_INET6
}

if strings.HasPrefix(network, "tcp") {
request.Protocol = unix.IPPROTO_TCP
} else if strings.HasPrefix(network, "udp") {
request.Protocol = unix.IPPROTO_UDP
} else {
return 0, 0, ErrInvalidNetwork
}

copy(request.Src[:], ip.AsSlice())

binary.BigEndian.PutUint16(request.SrcPort[:], uint16(srcPort))

conn, err := netlink.Dial(unix.NETLINK_INET_DIAG, nil)
func resolveSocketUID(ip netip.Addr, srcPort int) (uint32, error) {
connections, err := net.Connections("all")
if err != nil {
return 0, 0, err
return 0, err
}
defer conn.Close()

message := netlink.Message{
Header: netlink.Header{
Type: SOCK_DIAG_BY_FAMILY,
Flags: netlink.Request | netlink.Dump,
},
Data: (*(*[inetDiagRequestSize]byte)(unsafe.Pointer(request)))[:],
}

messages, err := conn.Execute(message)
if err != nil {
return 0, 0, err
}

for _, msg := range messages {
if len(msg.Data) < inetDiagResponseSize {
continue
for _, conn := range connections {
if conn.Laddr.Port == uint32(srcPort) && conn.Laddr.IP == ip.String() {
if len(conn.Uids) > 0 {
return uint32(conn.Uids[0]), nil
}
}

response := (*inetDiagResponse)(unsafe.Pointer(&msg.Data[0]))

return response.UID, response.INode, nil
}

return 0, 0, ErrNotFound
return 0, ErrNotFound
}

func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {
files, err := os.ReadDir("/proc")
func resolveProcessNameByUID(uid uint32) (string, error) {
processes, err := process.Processes()
if err != nil {
return "", err
}

buffer := make([]byte, unix.PathMax)
socket := fmt.Appendf(nil, "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 != uid {
continue
}

processPath := filepath.Join("/proc", f.Name())
fdPath := filepath.Join(processPath, "fd")

fds, err := os.ReadDir(fdPath)
if err != nil {
continue
}

for _, fd := range fds {
n, err := unix.Readlink(filepath.Join(fdPath, fd.Name()), buffer)
for _, p := range processes {
puid, err := p.Uids()
if err == nil && len(puid) > 0 && uint32(puid[0]) == uid {
name, err := p.Name()
if err != nil {
continue
}

if runtime.GOOS == "android" {
if bytes.Equal(buffer[:n], socket) {
cmdline, err := os.ReadFile(path.Join(processPath, "cmdline"))
if err != nil {
return "", err
}

return splitCmdline(cmdline), nil
}
} else {
if bytes.Equal(buffer[:n], socket) {
return os.Readlink(filepath.Join(processPath, "exe"))
}
}
return name, 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
return "", fmt.Errorf("process of uid(%d) not found", uid)
}
Loading