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