process_darwin.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. package process
  2. import (
  3. "encoding/binary"
  4. "net/netip"
  5. "strconv"
  6. "strings"
  7. "syscall"
  8. "unsafe"
  9. "golang.org/x/sys/unix"
  10. )
  11. const (
  12. procpidpathinfo = 0xb
  13. procpidpathinfosize = 1024
  14. proccallnumpidinfo = 0x2
  15. )
  16. var structSize = func() int {
  17. value, _ := syscall.Sysctl("kern.osrelease")
  18. major, _, _ := strings.Cut(value, ".")
  19. n, _ := strconv.ParseInt(major, 10, 64)
  20. switch true {
  21. case n >= 22:
  22. return 408
  23. default:
  24. // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n
  25. // size/offset are round up (aligned) to 8 bytes in darwin
  26. // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +
  27. // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))
  28. return 384
  29. }
  30. }()
  31. func findProcessName(network string, ip netip.Addr, port int) (uint32, string, error) {
  32. var spath string
  33. switch network {
  34. case TCP:
  35. spath = "net.inet.tcp.pcblist_n"
  36. case UDP:
  37. spath = "net.inet.udp.pcblist_n"
  38. default:
  39. return 0, "", ErrInvalidNetwork
  40. }
  41. isIPv4 := ip.Is4()
  42. value, err := unix.SysctlRaw(spath)
  43. if err != nil {
  44. return 0, "", err
  45. }
  46. buf := value
  47. itemSize := structSize
  48. if network == TCP {
  49. // rup8(sizeof(xtcpcb_n))
  50. itemSize += 208
  51. }
  52. var fallbackUDPProcess string
  53. // skip the first xinpgen(24 bytes) block
  54. for i := 24; i+itemSize <= len(buf); i += itemSize {
  55. // offset of xinpcb_n and xsocket_n
  56. inp, so := i, i+104
  57. srcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20])
  58. if uint16(port) != srcPort {
  59. continue
  60. }
  61. // xinpcb_n.inp_vflag
  62. flag := buf[inp+44]
  63. var (
  64. srcIP netip.Addr
  65. srcIsIPv4 bool
  66. )
  67. switch {
  68. case flag&0x1 > 0 && isIPv4:
  69. // ipv4
  70. srcIP, _ = netip.AddrFromSlice(buf[inp+76 : inp+80])
  71. srcIsIPv4 = true
  72. case flag&0x2 > 0 && !isIPv4:
  73. // ipv6
  74. srcIP, _ = netip.AddrFromSlice(buf[inp+64 : inp+80])
  75. default:
  76. continue
  77. }
  78. if ip == srcIP {
  79. // xsocket_n.so_last_pid
  80. pid := readNativeUint32(buf[so+68 : so+72])
  81. pp, err := getExecPathFromPID(pid)
  82. return 0, pp, err
  83. }
  84. // udp packet connection may be not equal with srcIP
  85. if network == UDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 {
  86. fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72]))
  87. }
  88. }
  89. if network == UDP && fallbackUDPProcess != "" {
  90. return 0, fallbackUDPProcess, nil
  91. }
  92. return 0, "", ErrNotFound
  93. }
  94. func getExecPathFromPID(pid uint32) (string, error) {
  95. buf := make([]byte, procpidpathinfosize)
  96. _, _, errno := syscall.Syscall6(
  97. syscall.SYS_PROC_INFO,
  98. proccallnumpidinfo,
  99. uintptr(pid),
  100. procpidpathinfo,
  101. 0,
  102. uintptr(unsafe.Pointer(&buf[0])),
  103. procpidpathinfosize)
  104. if errno != 0 {
  105. return "", errno
  106. }
  107. return unix.ByteSliceToString(buf), nil
  108. }
  109. func readNativeUint32(b []byte) uint32 {
  110. return *(*uint32)(unsafe.Pointer(&b[0]))
  111. }