github.com/yaling888/clash@v1.53.0/component/process/process_darwin.go (about) 1 package process 2 3 import ( 4 "encoding/binary" 5 "net/netip" 6 "strconv" 7 "strings" 8 "syscall" 9 "unsafe" 10 11 "golang.org/x/sys/unix" 12 13 "github.com/yaling888/clash/common/pool" 14 ) 15 16 const ( 17 procpidpathinfo = 0xb 18 procpidpathinfosize = 1024 19 proccallnumpidinfo = 0x2 20 ) 21 22 var offset = 408 23 24 func init() { 25 value, _ := syscall.Sysctl("kern.osrelease") 26 before, _, _ := strings.Cut(value, ".") 27 n, _ := strconv.ParseInt(before, 10, 64) 28 if n < 22 { 29 offset = 384 30 } 31 } 32 33 func findProcessPath(network string, from netip.AddrPort, _ netip.AddrPort) (string, error) { 34 var spath string 35 switch network { 36 case TCP: 37 spath = "net.inet.tcp.pcblist_n" 38 case UDP: 39 spath = "net.inet.udp.pcblist_n" 40 default: 41 return "", ErrInvalidNetwork 42 } 43 44 value, err := syscall.Sysctl(spath) 45 if err != nil { 46 return "", err 47 } 48 49 buf := []byte(value) 50 51 itemSize := offset 52 if network == TCP { 53 // rup8(sizeof(xtcpcb_n)) 54 itemSize += 208 55 } 56 57 var fallbackUDPProcess string 58 // skip the first xinpgen(24 bytes) block 59 for i := 24; i+itemSize <= len(buf); i += itemSize { 60 // offset of xinpcb_n and xsocket_n 61 so := i + 104 62 63 srcPort := binary.BigEndian.Uint16(buf[i+18 : i+20]) 64 if from.Port() != srcPort { 65 continue 66 } 67 68 // xinpcb_n.inp_vflag 69 flag := buf[i+44] 70 71 var srcIP netip.Addr 72 switch { 73 case flag&0x1 > 0: 74 // ipv4 75 srcIP, _ = netip.AddrFromSlice(buf[i+76 : i+80]) 76 case flag&0x2 > 0: 77 // ipv6 78 srcIP, _ = netip.AddrFromSlice(buf[i+64 : i+80]) 79 default: 80 continue 81 } 82 83 if !srcIP.IsValid() { 84 continue 85 } 86 87 if from.Addr() == srcIP { 88 // xsocket_n.so_last_pid 89 pid := readNativeUint32(buf[so+68 : so+72]) 90 return getExecPathFromPID(pid) 91 } 92 93 // udp packet connection may be not equal with srcIP 94 if network == UDP && srcIP.IsUnspecified() && from.Addr().Is4() == srcIP.Is4() { 95 fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72])) 96 } 97 } 98 99 if network == UDP && fallbackUDPProcess != "" { 100 return fallbackUDPProcess, nil 101 } 102 103 return "", ErrNotFound 104 } 105 106 func getExecPathFromPID(pid uint32) (string, error) { 107 bufP := pool.GetBufferWriter() 108 bufP.Grow(procpidpathinfosize) 109 defer pool.PutBufferWriter(bufP) 110 buf := *bufP 111 _, _, errno := syscall.Syscall6( 112 syscall.SYS_PROC_INFO, 113 proccallnumpidinfo, 114 uintptr(pid), 115 procpidpathinfo, 116 0, 117 uintptr(unsafe.Pointer(&buf[0])), 118 procpidpathinfosize) 119 if errno != 0 { 120 return "", errno 121 } 122 123 return unix.ByteSliceToString(buf), nil 124 } 125 126 func readNativeUint32(b []byte) uint32 { 127 return binary.NativeEndian.Uint32(b) 128 }