Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All releases with the relative changes are documented in this file.

## [UNRELEASED]
### Changed
- Lighten the dependency tree: drop `byteorder` on macOS, and use `windows-sys` instead of `windows` on Windows ([#60](https://github.com/GyulyVGC/listeners/pull/60))

## [0.6.1] - 2026-08-02
### Fixed
- Correctly report IPv4-mapped IPv6 addresses on macOS ([#57](https://github.com/GyulyVGC/listeners/pull/57) — fixes [#56](https://github.com/GyulyVGC/listeners/issues/56))
Expand Down
5 changes: 1 addition & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,13 @@ print_stdout = "warn"
print_stderr = "warn"

[target.'cfg(target_os = "windows")'.dependencies]
windows = { version = "0.62", features = [
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Threading",
"Win32_NetworkManagement_IpHelper"
] }

[target.'cfg(target_os = "macos")'.dependencies]
byteorder = "1.5"

[target.'cfg(target_os = "linux")'.dependencies]
rustix = {version = "1.1", features = ["fs"]}

Expand Down
12 changes: 4 additions & 8 deletions src/platform/macos/c_socket_fd_info.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use std::ffi::{c_char, c_int, c_longlong, c_short, c_uchar, c_uint, c_ushort};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use byteorder::{ByteOrder, NetworkEndian};

use crate::platform::macos::proto_listener::ProtoListener;
use crate::{Protocol, SocketState};

Expand Down Expand Up @@ -34,13 +32,13 @@ impl CSocketFdInfo {
}
};

let lport_bytes: [u8; 4] = i32::to_le_bytes(general_sock_info.insi_lport);
let [lport_hi, lport_lo, ..] = i32::to_le_bytes(general_sock_info.insi_lport);
let local_address = Self::get_local_addr(family, general_sock_info)?;
let protocol = Self::get_protocol(family, transport_protocol)?;

let socket_info = ProtoListener::new(
local_address,
NetworkEndian::read_u16(&lport_bytes),
u16::from_be_bytes([lport_hi, lport_lo]),
protocol,
state,
);
Expand Down Expand Up @@ -82,10 +80,8 @@ impl CSocketFdInfo {

/// The 16-byte `ina_6` slot.
fn v6_slot(sock_info: &InSockinfo) -> Ipv6Addr {
let addr = unsafe { &sock_info.insi_laddr.ina_6.__u6_addr.__u6_addr8 };
let mut ipv6_addr = [0_u16; 8];
NetworkEndian::read_u16_into(addr, &mut ipv6_addr);
Ipv6Addr::from(ipv6_addr)
let addr = unsafe { sock_info.insi_laddr.ina_6.__u6_addr.__u6_addr8 };
Ipv6Addr::from(addr)
}

fn get_protocol(family: c_int, ip_protocol: c_int) -> crate::Result<Protocol> {
Expand Down
77 changes: 46 additions & 31 deletions src/platform/windows/proto_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ use crate::platform::windows::tcp_table::TcpTable;
use crate::platform::windows::tcp6_table::Tcp6Table;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ffi::CStr;
use std::mem::size_of;
use std::mem::zeroed;
use std::net::{IpAddr, SocketAddr};
use std::os::windows::ffi::OsStringExt;
use std::path::Path;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::System::Diagnostics::ToolHelp::{
use windows_sys::Win32::Foundation::{CloseHandle, FALSE, HANDLE, INVALID_HANDLE_VALUE};
use windows_sys::Win32::System::Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, PROCESSENTRY32, Process32First, Process32Next, TH32CS_SNAPPROCESS,
};
use windows::Win32::System::Threading::{
OpenProcess, PROCESS_NAME_FORMAT, PROCESS_QUERY_LIMITED_INFORMATION, QueryFullProcessImageNameW,
use windows_sys::Win32::System::Threading::{
OpenProcess, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION, QueryFullProcessImageNameW,
};
use windows::core::PCSTR;
use windows::core::PWSTR;

use super::udp_table::UdpTable;
use super::udp6_table::Udp6Table;
Expand Down Expand Up @@ -149,27 +148,46 @@ impl PidNamePathCache {
}
}

fn is_invalid(handle: HANDLE) -> bool {
handle.is_null() || handle == INVALID_HANDLE_VALUE
}

/// Takes a snapshot of the running processes.
fn process_snapshot() -> Option<HANDLE> {
let handle = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) };
(!is_invalid(handle)).then_some(handle)
}

/// Reads the `szExeFile` field of a process entry.
///
/// Returns `None` if the field isn't NUL-terminated or isn't valid UTF-8.
fn exe_file(process: &PROCESSENTRY32) -> Option<String> {
let raw = &process.szExeFile;
// SAFETY: `c_char` and `u8` share their layout, and the length is taken from the
// array itself, so the read stays within `szExeFile` even if the OS didn't
// NUL-terminate it.
let bytes = unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<u8>(), raw.len()) };
let name = CStr::from_bytes_until_nul(bytes).ok()?;
name.to_str().ok().map(str::to_owned)
}

fn pname(pid: u32) -> Option<String> {
let h = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0).ok()? };
let dw_size = u32::try_from(size_of::<PROCESSENTRY32>()).ok()?;
let h = process_snapshot()?;

