Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for TCP and UDP #13

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ on:
pull_request:
branches:
- '*'
workflow_dispatch:

env:
CARGO_TERM_COLOR: always
39 changes: 32 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ pub struct Listener {
pub process: Process,
/// The TCP socket this listener is listening on.
pub socket: SocketAddr,
pub protocol: Protocol,
}

/// A process, characterized by its PID and name.
@@ -26,6 +27,11 @@ pub struct Process {
pub name: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Protocol {
TCP,
UDP,
}
/// Returns all the [Listener]s.
///
/// # Errors
@@ -144,9 +150,13 @@ pub fn get_ports_by_process_name(name: &str) -> Result<HashSet<u16>> {
}

impl Listener {
fn new(pid: u32, name: String, socket: SocketAddr) -> Self {
fn new(pid: u32, name: String, socket: SocketAddr, protocol: Protocol) -> Self {
let process = Process::new(pid, name);
Self { process, socket }
Self {
process,
socket,
protocol,
}
}
}

@@ -158,9 +168,13 @@ impl Process {

impl Display for Listener {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Listener { process, socket } = self;
let Listener {
process,
socket,
protocol,
} = self;
let process = process.to_string();
write!(f, "{process:<55} Socket: {socket}",)
write!(f, "{process:<55} Socket: {socket:<30} Protocol: {protocol}",)
}
}

@@ -171,22 +185,32 @@ impl Display for Process {
}
}

impl Display for Protocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Protocol::TCP => write!(f, "TCP"),
Protocol::UDP => write!(f, "UDP"),
}
}
}

#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};

use crate::{Listener, Process};
use crate::{Listener, Process, Protocol};

#[test]
fn test_v4_listener_to_string() {
let listener = Listener::new(
455,
"rapportd".to_string(),
SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 51189),
Protocol::TCP,
);
assert_eq!(
listener.to_string(),
"PID: 455 Process name: rapportd Socket: 0.0.0.0:51189"
"PID: 455 Process name: rapportd Socket: 0.0.0.0:51189 Protocol: TCP"
);
}

@@ -196,10 +220,11 @@ mod tests {
160,
"mysqld".to_string(),
SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 3306),
Protocol::TCP,
);
assert_eq!(
listener.to_string(),
"PID: 160 Process name: mysqld Socket: [::]:3306"
"PID: 160 Process name: mysqld Socket: [::]:3306 Protocol: TCP"
);
}

