diff --git a/Cargo.lock b/Cargo.lock index d525c83..4b1f7f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -539,11 +539,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] diff --git a/src/cdp.rs b/src/cdp.rs index be8d865..e8435d7 100644 --- a/src/cdp.rs +++ b/src/cdp.rs @@ -1677,14 +1677,8 @@ fn endpoint_owner_process_ids(port: u16, process_id: u32) -> Result Result, CdpError> { +fn list_all_process_ids() -> Result, CdpError> { const MAX_PROCESS_COUNT: usize = 1024 * 1024; - const MAX_DESCRIPTOR_BYTES: usize = 16 * 1024 * 1024; - const SOCKET_INFO_BYTES: usize = 792; - const PROC_PIDFDSOCKETINFO: i32 = 3; - const SOCKINFO_TCP: i32 = 2; - const TCP_LISTEN: i32 = 1; - let capacity = unsafe { libc::proc_listallpids(std::ptr::null_mut(), 0) }; let capacity = usize::try_from(capacity) .ok() @@ -1703,77 +1697,97 @@ fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result 0) { - let descriptor_bytes = unsafe { - libc::proc_pidinfo( - process_id, - libc::PROC_PIDLISTFDS, - 0, - std::ptr::null_mut(), - 0, - ) - }; - let Ok(descriptor_bytes) = usize::try_from(descriptor_bytes) else { - continue; - }; - if descriptor_bytes == 0 || descriptor_bytes > MAX_DESCRIPTOR_BYTES { + process_ids.truncate(count); + Ok(process_ids) +} + +#[cfg(target_os = "macos")] +fn process_has_tcp_listen_socket(process_id: i32, port: u16) -> Result { + const MAX_DESCRIPTOR_BYTES: usize = 16 * 1024 * 1024; + const SOCKET_INFO_BYTES: usize = 792; + const PROC_PIDFDSOCKETINFO: i32 = 3; + const SOCKINFO_TCP: i32 = 2; + const TCP_LISTEN: i32 = 1; + + let descriptor_bytes = unsafe { + libc::proc_pidinfo( + process_id, + libc::PROC_PIDLISTFDS, + 0, + std::ptr::null_mut(), + 0, + ) + }; + let Ok(descriptor_bytes) = usize::try_from(descriptor_bytes) else { + return Ok(false); + }; + if descriptor_bytes == 0 || descriptor_bytes > MAX_DESCRIPTOR_BYTES { + return Ok(false); + } + let descriptor_capacity = descriptor_bytes + .checked_add(64 * 8) + .filter(|bytes| *bytes <= MAX_DESCRIPTOR_BYTES) + .ok_or(CdpError::StaleTarget)?; + let mut descriptors = vec![0u8; descriptor_capacity]; + let requested = i32::try_from(descriptors.len()).map_err(|_| CdpError::StaleTarget)?; + let written = unsafe { + libc::proc_pidinfo( + process_id, + libc::PROC_PIDLISTFDS, + 0, + descriptors.as_mut_ptr().cast(), + requested, + ) + }; + let Ok(written) = usize::try_from(written) else { + return Ok(false); + }; + if written >= descriptors.len() || written % 8 != 0 { + return Err(CdpError::StaleTarget); + } + #[allow(clippy::chunks_exact_to_as_chunks)] + for descriptor in descriptors[..written].chunks_exact(8) { + let file_descriptor = + i32::from_ne_bytes(descriptor[..4].try_into().map_err(|_| CdpError::Protocol)?); + let descriptor_type = u32::from_ne_bytes( + descriptor[4..8] + .try_into() + .map_err(|_| CdpError::Protocol)?, + ); + if descriptor_type != libc::PROX_FDTYPE_SOCKET as u32 { continue; } - let descriptor_capacity = descriptor_bytes - .checked_add(64 * 8) - .filter(|bytes| *bytes <= MAX_DESCRIPTOR_BYTES) - .ok_or(CdpError::StaleTarget)?; - let mut descriptors = vec![0u8; descriptor_capacity]; - let requested = i32::try_from(descriptors.len()).map_err(|_| CdpError::StaleTarget)?; + let mut socket = [0u8; SOCKET_INFO_BYTES]; let written = unsafe { - libc::proc_pidinfo( + libc::proc_pidfdinfo( process_id, - libc::PROC_PIDLISTFDS, - 0, - descriptors.as_mut_ptr().cast(), - requested, + file_descriptor, + PROC_PIDFDSOCKETINFO, + socket.as_mut_ptr().cast(), + SOCKET_INFO_BYTES as i32, ) }; - let Ok(written) = usize::try_from(written) else { + if written != SOCKET_INFO_BYTES as i32 + || i32::from_ne_bytes(socket[256..260].try_into().unwrap_or_default()) != SOCKINFO_TCP + || i32::from_ne_bytes(socket[344..348].try_into().unwrap_or_default()) != TCP_LISTEN + || socket[288] & 1 == 0 + || socket[324..328] != Ipv4Addr::LOCALHOST.octets() + || u16::from_be_bytes(socket[268..270].try_into().unwrap_or_default()) != port + { continue; - }; - if written >= descriptors.len() || written % 8 != 0 { - return Err(CdpError::StaleTarget); } - for descriptor in descriptors[..written].chunks_exact(8) { - let file_descriptor = - i32::from_ne_bytes(descriptor[..4].try_into().map_err(|_| CdpError::Protocol)?); - let descriptor_type = u32::from_ne_bytes( - descriptor[4..8] - .try_into() - .map_err(|_| CdpError::Protocol)?, - ); - if descriptor_type != libc::PROX_FDTYPE_SOCKET as u32 { - continue; - } - let mut socket = [0u8; SOCKET_INFO_BYTES]; - let written = unsafe { - libc::proc_pidfdinfo( - process_id, - file_descriptor, - PROC_PIDFDSOCKETINFO, - socket.as_mut_ptr().cast(), - SOCKET_INFO_BYTES as i32, - ) - }; - if written != SOCKET_INFO_BYTES as i32 - || i32::from_ne_bytes(socket[256..260].try_into().unwrap_or_default()) - != SOCKINFO_TCP - || i32::from_ne_bytes(socket[344..348].try_into().unwrap_or_default()) != TCP_LISTEN - || socket[288] & 1 == 0 - || socket[324..328] != Ipv4Addr::LOCALHOST.octets() - || u16::from_be_bytes(socket[268..270].try_into().unwrap_or_default()) != port - { - continue; - } + return Ok(true); + } + Ok(false) +} + +#[cfg(target_os = "macos")] +fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result, CdpError> { + let process_ids = list_all_process_ids()?; + let mut owners = BTreeSet::new(); + for process_id in process_ids.into_iter().filter(|id| *id > 0) { + if process_has_tcp_listen_socket(process_id, port)? { owners.insert(u32::try_from(process_id).map_err(|_| CdpError::Protocol)?); - break; } } Ok(owners) @@ -1851,6 +1865,7 @@ fn endpoint_owner_process_ids(port: u16, _process_id: u32) -> Result std::process::Output { .stderr(Stdio::piped()) .spawn() .expect("spawn CLI"); -if let Some(mut child_stdin) = child.stdin.take() { + if let Some(mut child_stdin) = child.stdin.take() { let _ = child_stdin.write_all(stdin.as_bytes()); } child.wait_with_output().expect("CLI output")