find_process_linux.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. //go:build linux
  2. package net
  3. import (
  4. "bufio"
  5. "encoding/hex"
  6. "fmt"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "github.com/xtls/xray-core/common/errors"
  11. )
  12. func FindProcess(dest Destination) (PID int, Name string, AbsolutePath string, err error) {
  13. isLocal, err := IsLocal(dest.Address.IP())
  14. if err != nil {
  15. return 0, "", "", errors.New("failed to determine if address is local: ", err)
  16. }
  17. if !isLocal {
  18. return 0, "", "", ErrNotLocal
  19. }
  20. if dest.Network != Network_TCP && dest.Network != Network_UDP {
  21. panic("Unsupported network type for process lookup.")
  22. }
  23. // the core should never has a domain as source(?
  24. if dest.Address.Family() == AddressFamilyDomain {
  25. panic("Domain addresses are not supported for process lookup.")
  26. }
  27. var procFile string
  28. switch dest.Network {
  29. case Network_TCP:
  30. if dest.Address.Family() == AddressFamilyIPv4 {
  31. procFile = "/proc/net/tcp"
  32. }
  33. if dest.Address.Family() == AddressFamilyIPv6 {
  34. procFile = "/proc/net/tcp6"
  35. }
  36. case Network_UDP:
  37. if dest.Address.Family() == AddressFamilyIPv4 {
  38. procFile = "/proc/net/udp"
  39. }
  40. if dest.Address.Family() == AddressFamilyIPv6 {
  41. procFile = "/proc/net/udp6"
  42. }
  43. default:
  44. panic("Unsupported network type for process lookup.")
  45. }
  46. targetHexAddr, err := formatLittleEndianString(dest.Address, dest.Port)
  47. if err != nil {
  48. return 0, "", "", errors.New("failed to format address: ", err)
  49. }
  50. inode, err := findInodeInFile(procFile, targetHexAddr)
  51. if err != nil {
  52. return 0, "", "", errors.New("could not search in ", procFile).Base(err)
  53. }
  54. if inode == "" {
  55. return 0, "", "", errors.New("connection for ", dest.Address, ":", dest.Port, " not found in ", procFile)
  56. }
  57. pidStr, err := findPidByInode(inode)
  58. if err != nil {
  59. return 0, "", "", errors.New("could not find PID for inode ", inode, ": ", err)
  60. }
  61. if pidStr == "" {
  62. return 0, "", "", errors.New("no process found for inode ", inode)
  63. }
  64. absPath, err := getAbsPath(pidStr)
  65. if err != nil {
  66. return 0, "", "", errors.New("could not get process name for PID ", pidStr, ":", err)
  67. }
  68. nameSplit := strings.Split(absPath, "/")
  69. procName := nameSplit[len(nameSplit)-1]
  70. pid, err := strconv.Atoi(pidStr)
  71. if err != nil {
  72. return 0, "", "", errors.New("failed to parse PID: ", err)
  73. }
  74. return pid, procName, absPath, nil
  75. }
  76. func formatLittleEndianString(addr Address, port Port) (string, error) {
  77. ip := addr.IP()
  78. var ipBytes []byte
  79. if addr.Family() == AddressFamilyIPv4 {
  80. ipBytes = ip.To4()
  81. } else {
  82. ipBytes = ip.To16()
  83. }
  84. if ipBytes == nil {
  85. return "", errors.New("invalid IP format for ", addr.Family(), ": ", ip)
  86. }
  87. for i, j := 0, len(ipBytes)-1; i < j; i, j = i+1, j-1 {
  88. ipBytes[i], ipBytes[j] = ipBytes[j], ipBytes[i]
  89. }
  90. portHex := fmt.Sprintf("%04X", uint16(port))
  91. ipHex := strings.ToUpper(hex.EncodeToString(ipBytes))
  92. return fmt.Sprintf("%s:%s", ipHex, portHex), nil
  93. }
  94. func findInodeInFile(filePath, targetHexAddr string) (string, error) {
  95. file, err := os.Open(filePath)
  96. if err != nil {
  97. return "", err
  98. }
  99. defer file.Close()
  100. scanner := bufio.NewScanner(file)
  101. for scanner.Scan() {
  102. line := scanner.Text()
  103. fields := strings.Fields(line)
  104. if len(fields) < 10 {
  105. continue
  106. }
  107. localAddress := fields[1]
  108. if localAddress == targetHexAddr {
  109. inode := fields[9]
  110. return inode, nil
  111. }
  112. }
  113. return "", scanner.Err()
  114. }
  115. func findPidByInode(inode string) (string, error) {
  116. procDir, err := os.ReadDir("/proc")
  117. if err != nil {
  118. return "", err
  119. }
  120. targetLink := "socket:[" + inode + "]"
  121. for _, entry := range procDir {
  122. if !entry.IsDir() {
  123. continue
  124. }
  125. pid := entry.Name()
  126. if _, err := strconv.Atoi(pid); err != nil {
  127. continue
  128. }
  129. fdPath := fmt.Sprintf("/proc/%s/fd", pid)
  130. fdDir, err := os.ReadDir(fdPath)
  131. if err != nil {
  132. continue
  133. }
  134. for _, fdEntry := range fdDir {
  135. linkPath := fmt.Sprintf("%s/%s", fdPath, fdEntry.Name())
  136. linkTarget, err := os.Readlink(linkPath)
  137. if err != nil {
  138. continue
  139. }
  140. if linkTarget == targetLink {
  141. return pid, nil
  142. }
  143. }
  144. }
  145. return "", nil
  146. }
  147. func getAbsPath(pid string) (string, error) {
  148. path := fmt.Sprintf("/proc/%s/exe", pid)
  149. return os.Readlink(path)
  150. }