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