find_process_linux.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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) (int, string, 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. procName, err := getProcessName(pidStr)
  65. if err != nil {
  66. return 0, "", fmt.Errorf("could not get process name for PID %s: %w", pidStr, err)
  67. }
  68. pid, err := strconv.Atoi(pidStr)
  69. if err != nil {
  70. return 0, "", errors.New("failed to parse PID: ", err)
  71. }
  72. return pid, procName, nil
  73. }
  74. func formatLittleEndianString(addr Address, port Port) (string, error) {
  75. ip := addr.IP()
  76. var ipBytes []byte
  77. if addr.Family() == AddressFamilyIPv4 {
  78. ipBytes = ip.To4()
  79. } else {
  80. ipBytes = ip.To16()
  81. }
  82. if ipBytes == nil {
  83. return "", errors.New("invalid IP format for ", addr.Family(), ": ", ip)
  84. }
  85. for i, j := 0, len(ipBytes)-1; i < j; i, j = i+1, j-1 {
  86. ipBytes[i], ipBytes[j] = ipBytes[j], ipBytes[i]
  87. }
  88. portHex := fmt.Sprintf("%04X", uint16(port))
  89. ipHex := strings.ToUpper(hex.EncodeToString(ipBytes))
  90. return fmt.Sprintf("%s:%s", ipHex, portHex), nil
  91. }
  92. func findInodeInFile(filePath, targetHexAddr string) (string, error) {
  93. file, err := os.Open(filePath)
  94. if err != nil {
  95. return "", err
  96. }
  97. defer file.Close()
  98. scanner := bufio.NewScanner(file)
  99. for scanner.Scan() {
  100. line := scanner.Text()
  101. fields := strings.Fields(line)
  102. if len(fields) < 10 {
  103. continue
  104. }
  105. localAddress := fields[1]
  106. if localAddress == targetHexAddr {
  107. inode := fields[9]
  108. return inode, nil
  109. }
  110. }
  111. return "", scanner.Err()
  112. }
  113. func findPidByInode(inode string) (string, error) {
  114. procDir, err := os.ReadDir("/proc")
  115. if err != nil {
  116. return "", err
  117. }
  118. targetLink := "socket:[" + inode + "]"
  119. for _, entry := range procDir {
  120. if !entry.IsDir() {
  121. continue
  122. }
  123. pid := entry.Name()
  124. if _, err := strconv.Atoi(pid); err != nil {
  125. continue
  126. }
  127. fdPath := fmt.Sprintf("/proc/%s/fd", pid)
  128. fdDir, err := os.ReadDir(fdPath)
  129. if err != nil {
  130. continue
  131. }
  132. for _, fdEntry := range fdDir {
  133. linkPath := fmt.Sprintf("%s/%s", fdPath, fdEntry.Name())
  134. linkTarget, err := os.Readlink(linkPath)
  135. if err != nil {
  136. continue
  137. }
  138. if linkTarget == targetLink {
  139. return pid, nil
  140. }
  141. }
  142. }
  143. return "", nil
  144. }
  145. func getProcessName(pid string) (string, error) {
  146. path := fmt.Sprintf("/proc/%s/comm", pid)
  147. content, err := os.ReadFile(path)
  148. if err != nil {
  149. return "", err
  150. }
  151. // remove trailing \n
  152. return strings.TrimSpace(string(content)), nil
  153. }