7 changes: 6 additions & 1 deletion src/platform/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -19,7 +19,12 @@ pub(crate) fn get_all() -> crate::Result<HashSet<Listener>> {

for tcp_listener in TcpListener::get_all()? {
if let Some(p) = inode_proc_map.get(&tcp_listener.inode()) {
let listener = Listener::new(p.pid(), p.name(), tcp_listener.local_addr());
let listener = Listener::new(
p.pid(),
p.name(),
tcp_listener.local_addr(),
tcp_listener.protocol(),
);
listeners.insert(listener);
}
}
55 changes: 42 additions & 13 deletions src/platform/linux/tcp_listener.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::Protocol;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
@@ -7,10 +8,11 @@ use std::str::FromStr;
pub(super) struct TcpListener {
local_addr: SocketAddr,
inode: u64,
protocol: Protocol,
}

impl TcpListener {
const LISTEN_STATE: &'static str = "0A";
// const LISTEN_STATE: &'static str = "0A";

pub(super) fn local_addr(&self) -> SocketAddr {
self.local_addr
@@ -20,30 +22,50 @@ impl TcpListener {
self.inode
}

pub(super) fn protocol(&self) -> Protocol {
self.protocol
}

pub(super) fn get_all() -> crate::Result<Vec<TcpListener>> {
let mut table = Vec::new();
let tcp_table = File::open("/proc/net/tcp")?;
for line in BufReader::new(tcp_table).lines().map_while(Result::ok) {
if let Ok(l) = TcpListener::from_tcp_table_entry(&line) {
if let Ok(l) = TcpListener::from_protocol_table_entry(&line, Protocol::TCP) {
table.push(l);
}
}

let tcp6_table = File::open("/proc/net/tcp6")?;
for line in BufReader::new(tcp6_table).lines().map_while(Result::ok) {
if let Ok(l) = TcpListener::from_tcp6_table_entry(&line) {
if let Ok(l) = TcpListener::from_protocolv6_table_entry(&line, Protocol::TCP) {
table.push(l);
}
}

let udp_table = File::open("/proc/net/udp")?;
for line in BufReader::new(udp_table).lines().map_while(Result::ok) {
// the lines/fields for tcp and udp are identical as far as Listeners is concerend
if let Ok(l) = TcpListener::from_protocol_table_entry(&line, Protocol::UDP) {
table.push(l)
}
}

let udp_table = File::open("/proc/net/udp6")?;
for line in BufReader::new(udp_table).lines().map_while(Result::ok) {
// the lines/fields for tcp and udp are identical as far as Listeners is concerend
if let Ok(l) = TcpListener::from_protocolv6_table_entry(&line, Protocol::UDP) {
table.push(l)
}
}
Ok(table)
}

fn from_tcp_table_entry(line: &str) -> crate::Result<Self> {
fn from_protocol_table_entry(line: &str, protocol: Protocol) -> crate::Result<Self> {
let mut s = line.split_whitespace();

let local_addr_hex = s.nth(1).ok_or("Failed to get local address")?;
let Some(Self::LISTEN_STATE) = s.nth(1) else {
return Err("Not a listening socket".into());
};
// consider all states
let _ = s.nth(1).ok_or("Failed to get state")?;

let local_ip_port = local_addr_hex
.split(':')
@@ -59,10 +81,14 @@ impl TcpListener {
let inode_n = s.nth(5).ok_or("Failed to get inode")?;
let inode = u64::from_str(inode_n)?;

Ok(Self { local_addr, inode })
Ok(Self {
local_addr,
inode,
protocol,
})
}

fn from_tcp6_table_entry(line: &str) -> crate::Result<Self> {
fn from_protocolv6_table_entry(line: &str, protocol: Protocol) -> crate::Result<Self> {
#[cfg(target_endian = "little")]
let read_endian = u32::from_le_bytes;
#[cfg(target_endian = "big")]
@@ -71,9 +97,8 @@ impl TcpListener {
let mut s = line.split_whitespace();

let local_addr_hex = s.nth(1).ok_or("Failed to get local address")?;
let Some(Self::LISTEN_STATE) = s.nth(1) else {
return Err("Not a listening socket".into());
};
// consider all states
let _ = s.nth(1).ok_or("Failed to get state")?;

let mut local_ip_port = local_addr_hex.split(':');

@@ -108,6 +133,10 @@ impl TcpListener {
let inode_n = s.nth(5).ok_or("Failed to get inode")?;
let inode = u64::from_str(inode_n)?;

Ok(Self { local_addr, inode })
Ok(Self {
local_addr,
inode,
protocol,
})
}
}
41 changes: 32 additions & 9 deletions src/platform/macos/c_socket_fd_info.rs
Original file line number Diff line number Diff line change
@@ -3,8 +3,10 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

use byteorder::{ByteOrder, NetworkEndian};

use crate::platform::macos::statics::SOCKET_STATE_LISTEN;
use crate::platform::macos::tcp_listener::TcpListener;
use crate::Protocol;

use super::statics::{IPPROTO_TCP, IPPROTO_UDP};

#[repr(C)]
pub(super) struct CSocketFdInfo {
@@ -16,18 +18,31 @@ impl CSocketFdInfo {
pub(super) fn to_tcp_listener(&self) -> crate::Result<TcpListener> {
let sock_info = self.psi;
let family = sock_info.soi_family;
let transport_protocol = sock_info.soi_protocol;

let tcp_in = unsafe { sock_info.soi_proto.pri_tcp };
let general_sock_info = unsafe {
match transport_protocol {
IPPROTO_TCP => sock_info.soi_proto.pri_tcp.tcpsi_ini,
IPPROTO_UDP => sock_info.soi_proto.pri_in,
_ => return Err("Unsupported protocol".into()),
}
};

if tcp_in.tcpsi_state != SOCKET_STATE_LISTEN {
return Err("Socket is not in listen state".into());
}
// if tcp, do not filter on state (get em all)
// if tcp_in.tcpsi_state != SOCKET_STATE_LISTEN && ip_protocol == IPPROT_TCP {
// return Err("Socket is not in listening state".into());
// }

let tcp_sockaddr_in = tcp_in.tcpsi_ini;
let lport_bytes: [u8; 4] = i32::to_le_bytes(tcp_sockaddr_in.insi_lport);
let local_address = Self::get_local_addr(family, tcp_sockaddr_in)?;
// let tcp_sockaddr_in = tcp_in.tcpsi_ini;
let lport_bytes: [u8; 4] = 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 = TcpListener::new(local_address, NetworkEndian::read_u16(&lport_bytes));
let socket_info = TcpListener::new(
local_address,
NetworkEndian::read_u16(&lport_bytes),
protocol,
);

Ok(socket_info)
}
@@ -49,6 +64,14 @@ impl CSocketFdInfo {
_ => Err("Unsupported socket family".into()),
}
}

fn get_protocol(family: c_int, ip_protocol: c_int) -> crate::Result<Protocol> {
match (family, ip_protocol) {
(2 | 30, IPPROTO_TCP) => Ok(Protocol::TCP),
(2 | 30, IPPROTO_UDP) => Ok(Protocol::UDP),
(_, _) => Err("unsupported protocol".into()),
}
}
}

#[repr(C)]
7 changes: 6 additions & 1 deletion src/platform/macos/mod.rs
Original file line number Diff line number Diff line change
@@ -23,7 +23,12 @@ pub(crate) fn get_all() -> crate::Result<HashSet<Listener>> {
for fd in SocketFd::get_all_of_pid(pid).iter().flatten() {
if let Ok(tcp_listener) = TcpListener::from_pid_fd(pid, fd) {
if let Ok(ProcName(name)) = ProcName::from_pid(pid) {
let listener = Listener::new(pid.as_u_32()?, name, tcp_listener.socket_addr());
let listener = Listener::new(
pid.as_u_32()?,
name,
tcp_listener.socket_addr(),
tcp_listener.protocol(),
);
listeners.insert(listener);
}
}
4 changes: 3 additions & 1 deletion src/platform/macos/statics.rs
Original file line number Diff line number Diff line change
@@ -4,5 +4,7 @@ pub(super) const PROC_ALL_PIDS: u32 = 1;
pub(super) const PROC_PID_LIST_FDS: c_int = 1;
pub(super) const PROC_PID_FD_SOCKET_INFO: c_int = 3;
pub(super) const FD_TYPE_SOCKET: u32 = 2;
pub(super) const SOCKET_STATE_LISTEN: c_int = 1;
// pub(super) const SOCKET_STATE_LISTEN: c_int = 1;
pub(super) const PROC_PID_PATH_INFO_MAXSIZE: usize = 4096;
pub(super) const IPPROTO_TCP: c_int = 6;
pub(super) const IPPROTO_UDP: c_int = 17;
20 changes: 16 additions & 4 deletions src/platform/macos/tcp_listener.rs
Original file line number Diff line number Diff line change
@@ -9,16 +9,28 @@ use crate::platform::macos::proc_pid::ProcPid;
use crate::platform::macos::socket_fd::SocketFd;
use crate::platform::macos::statics::PROC_PID_FD_SOCKET_INFO;

use crate::Protocol;

#[derive(Debug)]
pub(super) struct TcpListener(SocketAddr);
pub(super) struct TcpListener {
local_addr: SocketAddr,
protocol: Protocol,
}

impl TcpListener {
pub(super) fn new(addr: IpAddr, port: u16) -> Self {
TcpListener(SocketAddr::new(addr, port))
pub(super) fn new(addr: IpAddr, port: u16, protocol: Protocol) -> Self {
TcpListener {
local_addr: SocketAddr::new(addr, port),
protocol,
}
}

pub(super) fn socket_addr(&self) -> SocketAddr {
self.0
self.local_addr
}

pub(super) fn protocol(&self) -> Protocol {
self.protocol
}

pub(super) fn from_pid_fd(pid: ProcPid, fd: &SocketFd) -> crate::Result<Self> {
13 changes: 13 additions & 0 deletions src/platform/windows/c_iphlpapi.rs
Original file line number Diff line number Diff line change
@@ -12,3 +12,16 @@ extern "system" {
Reserved: c_ulong,
) -> c_ulong;
}

#[allow(non_snake_case)]
#[link(name = "iphlpapi")]
extern "system" {
pub(super) fn GetExtendedUdpTable(
pUdpTable: *mut c_void,
pdwSize: *mut c_ulong,
bOrder: c_int,
ulAf: c_ulong,
TableClass: c_ulong,
Reserved: c_ulong,
) -> c_ulong;
}
2 changes: 2 additions & 0 deletions src/platform/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@ mod statics;
mod tcp6_table;
mod tcp_listener;
mod tcp_table;
mod udp6_table;
mod udp_table;

pub(crate) fn get_all() -> crate::Result<HashSet<Listener>> {
let mut listeners = HashSet::new();
133 changes: 115 additions & 18 deletions src/platform/windows/socket_table.rs
Original file line number Diff line number Diff line change
@@ -5,10 +5,16 @@ use crate::platform::target_os::c_iphlpapi::GetExtendedTcpTable;
use crate::platform::target_os::statics::FALSE;
use crate::platform::target_os::tcp_listener::TcpListener;
use crate::platform::windows::statics::{
AF_INET, AF_INET6, ERROR_INSUFFICIENT_BUFFER, LISTEN, NO_ERROR, TCP_TABLE_OWNER_PID_ALL,
AF_INET, AF_INET6, ERROR_INSUFFICIENT_BUFFER, NO_ERROR, TCP_TABLE_OWNER_PID_ALL,
};
use crate::platform::windows::tcp6_table::Tcp6Table;
use crate::platform::windows::tcp_table::TcpTable;
use crate::platform::windows::udp6_table::Udp6Table;
use crate::platform::windows::udp_table::UdpTable;
use crate::Protocol;

use super::c_iphlpapi::GetExtendedUdpTable;
use super::statics::UDP_TABLE_OWNER_PID;

pub(super) trait SocketTable {
fn get_table() -> crate::Result<Vec<u8>>;
@@ -32,15 +38,13 @@ impl SocketTable for TcpTable {
let table = unsafe { &*(table.as_ptr().cast::<TcpTable>()) };
let rows_ptr = std::ptr::addr_of!(table.rows[0]);
let row = unsafe { &*rows_ptr.add(index) };
if row.state == LISTEN {
Some(TcpListener::new(
IpAddr::V4(Ipv4Addr::from(u32::from_be(row.local_addr))),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
))
} else {
None
}
// if row.state == LISTEN { // get all states
Some(TcpListener::new(
IpAddr::V4(Ipv4Addr::from(u32::from_be(row.local_addr))),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
Protocol::TCP,
))
}
}

@@ -60,16 +64,109 @@ impl SocketTable for Tcp6Table {
let table = unsafe { &*(table.as_ptr().cast::<Tcp6Table>()) };
let rows_ptr = std::ptr::addr_of!(table.rows[0]);
let row = unsafe { &*rows_ptr.add(index) };
if row.state == LISTEN {
Some(TcpListener::new(
IpAddr::V6(Ipv6Addr::from(row.local_addr)),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
))
} else {
None
// if row.state == LISTEN {
Some(TcpListener::new(
IpAddr::V6(Ipv6Addr::from(row.local_addr)),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
Protocol::TCP,
))
}
}

impl SocketTable for UdpTable {
fn get_table() -> crate::Result<Vec<u8>> {
get_udp_table(AF_INET)
}

fn get_rows_count(table: &[u8]) -> usize {
#[allow(clippy::cast_ptr_alignment)]
let table = unsafe { &*(table.as_ptr().cast::<UdpTable>()) };
table.rows_count as usize
}

fn get_tcp_listener(table: &[u8], index: usize) -> Option<TcpListener> {
#[allow(clippy::cast_ptr_alignment)]
let table = unsafe { &*(table.as_ptr().cast::<UdpTable>()) };
let rows_ptr = std::ptr::addr_of!(table.rows[0]);
let row = unsafe { &*rows_ptr.add(index) };
Some(TcpListener::new(
IpAddr::V4(Ipv4Addr::from(u32::from_be(row.local_addr))),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
Protocol::UDP,
))
}
}

impl SocketTable for Udp6Table {
fn get_table() -> crate::Result<Vec<u8>> {
get_udp_table(AF_INET6)
}

fn get_rows_count(table: &[u8]) -> usize {
#[allow(clippy::cast_ptr_alignment)]
let table = unsafe { &*(table.as_ptr().cast::<Udp6Table>()) };
table.rows_count as usize
}

fn get_tcp_listener(table: &[u8], index: usize) -> Option<TcpListener> {
#[allow(clippy::cast_ptr_alignment)]
let table = unsafe { &*(table.as_ptr().cast::<Udp6Table>()) };
let rows_ptr = std::ptr::addr_of!(table.rows[0]);
let row = unsafe { &*rows_ptr.add(index) };
Some(TcpListener::new(
IpAddr::V6(Ipv6Addr::from(row.local_addr)),
u16::from_be(u16::try_from(row.local_port).ok()?),
row.owning_pid,
Protocol::UDP,
))
}
}

// fn get_protocol_table(address_family: c_ulong, protocol: Protocol) -> crate::Result<Vec<u8>> {
// match protocol {
// Protocol::TCP | Protocol::TCP6 => get_tcp_table(address_family),
// Protocol::UDP | Protocol::UDP6 => get_udp_table(address_family),
// }
// }

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(
std::ptr::null_mut(),
&mut table_size,
FALSE,
address_family,
UDP_TABLE_OWNER_PID,
0,
)
};
let mut table = Vec::<u8>::new();
let mut iterations = 0;
while err_code == ERROR_INSUFFICIENT_BUFFER {
table = Vec::<u8>::with_capacity(table_size as usize);
err_code = unsafe {
GetExtendedUdpTable(
table.as_mut_ptr().cast::<c_void>(),
&mut table_size,
FALSE,
address_family,
UDP_TABLE_OWNER_PID,
0,
)
};
iterations += 1;
if iterations > 100 {
return Err("Failed to allocate buffer".into());
}
}
if err_code == NO_ERROR {
Ok(table)
} else {
Err("Failed to get UDP table".into())
}
}

fn get_tcp_table(address_family: c_ulong) -> crate::Result<Vec<u8>> {
3 changes: 2 additions & 1 deletion src/platform/windows/statics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::ffi::{c_int, c_ulong};

pub(super) const TCP_TABLE_OWNER_PID_ALL: c_ulong = 5;
pub(super) const UDP_TABLE_OWNER_PID: c_ulong = 1;
pub(super) const FALSE: c_int = 0;
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;
pub(super) const LISTEN: c_ulong = 2;
// pub(super) const LISTEN: c_ulong = 2;
12 changes: 10 additions & 2 deletions src/platform/windows/tcp_listener.rs
Original file line number Diff line number Diff line change
@@ -11,12 +11,17 @@ use crate::platform::windows::socket_table::SocketTable;
use crate::platform::windows::tcp6_table::Tcp6Table;
use crate::platform::windows::tcp_table::TcpTable;
use crate::Listener;
use crate::Protocol;

use super::udp6_table::Udp6Table;
use super::udp_table::UdpTable;

#[derive(Debug)]
pub(super) struct TcpListener {
local_addr: IpAddr,
local_port: u16,
pid: u32,
protocol: Protocol,
}

impl TcpListener {
@@ -25,6 +30,8 @@ impl TcpListener {
.into_iter()
.flatten()
.chain(Self::table_entries::<Tcp6Table>().into_iter().flatten())
.chain(Self::table_entries::<UdpTable>().into_iter().flatten())
.chain(Self::table_entries::<Udp6Table>().into_iter().flatten())
.collect()
}

@@ -39,11 +46,12 @@ impl TcpListener {
Ok(tcp_listeners)
}

pub(super) fn new(local_addr: IpAddr, local_port: u16, pid: u32) -> Self {
pub(super) fn new(local_addr: IpAddr, local_port: u16, pid: u32, protocol: Protocol) -> Self {
Self {
local_addr,
local_port,
pid,
protocol,
}
}

@@ -79,6 +87,6 @@ impl TcpListener {
pub(super) fn to_listener(&self) -> Option<Listener> {
let socket = SocketAddr::new(self.local_addr, self.local_port);
let pname = self.pname()?;
Some(Listener::new(self.pid, pname, socket))
Some(Listener::new(self.pid, pname, socket, self.protocol))
}
}
18 changes: 18 additions & 0 deletions src/platform/windows/udp6_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::ffi::c_uchar;
use std::os::raw::c_ulong;

#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct Udp6Table {
pub(super) rows_count: c_ulong,
pub(super) rows: [Udp6Row; 1],
}

#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct Udp6Row {
pub(super) local_addr: [c_uchar; 16],
local_scope_id: c_ulong,
pub(super) local_port: c_ulong,
pub(super) owning_pid: c_ulong,
}
16 changes: 16 additions & 0 deletions src/platform/windows/udp_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::ffi::c_ulong;

#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct UdpTable {
pub(super) rows_count: c_ulong,
pub(super) rows: [UdpRow; 1],
}

#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub(super) struct UdpRow {
pub(super) local_addr: c_ulong,
pub(super) local_port: c_ulong,
pub(super) owning_pid: c_ulong,
}
167 changes: 151 additions & 16 deletions tests/integration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use std::net::SocketAddr;
use std::str::FromStr;

use http_test_server::TestServer;
use listeners::{Listener, Process, Protocol};
use serial_test::serial;

use listeners::{Listener, Process};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, TcpListener, UdpSocket};
use std::str::FromStr;

#[test]
#[serial]
@@ -16,19 +14,32 @@ fn test_consistency() {
let listeners = listeners::get_all().unwrap();
assert!(!listeners.is_empty());

// check that the listeners retrieved by the different APIs are consistent
for l in listeners {
println!("{l}");
// maybe there is no udp connection
if let Some(l_udp) = listeners.iter().find(|l| l.protocol == Protocol::UDP) {
println!("UDP: {l_udp}");

let ports_by_pid = listeners::get_ports_by_pid(l.process.pid).unwrap();
assert!(ports_by_pid.contains(&l.socket.port()));
let ports_by_name = listeners::get_ports_by_process_name(&l_udp.process.name).unwrap();
assert!(ports_by_name.contains(&l_udp.socket.port()));

let ports_by_name = listeners::get_ports_by_process_name(&l.process.name).unwrap();
assert!(ports_by_name.contains(&l.socket.port()));
let processes_by_port = listeners::get_processes_by_port(l_udp.socket.port()).unwrap();
assert!(processes_by_port.contains(&l_udp.process));

let processes_by_port = listeners::get_processes_by_port(l.socket.port()).unwrap();
assert!(processes_by_port.contains(&l.process));
}
let ports_by_pid = listeners::get_ports_by_pid(l_udp.process.pid).unwrap();
assert!(ports_by_pid.contains(&l_udp.socket.port()));
};

if let Some(l_tcp) = listeners.iter().find(|l| l.protocol == Protocol::TCP) {
println!("TCP: {l_tcp}");

let ports_by_name = listeners::get_ports_by_process_name(&l_tcp.process.name).unwrap();
assert!(ports_by_name.contains(&l_tcp.socket.port()));

let processes_by_port = listeners::get_processes_by_port(l_tcp.socket.port()).unwrap();
assert!(processes_by_port.contains(&l_tcp.process));

let ports_by_pid = listeners::get_ports_by_pid(l_tcp.process.pid).unwrap();
assert!(ports_by_pid.contains(&l_tcp.socket.port()));
};
}

#[test]
@@ -73,7 +84,131 @@ fn test_http_server() {
pid: http_server_pid,
name: http_server_name
},
socket: SocketAddr::from_str(&format!("127.0.0.1:{http_server_port}")).unwrap()
socket: SocketAddr::from_str(&format!("127.0.0.1:{http_server_port}")).unwrap(),
protocol: Protocol::TCP
}
);
}

#[test]
#[serial]
fn test_dns() {
let dns_port = 53;
let all = listeners::get_all().unwrap();
let found = all.iter().any(|l| {
l.socket.port() == dns_port && l.protocol == Protocol::UDP || l.protocol == Protocol::TCP
});
assert!(found);
}

#[test]
#[serial]
fn test_udp() {
let mut opened_ports: Vec<u16> = Vec::new();
let mut sockets: Vec<UdpSocket> = Vec::new();

let ip_addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let mut current_port = 1500;
let num_sockets = 10;

for _ in 0..num_sockets {
let socket = UdpSocket::bind(SocketAddr::new(ip_addr, current_port)).unwrap();
current_port = socket.local_addr().unwrap().port();
opened_ports.push(current_port);
sockets.push(socket);
current_port += 1;
}

let all_listeners = listeners::get_all().unwrap();
let all_found = opened_ports.iter().all(|p| {
all_listeners
.iter()
.any(|l| l.socket.port() == *p && l.protocol == Protocol::UDP)
});

assert!(all_found);
}

#[test]
#[serial]
fn test_tcp() {
let mut opened_ports: Vec<u16> = Vec::new();
let mut sockets: Vec<TcpListener> = Vec::new();

let ip_addr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let mut current_port = 4500;
let num_sockets = 10;

for _ in 0..num_sockets {
let socket = TcpListener::bind(SocketAddr::new(ip_addr, current_port)).unwrap();
current_port = socket.local_addr().unwrap().port();
opened_ports.push(current_port);
sockets.push(socket);
current_port += 1;
}

let all_listeners = listeners::get_all().unwrap();
let all_found = opened_ports.iter().all(|p| {
all_listeners
.iter()
.any(|l| l.socket.port() == *p && l.protocol == Protocol::TCP)
});

assert!(all_found);
}

#[test]
#[serial]
fn test_tcp6() {
let mut opened_ports: Vec<u16> = Vec::new();
let mut sockets: Vec<TcpListener> = Vec::new();

let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let mut current_port = 5600;
let num_sockets = 10;

for _ in 0..num_sockets {
let socket = TcpListener::bind(SocketAddr::new(ip_addr, current_port)).unwrap();
current_port = socket.local_addr().unwrap().port();
opened_ports.push(current_port);
sockets.push(socket);
current_port += 1;
}

let all_listeners = listeners::get_all().unwrap();
let all_found = opened_ports.iter().all(|p| {
all_listeners
.iter()
.any(|l| l.socket.port() == *p && l.protocol == Protocol::TCP)
});

assert!(all_found);
}

#[test]
#[serial]
fn test_udp6() {
let mut opened_ports: Vec<u16> = Vec::new();
let mut sockets: Vec<UdpSocket> = Vec::new();

let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
let mut current_port = 5600;
let num_sockets = 10;

for _ in 0..num_sockets {
let socket = UdpSocket::bind(SocketAddr::new(ip_addr, current_port)).unwrap();
current_port = socket.local_addr().unwrap().port();
opened_ports.push(current_port);
sockets.push(socket);
current_port += 1;
}

let all_listeners = listeners::get_all().unwrap();
let all_found = opened_ports.iter().all(|p| {
all_listeners
.iter()
.any(|l| l.socket.port() == *p && l.protocol == Protocol::UDP)
});

assert!(all_found);
}