searcher_linux_shared.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //go:build linux
  2. package process
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. "fmt"
  7. "net"
  8. "net/netip"
  9. "os"
  10. "path"
  11. "strings"
  12. "syscall"
  13. "unicode"
  14. "unsafe"
  15. "github.com/sagernet/sing/common"
  16. "github.com/sagernet/sing/common/buf"
  17. E "github.com/sagernet/sing/common/exceptions"
  18. N "github.com/sagernet/sing/common/network"
  19. )
  20. // from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62
  21. var nativeEndian = func() binary.ByteOrder {
  22. var x uint32 = 0x01020304
  23. if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
  24. return binary.BigEndian
  25. }
  26. return binary.LittleEndian
  27. }()
  28. const (
  29. sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48
  30. socketDiagByFamily = 20
  31. pathProc = "/proc"
  32. )
  33. func resolveSocketByNetlink(network string, source netip.AddrPort, destination netip.AddrPort) (inode, uid uint32, err error) {
  34. var family uint8
  35. var protocol uint8
  36. switch network {
  37. case N.NetworkTCP:
  38. protocol = syscall.IPPROTO_TCP
  39. case N.NetworkUDP:
  40. protocol = syscall.IPPROTO_UDP
  41. default:
  42. return 0, 0, os.ErrInvalid
  43. }
  44. if source.Addr().Is4() {
  45. family = syscall.AF_INET
  46. } else {
  47. family = syscall.AF_INET6
  48. }
  49. req := packSocketDiagRequest(family, protocol, source)
  50. socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG)
  51. if err != nil {
  52. return 0, 0, E.Cause(err, "dial netlink")
  53. }
  54. defer syscall.Close(socket)
  55. syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100})
  56. syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100})
  57. err = syscall.Connect(socket, &syscall.SockaddrNetlink{
  58. Family: syscall.AF_NETLINK,
  59. Pad: 0,
  60. Pid: 0,
  61. Groups: 0,
  62. })
  63. if err != nil {
  64. return
  65. }
  66. _, err = syscall.Write(socket, req)
  67. if err != nil {
  68. return 0, 0, E.Cause(err, "write netlink request")
  69. }
  70. _buffer := buf.StackNew()
  71. defer common.KeepAlive(_buffer)
  72. buffer := common.Dup(_buffer)
  73. defer buffer.Release()
  74. n, err := syscall.Read(socket, buffer.FreeBytes())
  75. if err != nil {
  76. return 0, 0, E.Cause(err, "read netlink response")
  77. }
  78. buffer.Truncate(n)
  79. messages, err := syscall.ParseNetlinkMessage(buffer.Bytes())
  80. if err != nil {
  81. return 0, 0, E.Cause(err, "parse netlink message")
  82. } else if len(messages) == 0 {
  83. return 0, 0, E.New("unexcepted netlink response")
  84. }
  85. message := messages[0]
  86. if message.Header.Type&syscall.NLMSG_ERROR != 0 {
  87. return 0, 0, E.New("netlink message: NLMSG_ERROR")
  88. }
  89. inode, uid = unpackSocketDiagResponse(&messages[0])
  90. return
  91. }
  92. func packSocketDiagRequest(family, protocol byte, source netip.AddrPort) []byte {
  93. s := make([]byte, 16)
  94. copy(s, source.Addr().AsSlice())
  95. buf := make([]byte, sizeOfSocketDiagRequest)
  96. nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest)
  97. nativeEndian.PutUint16(buf[4:6], socketDiagByFamily)
  98. nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP)
  99. nativeEndian.PutUint32(buf[8:12], 0)
  100. nativeEndian.PutUint32(buf[12:16], 0)
  101. buf[16] = family
  102. buf[17] = protocol
  103. buf[18] = 0
  104. buf[19] = 0
  105. nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF)
  106. binary.BigEndian.PutUint16(buf[24:26], source.Port())
  107. binary.BigEndian.PutUint16(buf[26:28], 0)
  108. copy(buf[28:44], s)
  109. copy(buf[44:60], net.IPv6zero)
  110. nativeEndian.PutUint32(buf[60:64], 0)
  111. nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF)
  112. return buf
  113. }
  114. func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid uint32) {
  115. if len(msg.Data) < 72 {
  116. return 0, 0
  117. }
  118. data := msg.Data
  119. uid = nativeEndian.Uint32(data[64:68])
  120. inode = nativeEndian.Uint32(data[68:72])
  121. return
  122. }
  123. func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {
  124. files, err := os.ReadDir(pathProc)
  125. if err != nil {
  126. return "", err
  127. }
  128. buffer := make([]byte, syscall.PathMax)
  129. socket := []byte(fmt.Sprintf("socket:[%d]", inode))
  130. for _, f := range files {
  131. if !f.IsDir() || !isPid(f.Name()) {
  132. continue
  133. }
  134. info, err := f.Info()
  135. if err != nil {
  136. return "", err
  137. }
  138. if info.Sys().(*syscall.Stat_t).Uid != uid {
  139. continue
  140. }
  141. processPath := path.Join(pathProc, f.Name())
  142. fdPath := path.Join(processPath, "fd")
  143. fds, err := os.ReadDir(fdPath)
  144. if err != nil {
  145. continue
  146. }
  147. for _, fd := range fds {
  148. n, err := syscall.Readlink(path.Join(fdPath, fd.Name()), buffer)
  149. if err != nil {
  150. continue
  151. }
  152. if bytes.Equal(buffer[:n], socket) {
  153. return os.Readlink(path.Join(processPath, "exe"))
  154. }
  155. }
  156. }
  157. return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode)
  158. }
  159. func isPid(s string) bool {
  160. return strings.IndexFunc(s, func(r rune) bool {
  161. return !unicode.IsDigit(r)
  162. }) == -1
  163. }