summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorremgodow <remgodow@users.noreply.github.com>2020-09-10 18:52:30 +0200
committerGitHub <noreply@github.com>2020-09-10 18:52:30 +0200
commit075693975376be05d228bc45b321b1f6d08021cd (patch)
tree13a4180f8fc43b690aca4cb038405905761eab81 /src
parent16d9758abfd3a8e48b1666496c9fc4ef2db00c3f (diff)
feat(platform): windows build and run support (#180)
* Remove connections vector from OpenSockets, use common OpenSockets implementation based on sysinfo and netstat2. * Replace termion backend with crossterm, which works on Windows as well. * More fixes for windows build. * Remove tui default-features (termion), update unit tests for crossterm. * Windows compilation fixes. * Remove unused get_open_sockets implementations for linux and mac. Fix formatting. * Add build.rs for windows to download and extract Packet.lib from npcap SDK. * Resolve Cargo.lock after merging main. * fix(tests): adjust snapshots new location of the dns resolution * style(clippy): clippy * style(clippy): remove dead code * style(clippy): use write_all in build.rs * style(clippy): remove unused import added by Intellij * style(review): use String instead of str * fix(build): run build.rs only once * fix(regression): skip iface_is_up() filter only for Windows * fix(review): restore per os implementation of get_open_sockets() * fix(cargo): add missing os specific packages * fix: conditional compilation of windows module * fix: compilation errors * fix: missing Protocol::from_str() implementation * style(clippy): remove unused methods Co-authored-by: Aram Drevekenin <aram@poor.dev>
Diffstat (limited to 'src')
-rw-r--r--src/main.rs17
-rw-r--r--src/os/linux.rs39
-rw-r--r--src/os/lsof.rs24
-rw-r--r--src/os/lsof_utils.rs34
-rw-r--r--src/os/mod.rs3
-rw-r--r--src/os/shared.rs26
-rw-r--r--src/os/windows.rs58
-rw-r--r--src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap8
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__traffic_with_host_names.snap8
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap8
-rw-r--r--src/tests/cases/snapshots/ui__truncate_long_hostnames.snap8
-rw-r--r--src/tests/fakes/fake_input.rs1
13 files changed, 141 insertions, 101 deletions
diff --git a/src/main.rs b/src/main.rs
index 966e871..82fd65b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,7 +9,7 @@ mod tests;
use display::{elapsed_time, RawTerminalBackend, Ui};
use network::{
dns::{self, IpTable},
- Connection, LocalSocket, Sniffer, Utilization,
+ LocalSocket, Sniffer, Utilization,
};
use os::OnSigWinch;
@@ -75,9 +75,6 @@ fn main() {
}
fn try_main() -> Result<(), failure::Error> {
- #[cfg(target_os = "windows")]
- compile_error!("Sorry, no implementations for Windows yet :( - PRs welcome!");
-
use os::get_input;
let opts = Opt::from_args();
let os_input = get_input(&opts.interface, !opts.no_resolve)?;
@@ -101,7 +98,6 @@ fn try_main() -> Result<(), failure::Error> {
pub struct OpenSockets {
sockets_to_procs: HashMap<LocalSocket, String>,
- connections: Vec<Connection>,
}
pub struct OsInputOutput {
@@ -190,19 +186,16 @@ where
while running.load(Ordering::Acquire) {
let render_start_time = Instant::now();
let utilization = { network_utilization.lock().unwrap().clone_and_reset() };
- let OpenSockets {
- sockets_to_procs,
- connections,
- } = get_open_sockets();
+ let OpenSockets { sockets_to_procs } = get_open_sockets();
let mut ip_to_host = IpTable::new();
if let Some(dns_client) = dns_client.as_mut() {
ip_to_host = dns_client.cache();
- let unresolved_ips = connections
- .iter()
+ let unresolved_ips = utilization
+ .connections
+ .keys()
.filter(|conn| !ip_to_host.contains_key(&conn.remote_socket.ip))
.map(|conn| conn.remote_socket.ip)
.collect::<Vec<_>>();
-
dns_client.resolve(unresolved_ips);
}
{
diff --git a/src/os/linux.rs b/src/os/linux.rs
index ada04fb..a28c5ac 100644
--- a/src/os/linux.rs
+++ b/src/os/linux.rs
@@ -2,12 +2,11 @@ use ::std::collections::HashMap;
use ::procfs::process::FDTarget;
-use crate::network::{Connection, Protocol};
+use crate::network::{LocalSocket, Protocol};
use crate::OpenSockets;
pub(crate) fn get_open_sockets() -> OpenSockets {
let mut open_sockets = HashMap::new();
- let mut connections = std::vec::Vec::new();
let mut inode_to_procname = HashMap::new();
if let Ok(all_procs) = procfs::process::all_processes() {
@@ -28,14 +27,15 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
tcp.append(&mut tcp6);
}
for entry in tcp.into_iter() {
- let local_port = entry.local_address.port();
- let local_ip = entry.local_address.ip();
- if let (connection, Some(procname)) = (
- Connection::new(entry.remote_address, local_ip, local_port, Protocol::Tcp),
- inode_to_procname.get(&entry.inode),
- ) {
- open_sockets.insert(connection.local_socket, procname.clone());
- connections.push(connection);
+ if let Some(procname) = inode_to_procname.get(&entry.inode) {
+ open_sockets.insert(
+ LocalSocket {
+ ip: entry.local_address.ip(),
+ port: entry.local_address.port(),
+ protocol: Protocol::Tcp,
+ },
+ procname.clone(),
+ );
};
}
}
@@ -45,19 +45,20 @@ pub(crate) fn get_open_sockets() -> OpenSockets {
udp.append(&mut udp6);
}
for entry in udp.into_iter() {
- let local_port = entry.local_address.port();
- let local_ip = entry.local_address.ip();
- if let (connection, Some(procname)) = (
- Connection::new(entry.remote_address, local_ip, local_port, Protocol::Udp),
- inode_to_procname.get(&entry.inode),
- ) {
- open_sockets.insert(connection.local_socket, procname.clone());
- connections.push(connection);
+ if let Some(procname) = inode_to_procname.get(&entry.inode) {
+ open_sockets.insert(
+ LocalSocket {
+ ip: entry.local_address.ip(),
+ port: entry.local_address.port(),
+ protocol: Protocol::Udp,
+ },
+ procname.clone(),
+ );
};
}
}
+
OpenSockets {
sockets_to_procs: open_sockets,
- connections,
}
}
diff --git a/src/os/lsof.rs b/src/os/lsof.rs
index 31e3266..bb17948 100644
--- a/src/os/lsof.rs
+++ b/src/os/lsof.rs
@@ -1,10 +1,9 @@
use ::std::collections::HashMap;
-use crate::network::Connection;
+use crate::network::LocalSocket;
use crate::OpenSockets;
use super::lsof_utils;
-use std::net::SocketAddr;
#[derive(Debug)]
struct RawConnection {
@@ -17,26 +16,21 @@ struct RawConnection {
pub(crate) fn get_open_sockets() -> OpenSockets {
let mut open_sockets = HashMap::new();
- let mut connections_vec = std::vec::Vec::new();
let connections = lsof_utils::get_connections();
for raw_connection in connections {
- let protocol = raw_connection.get_protocol();
- let remote_ip = raw_connection.get_remote_ip();
- let local_ip = raw_connection.get_local_ip();
- let remote_port = raw_connection.get_remote_port();
- let local_port = raw_connection.get_local_port();
-
- let socket_addr = SocketAddr::new(remote_ip, remote_port);
- let connection = Connection::new(socket_addr, local_ip, local_port, protocol);
-
- open_sockets.insert(connection.local_socket, raw_connection.process_name.clone());
- connections_vec.push(connection);
+ open_sockets.insert(
+ LocalSocket {
+ ip: raw_connection.get_local_ip(),
+ port: raw_connection.get_local_port(),
+ protocol: raw_connection.get_protocol(),
+ },
+ raw_connection.process_name.clone(),
+ );
}
OpenSockets {
sockets_to_procs: open_sockets,
- connections: connections_vec,
}
}
diff --git a/src/os/lsof_utils.rs b/src/os/lsof_utils.rs
index fa51fa1..3d715b8 100644
--- a/src/os/lsof_utils.rs
+++ b/src/os/lsof_utils.rs
@@ -106,14 +106,6 @@ impl RawConnection {
Protocol::from_str(&self.protocol).unwrap()
}
- pub fn get_remote_ip(&self) -> IpAddr {
- self.remote_ip.parse().unwrap()
- }
-
- pub fn get_remote_port(&self) -> u16 {
- self.remote_port.parse::<u16>().unwrap()
- }
-
pub fn get_local_ip(&self) -> IpAddr {
self.local_ip.parse().unwrap()
}
@@ -204,19 +196,6 @@ com.apple 590 etoledom 204u IPv4 0x28ffb9c04111253f 0t0 TCP 192.168.1.
}
#[test]
- fn test_raw_connection_parse_remote_port_ipv4() {
- test_raw_connection_parse_remote_port(LINE_RAW_OUTPUT);
- }
- #[test]
- fn test_raw_connection_parse_remote_port_ipv6() {
- test_raw_connection_parse_remote_port(IPV6_LINE_RAW_OUTPUT);
- }
- fn test_raw_connection_parse_remote_port(raw_output: &str) {
- let connection = RawConnection::new(raw_output).unwrap();
- assert_eq!(connection.get_remote_port(), 2222);
- }
-
- #[test]
fn test_raw_connection_parse_local_port_ipv4() {
test_raw_connection_parse_local_port(LINE_RAW_OUTPUT);
}
@@ -230,19 +209,6 @@ com.apple 590 etoledom 204u IPv4 0x28ffb9c04111253f 0t0 TCP 192.168.1.
}
#[test]
- fn test_raw_connection_parse_ip_address_ipv4() {
- test_raw_connection_parse_ip_address(LINE_RAW_OUTPUT, "198.252.206.25");
- }
- #[test]
- fn test_raw_connection_parse_ip_address_ipv6() {
- test_raw_connection_parse_ip_address(IPV6_LINE_RAW_OUTPUT, "fe80:4::aede:48ff:fe33:4455");
- }
- fn test_raw_connection_parse_ip_address(raw_output: &str, ip: &str) {
- let connection = RawConnection::new(raw_output).unwrap();
- assert_eq!(connection.get_remote_ip().to_string(), String::from(ip));
- }
-
- #[test]
fn test_raw_connection_parse_protocol_ipv4() {
test_raw_connection_parse_protocol(LINE_RAW_OUTPUT);
}
diff --git a/src/os/mod.rs b/src/os/mod.rs
index 056d44e..ec04ecd 100644
--- a/src/os/mod.rs
+++ b/src/os/mod.rs
@@ -7,6 +7,9 @@ pub(self) mod lsof;
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
mod lsof_utils;
+#[cfg(target_os = "windows")]
+pub(self) mod windows;
+
mod errors;
mod shared;
diff --git a/src/os/shared.rs b/src/os/shared.rs
index 7c252bf..4cc0c24 100644
--- a/src/os/shared.rs
+++ b/src/os/shared.rs
@@ -9,12 +9,16 @@ use ::tokio::runtime::Runtime;
use ::std::time;
use crate::os::errors::GetInterfaceErrorKind;
+#[cfg(not(target_os = "windows"))]
use signal_hook::iterator::Signals;
#[cfg(target_os = "linux")]
use crate::os::linux::get_open_sockets;
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
use crate::os::lsof::get_open_sockets;
+#[cfg(target_os = "windows")]
+use crate::os::windows::get_open_sockets;
+
use crate::{network::dns, OsInputOutput};
pub type OnSigWinch = dyn Fn(Box<dyn Fn()>) + Send;
@@ -63,6 +67,7 @@ fn get_interface(interface_name: &str) -> Option<NetworkInterface> {
.find(|iface| iface.name == interface_name)
}
+#[cfg(not(target_os = "windows"))]
fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
let signals = Signals::new(&[signal_hook::SIGWINCH]).unwrap();
let on_winch = {
@@ -82,6 +87,15 @@ fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
(Box::new(on_winch), Box::new(cleanup))
}
+#[cfg(any(target_os = "windows"))]
+fn sigwinch() -> (Box<OnSigWinch>, Box<SigCleanup>) {
+ let on_winch = { move |_cb: Box<dyn Fn()>| {} };
+ let cleanup = move || {
+ println!("Fake signal cleanup");
+ };
+ (Box::new(on_winch), Box::new(cleanup))
+}
+
fn create_write_to_stdout() -> Box<dyn FnMut(String) + Send> {
Box::new({
let mut stdout = io::stdout();
@@ -180,6 +194,12 @@ pub fn get_input(
datalink::interfaces()
};
+ #[cfg(any(target_os = "windows"))]
+ let network_frames = network_interfaces
+ .iter()
+ .filter(|iface| !iface.ips.is_empty())
+ .map(|iface| (iface, get_datalink_channel(iface)));
+ #[cfg(not(target_os = "windows"))]
let network_frames = network_interfaces
.iter()
.filter(|iface| iface.is_up() && !iface.ips.is_empty())
@@ -257,3 +277,9 @@ fn eperm_message() -> &'static str {
`cap_sys_ptrace,cap_dac_read_search,cap_net_raw,cap_net_admin+ep`
"#
}
+
+#[inline]
+#[cfg(any(target_os = "windows"))]
+fn eperm_message() -> &'static str {
+ "Insufficient permissions to listen on network interface(s). Try running with administrator rights."
+}
diff --git a/src/os/windows.rs b/src/os/windows.rs
new file mode 100644
index 0000000..69adfde
--- /dev/null
+++ b/src/os/windows.rs
@@ -0,0 +1,58 @@
+use ::std::collections::HashMap;
+
+use ::sysinfo::ProcessExt;
+use netstat2::*;
+
+use crate::network::{LocalSocket, Protocol};
+use crate::OpenSockets;
+use sysinfo::{Pid, System, SystemExt};
+
+pub(crate) fn get_open_sockets() -> OpenSockets {
+ let mut open_sockets = HashMap::new();
+
+ let mut sysinfo = System::new_all();
+ sysinfo.refresh_processes();
+
+ let af_flags = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6;
+ let proto_flags = ProtocolFlags::TCP | ProtocolFlags::UDP;
+ let sockets_info = get_sockets_info(af_flags, proto_flags);
+
+ if let Ok(sockets_info) = sockets_info {
+ for si in sockets_info {
+ let mut procname = String::new();
+ for pid in si.associated_pids {
+ if let Some(process) = sysinfo.get_process(pid as Pid) {
+ procname = String::from(process.name());
+ break;
+ }
+ }
+
+ match si.protocol_socket_info {
+ ProtocolSocketInfo::Tcp(tcp_si) => {
+ open_sockets.insert(
+ LocalSocket {
+ ip: tcp_si.local_addr,
+ port: tcp_si.local_port,
+ protocol: Protocol::Tcp,
+ },
+ procname,
+ );
+ }
+ ProtocolSocketInfo::Udp(udp_si) => {
+ open_sockets.insert(
+ LocalSocket {
+ ip: udp_si.local_addr,
+ port: udp_si.local_port,
+ protocol: Protocol::Udp,
+ },
+ procname,
+ );
+ }
+ }
+ }
+ }
+
+ OpenSockets {
+ sockets_to_procs: open_sockets,
+ }
+}
diff --git a/src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap b/src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap
index fa34c0f..70398ca 100644
--- a/src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap
+++ b/src/tests/cases/snapshots/raw_mode__traffic_with_host_names.snap
@@ -8,10 +8,10 @@ Refreshing:
Refreshing:
process: <TIMESTAMP_REMOVED> "1" up/down Bps: 28/30 connections: 1
process: <TIMESTAMP_REMOVED> "5" up/down Bps: 17/18 connections: 1
-connection: <TIMESTAMP_REMOVED> <interface_name>:443 => one.one.one.one:12345 (tcp) up/down Bps: 28/30 process: "1"
-connection: <TIMESTAMP_REMOVED> <interface_name>:4435 => three.three.three.three:1337 (tcp) up/down Bps: 17/18 process: "5"
-remote_address: <TIMESTAMP_REMOVED> one.one.one.one up/down Bps: 28/30 connections: 1
-remote_address: <TIMESTAMP_REMOVED> three.three.three.three up/down Bps: 17/18 connections: 1
+connection: <TIMESTAMP_REMOVED> <interface_name>:443 => 1.1.1.1:12345 (tcp) up/down Bps: 28/30 process: "1"
+connection: <TIMESTAMP_REMOVED> <interface_name>:4435 => 3.3.3.3:1337 (tcp) up/down Bps: 17/18 process: "5"
+remote_address: <TIMESTAMP_REMOVED> 1.1.1.1 up/down Bps: 28/30 connections: 1
+remote_address: <TIMESTAMP_REMOVED> 3.3.3.3 up/down Bps: 17/18 connections: 1
Refreshing:
process: <TIMESTAMP_REMOVED> "1" up/down Bps: 31/32 connections: 1
diff --git a/src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap b/src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap
index a620f65..18a9a28 100644
--- a/src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap
+++ b/src/tests/cases/snapshots/ui__traffic_with_host_names-2.snap
@@ -6,8 +6,8 @@ expression: "&terminal_draw_events_mirror[2]"
- 31 2 31 2
- 22 27 22 27
+ 31 2 one one.one.one 31 2
+ 22 27 three three.three.three 22 27
@@ -30,8 +30,8 @@ expression: "&terminal_draw_events_mirror[2]"
- 31 2
- 22 27
+ one one.one.one:12345 (tcp) 31 2
+ three three.three.three:1337 (tcp) 22 27
diff --git a/src/tests/cases/snapshots/ui__traffic_with_host_names.snap b/src/tests/cases/snapshots/ui__traffic_with_host_names.snap
index 2057262..6e3a0ec 100644
--- a/src/tests/cases/snapshots/ui__traffic_with_host_names.snap
+++ b/src/tests/cases/snapshots/ui__traffic_with_host_names.snap
@@ -6,8 +6,8 @@ expression: "&terminal_draw_events_mirror[1]"
- 1 1 28Bps / 30Bps one.one.one.one 1 28Bps / 30Bps
- 5 1 17Bps / 18Bps three.three.three.three 1 17Bps / 18Bps
+ 1 1 28Bps / 30Bps 1.1.1.1 1 28Bps / 30Bps
+ 5 1 17Bps / 18Bps 3.3.3.3 1 17Bps / 18Bps
@@ -30,8 +30,8 @@ expression: "&terminal_draw_events_mirror[1]"
- <interface_name>:443 => one.one.one.one:12345 (tcp) 1 28Bps / 30Bps
- <interface_name>:4435 => three.three.three.three:1337 (tcp) 5 17Bps / 18Bps
+ <interface_name>:443 => 1.1.1.1:12345 (tcp) 1 28Bps / 30Bps
+ <interface_name>:4435 => 3.3.3.3:1337 (tcp) 5 17Bps / 18Bps
diff --git a/src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap b/src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap
index a620f65..61dd48d 100644
--- a/src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap
+++ b/src/tests/cases/snapshots/ui__truncate_long_hostnames-2.snap
@@ -6,8 +6,8 @@ expression: "&terminal_draw_events_mirror[2]"
- 31 2 31 2
- 22 27 22 27
+ 31 2 i am.not.too.long 31 2
+ 22 27 i am.an.obno[...]really.i.ask 22 27
@@ -30,8 +30,8 @@ expression: "&terminal_draw_events_mirror[2]"
- 31 2
- 22 27
+ i am.not.too.long:12345 (tcp) 31 2
+ i am.an.obnoxiosuly.lo[...]hy.would.anyone.do.this.really.i.ask:1337 (tcp) 22 27
diff --git a/src/tests/cases/snapshots/ui__truncate_long_hostnames.snap b/src/tests/cases/snapshots/ui__truncate_long_hostnames.snap
index ff2bcd8..6e3a0ec 100644
--- a/src/tests/cases/snapshots/ui__truncate_long_hostnames.snap
+++ b/src/tests/cases/snapshots/ui__truncate_long_hostnames.snap
@@ -6,8 +6,8 @@ expression: "&terminal_draw_events_mirror[1]"
- 1 1 28Bps / 30Bps i.am.not.too.long 1 28Bps / 30Bps
- 5 1 17Bps / 18Bps i.am.an.obno[...]really.i.ask 1 17Bps / 18Bps
+ 1 1 28Bps / 30Bps 1.1.1.1 1 28Bps / 30Bps
+ 5 1 17Bps / 18Bps 3.3.3.3 1 17Bps / 18Bps
@@ -30,8 +30,8 @@ expression: "&terminal_draw_events_mirror[1]"
- <interface_name>:443 => i.am.not.too.long:12345 (tcp) 1 28Bps / 30Bps
- <interface_name>:4435 => i.am.an.obnoxiosuly.lo[...]hy.would.anyone.do.this.really.i.ask:1337 (tcp) 5 17Bps / 18Bps
+ <interface_name>:443 => 1.1.1.1:12345 (tcp) 1 28Bps / 30Bps
+ <interface_name>:4435 => 3.3.3.3:1337 (tcp) 5 17Bps / 18Bps
diff --git a/src/tests/fakes/fake_input.rs b/src/tests/fakes/fake_input.rs
index ac6c246..5e9e913 100644
--- a/src/tests/fakes/fake_input.rs
+++ b/src/tests/fakes/fake_input.rs
@@ -141,7 +141,6 @@ pub fn get_open_sockets() -> OpenSockets {
OpenSockets {
sockets_to_procs: local_socket_to_procs,
- connections,
}
}