github.com/igoogolx/clash@v1.19.8/component/process/process_freebsd.go (about) 1 package process 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "net/netip" 7 "strconv" 8 "strings" 9 "unsafe" 10 11 "golang.org/x/sys/unix" 12 ) 13 14 type Xinpgen12 [64]byte // size 64 15 16 type InEndpoints12 struct { 17 FPort [2]byte 18 LPort [2]byte 19 FAddr [16]byte 20 LAddr [16]byte 21 ZoneID uint32 22 } // size 40 23 24 type XTcpcb12 struct { 25 Len uint32 // offset 0 26 _ [20]byte // offset 4 27 SocketAddr uint64 // offset 24 28 _ [84]byte // offset 32 29 Family uint32 // offset 116 30 _ [140]byte // offset 120 31 InEndpoints InEndpoints12 // offset 260 32 _ [444]byte // offset 300 33 } // size 744 34 35 type XInpcb12 struct { 36 Len uint32 // offset 0 37 _ [12]byte // offset 4 38 SocketAddr uint64 // offset 16 39 _ [84]byte // offset 24 40 Family uint32 // offset 108 41 _ [140]byte // offset 112 42 InEndpoints InEndpoints12 // offset 252 43 _ [108]byte // offset 292 44 } // size 400 45 46 type XFile12 struct { 47 Size uint64 // offset 0 48 Pid uint32 // offset 8 49 _ [44]byte // offset 12 50 DataAddr uint64 // offset 56 51 _ [64]byte // offset 64 52 } // size 128 53 54 var majorVersion = func() int { 55 releaseVersion, err := unix.Sysctl("kern.osrelease") 56 if err != nil { 57 return 0 58 } 59 60 majorVersionText, _, _ := strings.Cut(releaseVersion, ".") 61 62 majorVersion, err := strconv.Atoi(majorVersionText) 63 if err != nil { 64 return 0 65 } 66 67 return majorVersion 68 }() 69 70 func findProcessPath(network string, from netip.AddrPort, to netip.AddrPort) (string, error) { 71 switch majorVersion { 72 case 12, 13: 73 return findProcessPath12(network, from, to) 74 } 75 76 return "", ErrPlatformNotSupport 77 } 78 79 func findProcessPath12(network string, from netip.AddrPort, to netip.AddrPort) (string, error) { 80 switch network { 81 case TCP: 82 data, err := unix.SysctlRaw("net.inet.tcp.pcblist") 83 if err != nil { 84 return "", err 85 } 86 87 if len(data) < int(unsafe.Sizeof(Xinpgen12{})) { 88 return "", fmt.Errorf("invalid sysctl data len: %d", len(data)) 89 } 90 91 data = data[unsafe.Sizeof(Xinpgen12{}):] 92 93 for len(data) > int(unsafe.Sizeof(XTcpcb12{}.Len)) { 94 tcb := (*XTcpcb12)(unsafe.Pointer(&data[0])) 95 if tcb.Len < uint32(unsafe.Sizeof(XTcpcb12{})) || uint32(len(data)) < tcb.Len { 96 break 97 } 98 99 data = data[tcb.Len:] 100 101 var ( 102 connFromAddr netip.Addr 103 connToAddr netip.Addr 104 ) 105 if tcb.Family == unix.AF_INET { 106 connFromAddr = netip.AddrFrom4([4]byte(tcb.InEndpoints.LAddr[12:16])) 107 connToAddr = netip.AddrFrom4([4]byte(tcb.InEndpoints.FAddr[12:16])) 108 } else if tcb.Family == unix.AF_INET6 { 109 connFromAddr = netip.AddrFrom16(tcb.InEndpoints.LAddr) 110 connToAddr = netip.AddrFrom16(tcb.InEndpoints.FAddr) 111 } else { 112 continue 113 } 114 115 connFrom := netip.AddrPortFrom(connFromAddr, binary.BigEndian.Uint16(tcb.InEndpoints.LPort[:])) 116 connTo := netip.AddrPortFrom(connToAddr, binary.BigEndian.Uint16(tcb.InEndpoints.FPort[:])) 117 118 if connFrom == from && connTo == to { 119 pid, err := findPidBySocketAddr12(tcb.SocketAddr) 120 if err != nil { 121 return "", err 122 } 123 124 return findExecutableByPid(pid) 125 } 126 } 127 case UDP: 128 data, err := unix.SysctlRaw("net.inet.udp.pcblist") 129 if err != nil { 130 return "", err 131 } 132 133 if len(data) < int(unsafe.Sizeof(Xinpgen12{})) { 134 return "", fmt.Errorf("invalid sysctl data len: %d", len(data)) 135 } 136 137 data = data[unsafe.Sizeof(Xinpgen12{}):] 138 139 for len(data) > int(unsafe.Sizeof(XInpcb12{}.Len)) { 140 icb := (*XInpcb12)(unsafe.Pointer(&data[0])) 141 if icb.Len < uint32(unsafe.Sizeof(XInpcb12{})) || uint32(len(data)) < icb.Len { 142 break 143 } 144 data = data[icb.Len:] 145 146 var connFromAddr netip.Addr 147 if icb.Family == unix.AF_INET { 148 connFromAddr = netip.AddrFrom4([4]byte(icb.InEndpoints.LAddr[12:16])) 149 } else if icb.Family == unix.AF_INET6 { 150 connFromAddr = netip.AddrFrom16(icb.InEndpoints.LAddr) 151 } else { 152 continue 153 } 154 155 connFromPort := binary.BigEndian.Uint16(icb.InEndpoints.LPort[:]) 156 157 if (connFromAddr == from.Addr() || connFromAddr.IsUnspecified()) && connFromPort == from.Port() { 158 pid, err := findPidBySocketAddr12(icb.SocketAddr) 159 if err != nil { 160 return "", err 161 } 162 163 return findExecutableByPid(pid) 164 } 165 } 166 } 167 168 return "", ErrNotFound 169 } 170 171 func findPidBySocketAddr12(socketAddr uint64) (uint32, error) { 172 buf, err := unix.SysctlRaw("kern.file") 173 if err != nil { 174 return 0, err 175 } 176 177 filesLen := len(buf) / int(unsafe.Sizeof(XFile12{})) 178 files := unsafe.Slice((*XFile12)(unsafe.Pointer(&buf[0])), filesLen) 179 180 for _, file := range files { 181 if file.Size != uint64(unsafe.Sizeof(XFile12{})) { 182 return 0, fmt.Errorf("invalid xfile size: %d", file.Size) 183 } 184 185 if file.DataAddr == socketAddr { 186 return file.Pid, nil 187 } 188 } 189 190 return 0, ErrNotFound 191 } 192 193 func findExecutableByPid(pid uint32) (string, error) { 194 buf := make([]byte, unix.PathMax) 195 size := uint64(len(buf)) 196 mib := [4]uint32{ 197 unix.CTL_KERN, 198 14, // KERN_PROC 199 12, // KERN_PROC_PATHNAME 200 pid, 201 } 202 203 _, _, errno := unix.Syscall6( 204 unix.SYS___SYSCTL, 205 uintptr(unsafe.Pointer(&mib[0])), 206 uintptr(len(mib)), 207 uintptr(unsafe.Pointer(&buf[0])), 208 uintptr(unsafe.Pointer(&size)), 209 0, 210 0, 211 ) 212 if errno != 0 || size == 0 { 213 return "", fmt.Errorf("sysctl: get proc name: %w", errno) 214 } 215 216 return string(buf[:size-1]), nil 217 }