searcher_darwin.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package process
  2. import (
  3. "context"
  4. "encoding/binary"
  5. "net/netip"
  6. "os"
  7. "syscall"
  8. "unsafe"
  9. N "github.com/sagernet/sing/common/network"
  10. "golang.org/x/sys/unix"
  11. )
  12. var _ Searcher = (*darwinSearcher)(nil)
  13. type darwinSearcher struct{}
  14. func NewSearcher(_ Config) (Searcher, error) {
  15. return &darwinSearcher{}, nil
  16. }
  17. func (d *darwinSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*Info, error) {
  18. processName, err := findProcessName(network, source.Addr(), int(source.Port()))
  19. if err != nil {
  20. return nil, err
  21. }
  22. return &Info{ProcessPath: processName, UserId: -1}, nil
  23. }
  24. func findProcessName(network string, ip netip.Addr, port int) (string, error) {
  25. var spath string
  26. switch network {
  27. case N.NetworkTCP:
  28. spath = "net.inet.tcp.pcblist_n"
  29. case N.NetworkUDP:
  30. spath = "net.inet.udp.pcblist_n"
  31. default:
  32. return "", os.ErrInvalid
  33. }
  34. isIPv4 := ip.Is4()
  35. value, err := syscall.Sysctl(spath)
  36. if err != nil {
  37. return "", err
  38. }
  39. buf := []byte(value)
  40. // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
  41. // size/offset are round up (aligned) to 8 bytes in darwin
  42. // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
  43. // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
  44. itemSize := 384
  45. if network == N.NetworkTCP {
  46. // rup8(sizeof(xtcpcb_n))
  47. itemSize += 208
  48. }
  49. // skip the first xinpgen(24 bytes) block
  50. for i := 24; i+itemSize <= len(buf); i += itemSize {
  51. // offset of xinpcb_n and xsocket_n
  52. inp, so := i, i+104
  53. srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20])
  54. if uint16(port) != srcPort {
  55. continue
  56. }
  57. // xinpcb_n.inp_vflag
  58. flag := buf[inp+44]
  59. var srcIP netip.Addr
  60. switch {
  61. case flag&0x1 > 0 && isIPv4:
  62. // ipv4
  63. srcIP = netip.AddrFrom4(*(*[4]byte)(buf[inp+76 : inp+80]))
  64. case flag&0x2 > 0 && !isIPv4:
  65. // ipv6
  66. srcIP = netip.AddrFrom16(*(*[16]byte)(buf[inp+64 : inp+80]))
  67. default:
  68. continue
  69. }
  70. if ip != srcIP {
  71. continue
  72. }
  73. // xsocket_n.so_last_pid
  74. pid := readNativeUint32(buf[so+68 : so+72])
  75. return getExecPathFromPID(pid)
  76. }
  77. return "", ErrNotFound
  78. }
  79. func getExecPathFromPID(pid uint32) (string, error) {
  80. const (
  81. procpidpathinfo = 0xb
  82. procpidpathinfosize = 1024
  83. proccallnumpidinfo = 0x2
  84. )
  85. buf := make([]byte, procpidpathinfosize)
  86. _, _, errno := syscall.Syscall6(
  87. syscall.SYS_PROC_INFO,
  88. proccallnumpidinfo,
  89. uintptr(pid),
  90. procpidpathinfo,
  91. 0,
  92. uintptr(unsafe.Pointer(&buf[0])),
  93. procpidpathinfosize)
  94. if errno != 0 {
  95. return "", errno
  96. }
  97. return unix.ByteSliceToString(buf), nil
  98. }
  99. func readNativeUint32(b []byte) uint32 {
  100. return *(*uint32)(unsafe.Pointer(&b[0]))
  101. }