let mut process = unsafe { zeroed::<PROCESSENTRY32>() };
process.dwSize = u32::try_from(size_of::<PROCESSENTRY32>()).ok()?;
process.dwSize = dw_size;

let mut result = None;

if unsafe { Process32First(h, &raw mut process) }.is_ok() {
if unsafe { Process32First(h, &raw mut process) } != FALSE {
loop {
if process.th32ProcessID == pid {
let name = unsafe {
PCSTR(process.szExeFile.as_ptr().cast::<u8>())
.to_string()
.ok()?
};
result = Some(name);
result = exe_file(&process);
break;
}

if unsafe { Process32Next(h, &raw mut process) }.is_err() {
if unsafe { Process32Next(h, &raw mut process) } == FALSE {
break;
}
}
Expand All @@ -184,10 +202,8 @@ fn pname(pid: u32) -> Option<String> {

fn ppath(pid: u32) -> String {
unsafe {
let Ok(handle) = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) else {
return String::new();
};
if handle.is_invalid() {
let handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
if is_invalid(handle) {
return String::new();
}

Expand All @@ -196,13 +212,13 @@ fn ppath(pid: u32) -> String {

let result = QueryFullProcessImageNameW(
handle,
PROCESS_NAME_FORMAT(0),
PWSTR(buffer.as_mut_ptr()),
PROCESS_NAME_WIN32,
buffer.as_mut_ptr(),
&raw mut size,
);
let _ = CloseHandle(handle);

if result.is_err() {
if result == FALSE {
return String::new();
}

Expand All @@ -214,25 +230,24 @@ fn ppath(pid: u32) -> String {
fn pname_collect() -> HashMap<u32, String> {
let mut ret_val = HashMap::default();

let Ok(h) = (unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }) else {
let Ok(dw_size) = u32::try_from(size_of::<PROCESSENTRY32>()) else {
return ret_val;
};

let mut process = unsafe { zeroed::<PROCESSENTRY32>() };
let Ok(dw_size) = u32::try_from(size_of::<PROCESSENTRY32>()) else {
let Some(h) = process_snapshot() else {
return ret_val;
};

let mut process = unsafe { zeroed::<PROCESSENTRY32>() };
process.dwSize = dw_size;

if unsafe { Process32First(h, &raw mut process) }.is_ok() {
if unsafe { Process32First(h, &raw mut process) } != FALSE {
loop {
if let Ok(name) = unsafe { PCSTR(process.szExeFile.as_ptr().cast::<u8>()).to_string() }
{
if let Some(name) = exe_file(&process) {
let id = process.th32ProcessID;
ret_val.insert(id, name);
}

if unsafe { Process32Next(h, &raw mut process) }.is_err() {
if unsafe { Process32Next(h, &raw mut process) } == FALSE {
break;
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/platform/windows/socket_table.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use std::ffi::{c_ulong, c_void};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use super::statics::UDP_TABLE_OWNER_PID;
use crate::Protocol;
use crate::SocketState;
use crate::platform::target_os::proto_listener::ProtoListener;
use crate::platform::windows::statics::{
AF_INET, AF_INET6, ERROR_INSUFFICIENT_BUFFER, NO_ERROR, TCP_TABLE_OWNER_PID_ALL,
};
use crate::platform::windows::statics::{AF_INET, AF_INET6};
use crate::platform::windows::tcp_table::TcpTable;
use crate::platform::windows::tcp6_table::Tcp6Table;
use crate::platform::windows::udp_table::UdpTable;
use crate::platform::windows::udp6_table::Udp6Table;
use windows::Win32::NetworkManagement::IpHelper::{GetExtendedTcpTable, GetExtendedUdpTable};
use windows_sys::Win32::Foundation::{ERROR_INSUFFICIENT_BUFFER, FALSE, NO_ERROR};
use windows_sys::Win32::NetworkManagement::IpHelper::{
GetExtendedTcpTable, GetExtendedUdpTable, TCP_TABLE_OWNER_PID_ALL, UDP_TABLE_OWNER_PID,
};

pub(super) trait SocketTable {
fn get_table() -> crate::Result<Vec<u8>>;
Expand Down Expand Up @@ -180,9 +180,9 @@ fn get_udp_table(address_family: c_ulong) -> crate::Result<Vec<u8>> {
let mut table_size: c_ulong = 0;
let mut err_code = unsafe {
GetExtendedUdpTable(
None,
std::ptr::null_mut(),
&raw mut table_size,
false,
FALSE,
address_family,
UDP_TABLE_OWNER_PID,
0,
Expand All @@ -194,9 +194,9 @@ fn get_udp_table(address_family: c_ulong) -> crate::Result<Vec<u8>> {
table = Vec::<u8>::with_capacity(table_size as usize);
err_code = unsafe {
GetExtendedUdpTable(
Some(table.as_mut_ptr().cast::<c_void>()),
table.as_mut_ptr().cast::<c_void>(),
&raw mut table_size,
false,
FALSE,
address_family,
UDP_TABLE_OWNER_PID,
0,
Expand All @@ -218,9 +218,9 @@ fn get_tcp_table(address_family: c_ulong) -> crate::Result<Vec<u8>> {
let mut table_size: c_ulong = 0;
let mut err_code = unsafe {
GetExtendedTcpTable(
None,
std::ptr::null_mut(),
&raw mut table_size,
false,
FALSE,
address_family,
TCP_TABLE_OWNER_PID_ALL,
0,
Expand All @@ -232,9 +232,9 @@ fn get_tcp_table(address_family: c_ulong) -> crate::Result<Vec<u8>> {
table = Vec::<u8>::with_capacity(table_size as usize);
err_code = unsafe {
GetExtendedTcpTable(
Some(table.as_mut_ptr().cast::<c_void>()),
table.as_mut_ptr().cast::<c_void>(),
&raw mut table_size,
false,
FALSE,
address_family,
TCP_TABLE_OWNER_PID_ALL,
0,
Expand Down
5 changes: 0 additions & 5 deletions src/platform/windows/statics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
use std::ffi::c_ulong;
use windows::Win32::NetworkManagement::IpHelper::{TCP_TABLE_CLASS, UDP_TABLE_CLASS};

pub(super) const TCP_TABLE_OWNER_PID_ALL: TCP_TABLE_CLASS = TCP_TABLE_CLASS(5);
pub(super) const UDP_TABLE_OWNER_PID: UDP_TABLE_CLASS = UDP_TABLE_CLASS(1);
pub(super) const ERROR_INSUFFICIENT_BUFFER: c_ulong = 0x7A;
pub(super) const NO_ERROR: c_ulong = 0;
pub(super) const AF_INET: c_ulong = 2;
pub(super) const AF_INET6: c_ulong = 23;
Loading