process_linux.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package process
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "net/netip"
  7. "os"
  8. "path"
  9. "path/filepath"
  10. "runtime"
  11. "strings"
  12. "syscall"
  13. "unicode"
  14. "unsafe"
  15. "github.com/mdlayher/netlink"
  16. "golang.org/x/sys/unix"
  17. )
  18. const (
  19. SOCK_DIAG_BY_FAMILY = 20
  20. inetDiagRequestSize = int(unsafe.Sizeof(inetDiagRequest{}))
  21. inetDiagResponseSize = int(unsafe.Sizeof(inetDiagResponse{}))
  22. )
  23. type inetDiagRequest struct {
  24. Family byte
  25. Protocol byte
  26. Ext byte
  27. Pad byte
  28. States uint32
  29. SrcPort [2]byte
  30. DstPort [2]byte
  31. Src [16]byte
  32. Dst [16]byte
  33. If uint32
  34. Cookie [2]uint32
  35. }
  36. type inetDiagResponse struct {
  37. Family byte
  38. State byte
  39. Timer byte
  40. ReTrans byte
  41. SrcPort [2]byte
  42. DstPort [2]byte
  43. Src [16]byte
  44. Dst [16]byte
  45. If uint32
  46. Cookie [2]uint32
  47. Expires uint32
  48. RQueue uint32
  49. WQueue uint32
  50. UID uint32
  51. INode uint32
  52. }
  53. func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {
  54. uid, inode, err := resolveSocketByNetlink(network, ip, srcPort)
  55. if err != nil {
  56. return 0, "", err
  57. }
  58. pp, err := resolveProcessNameByProcSearch(inode, uid)
  59. return uid, pp, err
  60. }
  61. func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {
  62. request := &inetDiagRequest{
  63. States: 0xffffffff,
  64. Cookie: [2]uint32{0xffffffff, 0xffffffff},
  65. }
  66. if ip.Is4() {
  67. request.Family = unix.AF_INET
  68. } else {
  69. request.Family = unix.AF_INET6
  70. }
  71. if strings.HasPrefix(network, "tcp") {
  72. request.Protocol = unix.IPPROTO_TCP
  73. } else if strings.HasPrefix(network, "udp") {
  74. request.Protocol = unix.IPPROTO_UDP
  75. } else {
  76. return 0, 0, ErrInvalidNetwork
  77. }
  78. copy(request.Src[:], ip.AsSlice())
  79. binary.BigEndian.PutUint16(request.SrcPort[:], uint16(srcPort))
  80. conn, err := netlink.Dial(unix.NETLINK_INET_DIAG, nil)
  81. if err != nil {
  82. return 0, 0, err
  83. }
  84. defer conn.Close()
  85. message := netlink.Message{
  86. Header: netlink.Header{
  87. Type: SOCK_DIAG_BY_FAMILY,
  88. Flags: netlink.Request | netlink.Dump,
  89. },
  90. Data: (*(*[inetDiagRequestSize]byte)(unsafe.Pointer(request)))[:],
  91. }
  92. messages, err := conn.Execute(message)
  93. if err != nil {
  94. return 0, 0, err
  95. }
  96. for _, msg := range messages {
  97. if len(msg.Data) < inetDiagResponseSize {
  98. continue
  99. }
  100. response := (*inetDiagResponse)(unsafe.Pointer(&msg.Data[0]))
  101. return response.UID, response.INode, nil
  102. }
  103. return 0, 0, ErrNotFound
  104. }
  105. func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {
  106. files, err := os.ReadDir("/proc")
  107. if err != nil {
  108. return "", err
  109. }
  110. buffer := make([]byte, unix.PathMax)
  111. socket := fmt.Appendf(nil, "socket:[%d]", inode)
  112. for _, f := range files {
  113. if !f.IsDir() || !isPid(f.Name()) {
  114. continue
  115. }
  116. info, err := f.Info()
  117. if err != nil {
  118. return "", err
  119. }
  120. if info.Sys().(*syscall.Stat_t).Uid != uid {
  121. continue
  122. }
  123. processPath := filepath.Join("/proc", f.Name())
  124. fdPath := filepath.Join(processPath, "fd")
  125. fds, err := os.ReadDir(fdPath)
  126. if err != nil {
  127. continue
  128. }
  129. for _, fd := range fds {
  130. n, err := unix.Readlink(filepath.Join(fdPath, fd.Name()), buffer)
  131. if err != nil {
  132. continue
  133. }
  134. if runtime.GOOS == "android" {
  135. if bytes.Equal(buffer[:n], socket) {
  136. cmdline, err := os.ReadFile(path.Join(processPath, "cmdline"))
  137. if err != nil {
  138. return "", err
  139. }
  140. return splitCmdline(cmdline), nil
  141. }
  142. } else {
  143. if bytes.Equal(buffer[:n], socket) {
  144. return os.Readlink(filepath.Join(processPath, "exe"))
  145. }
  146. }
  147. }
  148. }
  149. return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
  150. }
  151. func splitCmdline(cmdline []byte) string {
  152. cmdline = bytes.Trim(cmdline, " ")
  153. idx := bytes.IndexFunc(cmdline, func(r rune) bool {
  154. return unicode.IsControl(r) || unicode.IsSpace(r)
  155. })
  156. if idx == -1 {
  157. return filepath.Base(string(cmdline))
  158. }
  159. return filepath.Base(string(cmdline[:idx]))
  160. }
  161. func isPid(s string) bool {
  162. return strings.IndexFunc(s, func(r rune) bool {
  163. return !unicode.IsDigit(r)
  164. }) == -1
  165. }