1use std::collections::HashMap;
2use std::fs;
3use std::net::{IpAddr, SocketAddr};
4use std::str::FromStr;
5
6#[derive(Debug, Clone)]
7pub struct NetworkConnection {
8 pub local_addr: SocketAddr,
9 pub remote_addr: SocketAddr,
10 pub state: ConnectionState,
11 pub protocol: Protocol,
12 pub pid: Option<u32>,
13 pub process_name: Option<String>,
14 pub bytes_sent: u64,
15 pub bytes_received: u64,
16 pub socket_info: SocketInfo,
18}
19
20#[derive(Debug, Clone, Default)]
21pub struct SocketInfo {
22 pub rtt: Option<f64>, pub rttvar: Option<f64>, pub cwnd: Option<u32>, pub ssthresh: Option<u32>, pub send_queue: u32, pub recv_queue: u32, pub bandwidth: Option<u64>, pub pacing_rate: Option<u64>, pub retrans: u32, pub lost: u32, pub duration: Option<String>, pub interface: Option<String>, pub tcp_info: Option<TcpInfo>, }
36
37#[derive(Debug, Clone)]
38pub struct TcpInfo {
39 pub mss: u32, pub pmtu: u32, pub rcv_mss: u32, pub advmss: u32, pub cwnd_clamp: u32, pub delivery_rate: Option<u64>, pub app_limited: bool, pub reordering: u32, }
48
49#[derive(Debug, Clone, PartialEq)]
50pub enum ConnectionState {
51 Established,
52 Listen,
53 SynSent,
54 SynReceived,
55 FinWait1,
56 FinWait2,
57 TimeWait,
58 Close,
59 CloseWait,
60 LastAck,
61 Closing,
62 Unknown,
63}
64
65impl ConnectionState {
66 pub fn as_str(&self) -> &'static str {
67 match self {
68 ConnectionState::Established => "ESTABLISHED",
69 ConnectionState::Listen => "LISTEN",
70 ConnectionState::SynSent => "SYN_SENT",
71 ConnectionState::SynReceived => "SYN_RECV",
72 ConnectionState::FinWait1 => "FIN_WAIT1",
73 ConnectionState::FinWait2 => "FIN_WAIT2",
74 ConnectionState::TimeWait => "TIME_WAIT",
75 ConnectionState::Close => "CLOSE",
76 ConnectionState::CloseWait => "CLOSE_WAIT",
77 ConnectionState::LastAck => "LAST_ACK",
78 ConnectionState::Closing => "CLOSING",
79 ConnectionState::Unknown => "UNKNOWN",
80 }
81 }
82
83 pub fn color(&self) -> ratatui::style::Color {
84 use ratatui::style::Color;
85 match self {
86 ConnectionState::Established => Color::Green,
87 ConnectionState::Listen => Color::Blue,
88 ConnectionState::SynSent | ConnectionState::SynReceived => Color::Yellow,
89 ConnectionState::FinWait1
90 | ConnectionState::FinWait2
91 | ConnectionState::TimeWait
92 | ConnectionState::CloseWait
93 | ConnectionState::LastAck
94 | ConnectionState::Closing => Color::Red,
95 ConnectionState::Close => Color::Gray,
96 ConnectionState::Unknown => Color::Magenta,
97 }
98 }
99}
100
101impl FromStr for ConnectionState {
102 type Err = ();
103
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
105 match s {
106 "01" => Ok(ConnectionState::Established),
107 "02" => Ok(ConnectionState::SynSent),
108 "03" => Ok(ConnectionState::SynReceived),
109 "04" => Ok(ConnectionState::FinWait1),
110 "05" => Ok(ConnectionState::FinWait2),
111 "06" => Ok(ConnectionState::TimeWait),
112 "07" => Ok(ConnectionState::Close),
113 "08" => Ok(ConnectionState::CloseWait),
114 "09" => Ok(ConnectionState::LastAck),
115 "0A" | "10" => Ok(ConnectionState::Listen),
116 "0B" | "11" => Ok(ConnectionState::Closing),
117 _ => Ok(ConnectionState::Unknown),
118 }
119 }
120}
121
122#[derive(Debug, Clone, PartialEq)]
123pub enum Protocol {
124 Tcp,
125 Udp,
126 Tcp6,
127 Udp6,
128}
129
130impl Protocol {
131 pub fn as_str(&self) -> &'static str {
132 match self {
133 Protocol::Tcp => "TCP",
134 Protocol::Udp => "UDP",
135 Protocol::Tcp6 => "TCP6",
136 Protocol::Udp6 => "UDP6",
137 }
138 }
139}
140
141pub struct ConnectionMonitor {
142 connections: Vec<NetworkConnection>,
143 process_cache: HashMap<u32, String>,
144}
145
146impl ConnectionMonitor {
147 pub fn new() -> Self {
148 Self {
149 connections: Vec::new(),
150 process_cache: HashMap::new(),
151 }
152 }
153
154 pub fn update(&mut self) -> Result<(), Box<dyn std::error::Error>> {
155 self.connections.clear();
157
158 #[cfg(target_os = "macos")]
160 {
161 self.read_tcp_connections()?;
162 self.read_udp_connections()?;
163 let _ = self.update_process_info();
165 }
166
167 #[cfg(not(target_os = "macos"))]
168 {
169 if self.read_ss_connections().is_ok() {
171 } else {
173 self.read_tcp_connections()?;
175 self.read_udp_connections()?;
176
177 self.update_process_info()?;
179 }
180 }
181
182 self.connections.sort_by(|a, b| {
184 match (a.socket_info.rtt, b.socket_info.rtt) {
186 (Some(rtt_a), Some(rtt_b)) => rtt_a
187 .partial_cmp(&rtt_b)
188 .unwrap_or(std::cmp::Ordering::Equal),
189 (Some(_), None) => std::cmp::Ordering::Less,
190 (None, Some(_)) => std::cmp::Ordering::Greater,
191 (None, None) => {
192 (b.bytes_sent + b.bytes_received).cmp(&(a.bytes_sent + a.bytes_received))
194 }
195 }
196 });
197
198 Ok(())
199 }
200
201 #[allow(dead_code)]
202 fn read_ss_connections(&mut self) -> Result<(), Box<dyn std::error::Error>> {
203 use std::process::Command;
204
205 let output = Command::new("ss")
207 .args(["-tupln", "-i", "-e", "-p"]) .output()?;
209
210 if !output.status.success() {
211 return Err("ss command failed".into());
212 }
213
214 let content = String::from_utf8_lossy(&output.stdout);
215 self.parse_ss_output(&content)?;
216
217 Ok(())
218 }
219
220 #[allow(dead_code)]
221 fn parse_ss_output(&mut self, content: &str) -> Result<(), Box<dyn std::error::Error>> {
222 let lines: Vec<&str> = content.lines().collect();
223 let mut i = 0;
224
225 while i < lines.len() {
226 let line = lines[i].trim();
227
228 if line.starts_with("Netid") || line.is_empty() {
230 i += 1;
231 continue;
232 }
233
234 if let Some(connection) = self.parse_ss_connection_line(line)? {
236 let mut socket_info = SocketInfo::default();
238
239 i += 1;
241 while i < lines.len() {
242 let next_line = lines[i].trim();
243
244 if next_line.starts_with("cubic")
246 || next_line.starts_with("rto:")
247 || next_line.contains("rtt:")
248 {
249 self.parse_socket_details(next_line, &mut socket_info)?;
250 i += 1;
251 } else {
252 break;
254 }
255 }
256
257 let mut conn = connection;
258 conn.socket_info = socket_info;
259 self.connections.push(conn);
260 } else {
261 i += 1;
262 }
263 }
264
265 Ok(())
266 }
267
268 #[allow(dead_code)]
269 fn parse_ss_connection_line(
270 &self,
271 line: &str,
272 ) -> Result<Option<NetworkConnection>, Box<dyn std::error::Error>> {
273 let parts: Vec<&str> = line.split_whitespace().collect();
274 if parts.len() < 5 {
275 return Ok(None);
276 }
277
278 let protocol = match parts[0] {
280 "tcp" => Protocol::Tcp,
281 "udp" => Protocol::Udp,
282 "tcp6" => Protocol::Tcp6,
283 "udp6" => Protocol::Udp6,
284 _ => return Ok(None),
285 };
286
287 let state = match parts[1] {
289 "ESTAB" => ConnectionState::Established,
290 "LISTEN" => ConnectionState::Listen,
291 "SYN-SENT" => ConnectionState::SynSent,
292 "SYN-RECV" => ConnectionState::SynReceived,
293 "FIN-WAIT-1" => ConnectionState::FinWait1,
294 "FIN-WAIT-2" => ConnectionState::FinWait2,
295 "TIME-WAIT" => ConnectionState::TimeWait,
296 "CLOSE" => ConnectionState::Close,
297 "CLOSE-WAIT" => ConnectionState::CloseWait,
298 "LAST-ACK" => ConnectionState::LastAck,
299 "CLOSING" => ConnectionState::Closing,
300 _ => ConnectionState::Unknown,
301 };
302
303 let recv_queue = parts[2].parse().unwrap_or(0);
305 let send_queue = parts[3].parse().unwrap_or(0);
306
307 let local_addr = self.parse_address(parts[4])?;
309
310 let remote_addr = if parts.len() > 5 && parts[5] != "*:*" {
312 self.parse_address(parts[5])?
313 } else {
314 SocketAddr::new(IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), 0)
315 };
316
317 let (pid, process_name) =
319 if let Some(process_part) = parts.iter().find(|p| p.starts_with("users:")) {
320 self.parse_process_info(process_part)?
321 } else {
322 (None, None)
323 };
324
325 let socket_info = SocketInfo {
326 recv_queue,
327 send_queue,
328 ..Default::default()
329 };
330
331 Ok(Some(NetworkConnection {
332 local_addr,
333 remote_addr,
334 state,
335 protocol,
336 pid,
337 process_name,
338 bytes_sent: 0, bytes_received: 0,
340 socket_info,
341 }))
342 }
343
344 fn parse_address_string(
345 &self,
346 addr_str: &str,
347 ) -> Result<SocketAddr, Box<dyn std::error::Error>> {
348 if addr_str.contains("*:*") || addr_str == "*" {
350 return Ok(SocketAddr::new(
351 IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)),
352 0,
353 ));
354 }
355
356 if addr_str.starts_with('[') {
357 let end_bracket = addr_str.find(']').ok_or("Invalid IPv6 format")?;
359 let ip_str = &addr_str[1..end_bracket];
360 let port_str = &addr_str[end_bracket + 2..]; let ip = ip_str.parse()?;
362 let port = port_str.parse()?;
363 Ok(SocketAddr::new(ip, port))
364 } else {
365 let last_dot = addr_str.rfind('.').ok_or("Invalid address format")?;
367 let ip_str = &addr_str[..last_dot];
368 let port_str = &addr_str[last_dot + 1..];
369 let ip = ip_str.parse()?;
370 let port = port_str.parse()?;
371 Ok(SocketAddr::new(ip, port))
372 }
373 }
374
375 #[allow(dead_code)]
376 fn parse_address(&self, addr_str: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> {
377 if addr_str.starts_with('[') {
379 let end_bracket = addr_str.find(']').ok_or("Invalid IPv6 format")?;
381 let ip_str = &addr_str[1..end_bracket];
382 let port_str = &addr_str[end_bracket + 2..]; let ip = ip_str.parse()?;
384 let port = port_str.parse()?;
385 Ok(SocketAddr::new(ip, port))
386 } else {
387 let parts: Vec<&str> = addr_str.rsplitn(2, ':').collect();
389 if parts.len() != 2 {
390 return Err("Invalid address format".into());
391 }
392 let port = parts[0].parse()?;
393 let ip = parts[1].parse()?;
394 Ok(SocketAddr::new(ip, port))
395 }
396 }
397
398 #[allow(dead_code)]
399 fn parse_process_info(
400 &self,
401 process_part: &str,
402 ) -> Result<(Option<u32>, Option<String>), Box<dyn std::error::Error>> {
403 if let Some(start) = process_part.find("pid=") {
405 let pid_part = &process_part[start + 4..];
406 if let Some(end) = pid_part.find(',') {
407 let pid_str = &pid_part[..end];
408 if let Ok(pid) = pid_str.parse::<u32>() {
409 if let Some(name_start) = process_part.find("\"") {
411 if let Some(name_end) = process_part[name_start + 1..].find("\"") {
412 let name = &process_part[name_start + 1..name_start + 1 + name_end];
413 return Ok((Some(pid), Some(name.to_string())));
414 }
415 }
416 return Ok((Some(pid), None));
417 }
418 }
419 }
420 Ok((None, None))
421 }
422
423 #[allow(dead_code)]
424 fn parse_socket_details(
425 &self,
426 line: &str,
427 socket_info: &mut SocketInfo,
428 ) -> Result<(), Box<dyn std::error::Error>> {
429 for part in line.split_whitespace() {
431 if let Some(rtt_part) = part.strip_prefix("rtt:") {
432 if let Some(slash_pos) = rtt_part.find('/') {
434 let rtt_str = &rtt_part[..slash_pos];
435 socket_info.rtt = rtt_str.parse().ok();
436
437 let rttvar_part = &rtt_part[slash_pos + 1..];
438 if let Some(ms_pos) = rttvar_part.find("ms") {
439 let rttvar_str = &rttvar_part[..ms_pos];
440 socket_info.rttvar = rttvar_str.parse().ok();
441 }
442 }
443 } else if let Some(cwnd_part) = part.strip_prefix("cwnd:") {
444 socket_info.cwnd = cwnd_part.parse().ok();
445 } else if let Some(ssthresh_part) = part.strip_prefix("ssthresh:") {
446 socket_info.ssthresh = ssthresh_part.parse().ok();
447 } else if part.starts_with("pacing_rate") {
448 if let Some(rate_str) = part.split(':').nth(1) {
450 socket_info.pacing_rate = self.parse_bandwidth(rate_str);
451 }
452 } else if let Some(retrans_part) = part.strip_prefix("retrans:") {
453 if let Some(slash_pos) = retrans_part.find('/') {
455 socket_info.retrans = retrans_part[..slash_pos].parse().unwrap_or(0);
456 socket_info.lost = retrans_part[slash_pos + 1..].parse().unwrap_or(0);
457 }
458 }
459 }
460
461 Ok(())
462 }
463
464 #[allow(dead_code)]
465 fn parse_bandwidth(&self, bw_str: &str) -> Option<u64> {
466 let bw_str = bw_str.trim();
467 if let Some(kbps_part) = bw_str.strip_suffix("Kbps") {
468 kbps_part.parse::<f64>().ok().map(|n| (n * 1000.0) as u64)
469 } else if let Some(mbps_part) = bw_str.strip_suffix("Mbps") {
470 mbps_part
471 .parse::<f64>()
472 .ok()
473 .map(|n| (n * 1_000_000.0) as u64)
474 } else if let Some(gbps_part) = bw_str.strip_suffix("Gbps") {
475 gbps_part
476 .parse::<f64>()
477 .ok()
478 .map(|n| (n * 1_000_000_000.0) as u64)
479 } else {
480 bw_str.parse().ok()
481 }
482 }
483
484 fn read_tcp_connections(&mut self) -> Result<(), Box<dyn std::error::Error>> {
485 if let Ok(content) = fs::read_to_string("/proc/net/tcp") {
487 self.parse_connections(&content, Protocol::Tcp)?;
488 } else {
489 self.create_real_connections_from_system(Protocol::Tcp);
491 }
492
493 if let Ok(content) = fs::read_to_string("/proc/net/tcp6") {
494 self.parse_connections(&content, Protocol::Tcp6)?;
495 } else {
496 self.create_real_connections_from_system(Protocol::Tcp6);
498 }
499
500 Ok(())
501 }
502
503 fn read_udp_connections(&mut self) -> Result<(), Box<dyn std::error::Error>> {
504 if let Ok(content) = fs::read_to_string("/proc/net/udp") {
506 self.parse_connections(&content, Protocol::Udp)?;
507 } else {
508 self.create_real_connections_from_system(Protocol::Udp);
509 }
510
511 if let Ok(content) = fs::read_to_string("/proc/net/udp6") {
513 self.parse_connections(&content, Protocol::Udp6)?;
514 } else {
515 self.create_real_connections_from_system(Protocol::Udp6);
516 }
517
518 Ok(())
519 }
520
521 fn parse_connections(
522 &mut self,
523 content: &str,
524 protocol: Protocol,
525 ) -> Result<(), Box<dyn std::error::Error>> {
526 for line in content.lines().skip(1) {
527 let fields: Vec<&str> = line.split_whitespace().collect();
529 if fields.len() < 10 {
530 continue;
531 }
532
533 let local_addr = self.parse_socket_addr(fields[1])?;
535 let remote_addr = self.parse_socket_addr(fields[2])?;
536
537 let state = ConnectionState::from_str(fields[3]).unwrap_or(ConnectionState::Unknown);
539
540 let pid = if fields.len() > 7 {
542 fields[7].parse().ok()
543 } else {
544 None
545 };
546
547 let connection = NetworkConnection {
549 local_addr,
550 remote_addr,
551 state,
552 protocol: protocol.clone(),
553 pid,
554 process_name: None, bytes_sent: 0, bytes_received: 0,
557 socket_info: SocketInfo::default(),
558 };
559
560 self.connections.push(connection);
561 }
562
563 Ok(())
564 }
565
566 fn parse_socket_addr(&self, addr_str: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> {
567 let parts: Vec<&str> = addr_str.split(':').collect();
568 if parts.len() != 2 {
569 return Err("Invalid socket address format".into());
570 }
571
572 let ip_hex = parts[0];
574 let port_hex = parts[1];
575
576 let port = u16::from_str_radix(port_hex, 16)?;
577
578 let ip = if ip_hex.len() == 8 {
580 let ip_num = u32::from_str_radix(ip_hex, 16)?;
582 let ip_bytes = [
583 (ip_num & 0xFF) as u8,
584 ((ip_num >> 8) & 0xFF) as u8,
585 ((ip_num >> 16) & 0xFF) as u8,
586 ((ip_num >> 24) & 0xFF) as u8,
587 ];
588 IpAddr::V4(ip_bytes.into())
589 } else if ip_hex.len() == 32 {
590 let mut ip_bytes = [0u8; 16];
592 for i in 0..16 {
593 ip_bytes[i] = u8::from_str_radix(&ip_hex[i * 2..i * 2 + 2], 16)?;
594 }
595 IpAddr::V6(ip_bytes.into())
596 } else {
597 return Err("Invalid IP address length".into());
598 };
599
600 Ok(SocketAddr::new(ip, port))
601 }
602
603 fn update_process_info(&mut self) -> Result<(), Box<dyn std::error::Error>> {
604 let mut pid_to_name = HashMap::new();
606
607 if let Ok(entries) = fs::read_dir("/proc") {
608 for entry in entries.flatten() {
609 if let Ok(file_name) = entry.file_name().into_string() {
610 if let Ok(pid) = file_name.parse::<u32>() {
611 let comm_path = format!("/proc/{pid}/comm");
612 if let Ok(process_name) = fs::read_to_string(comm_path) {
613 pid_to_name.insert(pid, process_name.trim().to_string());
614 }
615 }
616 }
617 }
618 }
619
620 for connection in &mut self.connections {
622 if let Some(pid) = connection.pid {
623 connection.process_name = pid_to_name.get(&pid).cloned();
624 }
625 }
626
627 self.process_cache = pid_to_name;
628 Ok(())
629 }
630
631 pub fn get_connections(&self) -> &[NetworkConnection] {
632 &self.connections
633 }
634
635 pub fn get_connection_stats(&self) -> ConnectionStats {
636 let mut stats = ConnectionStats::default();
637
638 for conn in &self.connections {
639 match conn.state {
640 ConnectionState::Established => stats.established += 1,
641 ConnectionState::Listen => stats.listening += 1,
642 ConnectionState::TimeWait => stats.time_wait += 1,
643 _ => stats.other += 1,
644 }
645
646 match conn.protocol {
647 Protocol::Tcp | Protocol::Tcp6 => stats.tcp += 1,
648 Protocol::Udp | Protocol::Udp6 => stats.udp += 1,
649 }
650
651 stats.total += 1;
652 }
653
654 stats
655 }
656
657 pub fn get_top_processes(&self) -> Vec<(String, u32)> {
658 let mut process_counts: HashMap<String, u32> = HashMap::new();
659
660 for conn in &self.connections {
661 if let Some(process_name) = &conn.process_name {
662 *process_counts.entry(process_name.clone()).or_insert(0) += 1;
663 }
664 }
665
666 let mut sorted_processes: Vec<(String, u32)> = process_counts.into_iter().collect();
667 sorted_processes.sort_by(|a, b| b.1.cmp(&a.1));
668 sorted_processes.truncate(10); sorted_processes
671 }
672
673 pub fn get_remote_hosts(&self) -> Vec<(IpAddr, u32)> {
674 let mut host_counts: HashMap<IpAddr, u32> = HashMap::new();
675
676 for conn in &self.connections {
677 if conn.state == ConnectionState::Established {
678 *host_counts.entry(conn.remote_addr.ip()).or_insert(0) += 1;
679 }
680 }
681
682 let mut sorted_hosts: Vec<(IpAddr, u32)> = host_counts.into_iter().collect();
683 sorted_hosts.sort_by(|a, b| b.1.cmp(&a.1));
684 sorted_hosts.truncate(10); sorted_hosts
687 }
688}
689
690#[derive(Default)]
691pub struct ConnectionStats {
692 pub total: u32,
693 pub established: u32,
694 pub listening: u32,
695 pub time_wait: u32,
696 pub other: u32,
697 pub tcp: u32,
698 pub udp: u32,
699}
700
701impl ConnectionMonitor {
702 fn create_real_connections_from_system(&mut self, protocol: Protocol) {
703 self.get_connections_from_netstat(protocol);
705 }
706
707 fn get_connections_from_netstat(&mut self, protocol: Protocol) {
708 use std::process::Command;
709
710 let protocol_flag = match protocol {
711 Protocol::Tcp => "tcp",
712 Protocol::Tcp6 => "tcp6",
713 Protocol::Udp => "udp",
714 Protocol::Udp6 => "udp6",
715 };
716
717 let output = Command::new("netstat")
719 .args(["-n", "-p", protocol_flag])
720 .output();
721
722 match output {
723 Ok(output) => {
724 let stdout = String::from_utf8_lossy(&output.stdout);
725 self.parse_netstat_output(&stdout, protocol);
726 }
727 Err(_e) => {
728 self.get_connections_from_lsof(protocol);
730 }
731 }
732 }
733
734 fn get_connections_from_lsof(&mut self, protocol: Protocol) {
735 use std::process::Command;
736
737 let protocol_flag = match protocol {
738 Protocol::Tcp | Protocol::Tcp6 => "TCP",
739 Protocol::Udp | Protocol::Udp6 => "UDP",
740 };
741
742 let output = Command::new("lsof")
743 .args(["-i", protocol_flag, "-n"])
744 .output();
745
746 if let Ok(output) = output {
747 let stdout = String::from_utf8_lossy(&output.stdout);
748 self.parse_lsof_output(&stdout, protocol);
749 }
750 }
752
753 fn parse_netstat_output(&mut self, output: &str, protocol: Protocol) {
754 for line in output.lines().skip(2) {
756 if let Some(connection) = self.parse_netstat_line(line, &protocol) {
758 self.connections.push(connection);
759 }
760 }
761 }
762
763 fn parse_lsof_output(&mut self, output: &str, protocol: Protocol) {
764 for line in output.lines().skip(1) {
766 if let Some(connection) = self.parse_lsof_line(line, &protocol) {
768 self.connections.push(connection);
769 }
770 }
771 }
772
773 fn parse_netstat_line(&self, line: &str, protocol: &Protocol) -> Option<NetworkConnection> {
774 let parts: Vec<&str> = line.split_whitespace().collect();
776 if parts.len() < 6 {
777 return None;
778 }
779
780 if parts[0] == "Proto" || parts[0] == "Active" {
782 return None;
783 }
784
785 let local_addr = self.parse_address_string(parts[3]).ok()?;
787 let remote_addr = self.parse_address_string(parts[4]).unwrap_or_else(|_| {
788 std::net::SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)), 0)
789 });
790
791 let state = match parts[5] {
793 "ESTABLISHED" => ConnectionState::Established,
794 "LISTEN" => ConnectionState::Listen,
795 "TIME_WAIT" => ConnectionState::TimeWait,
796 "CLOSE_WAIT" => ConnectionState::CloseWait,
797 "FIN_WAIT_1" => ConnectionState::FinWait1,
798 "FIN_WAIT_2" => ConnectionState::FinWait2,
799 "SYN_SENT" => ConnectionState::SynSent,
800 "SYN_RECV" => ConnectionState::SynReceived,
801 "CLOSING" => ConnectionState::Closing,
802 "LAST_ACK" => ConnectionState::LastAck,
803 _ => ConnectionState::Unknown,
804 };
805
806 Some(NetworkConnection {
807 local_addr,
808 remote_addr,
809 state,
810 protocol: protocol.clone(),
811 pid: None,
812 process_name: None,
813 bytes_sent: 0,
814 bytes_received: 0,
815 socket_info: SocketInfo::default(),
816 })
817 }
818
819 fn parse_lsof_line(&self, line: &str, protocol: &Protocol) -> Option<NetworkConnection> {
820 let parts: Vec<&str> = line.split_whitespace().collect();
824 if parts.len() < 8 {
825 return None;
826 }
827
828 if parts[0] == "COMMAND" {
830 return None;
831 }
832
833 let process_name = Some(parts[0].to_string());
834 let pid = parts[1].parse::<u32>().ok();
835
836 let network_part = parts
838 .iter()
839 .find(|&&part| part.contains("->") || (part.contains(":") && !part.contains("0x")))?;
840
841 let state = if let Some(state_part) = parts.last() {
843 match state_part.trim_matches(|c| c == '(' || c == ')') {
844 "ESTABLISHED" => ConnectionState::Established,
845 "LISTEN" => ConnectionState::Listen,
846 "TIME_WAIT" => ConnectionState::TimeWait,
847 "CLOSE_WAIT" => ConnectionState::CloseWait,
848 "SYN_SENT" => ConnectionState::SynSent,
849 "SYN_RECV" => ConnectionState::SynReceived,
850 "FIN_WAIT1" => ConnectionState::FinWait1,
851 "FIN_WAIT2" => ConnectionState::FinWait2,
852 "CLOSING" => ConnectionState::Closing,
853 "LAST_ACK" => ConnectionState::LastAck,
854 _ => ConnectionState::Unknown,
855 }
856 } else {
857 ConnectionState::Unknown
858 };
859
860 if let Some((local_str, remote_str)) = network_part.split_once("->") {
862 let local_addr = self.parse_lsof_address(local_str).ok()?;
864 let remote_addr = self.parse_lsof_address(remote_str).ok()?;
865
866 return Some(NetworkConnection {
867 local_addr,
868 remote_addr,
869 state,
870 protocol: protocol.clone(),
871 pid,
872 process_name,
873 bytes_sent: 0,
874 bytes_received: 0,
875 socket_info: SocketInfo::default(),
876 });
877 } else if network_part.contains(":") {
878 let local_addr = self.parse_lsof_address(network_part).ok()?;
880 let remote_addr = std::net::SocketAddr::new(
881 std::net::IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)),
882 0,
883 );
884
885 return Some(NetworkConnection {
886 local_addr,
887 remote_addr,
888 state,
889 protocol: protocol.clone(),
890 pid,
891 process_name,
892 bytes_sent: 0,
893 bytes_received: 0,
894 socket_info: SocketInfo::default(),
895 });
896 }
897
898 None
899 }
900
901 fn parse_lsof_address(&self, addr_str: &str) -> Result<SocketAddr, Box<dyn std::error::Error>> {
902 if let Some(port_str) = addr_str.strip_prefix("*:") {
908 let port = port_str.parse()?;
910 return Ok(SocketAddr::new(
911 IpAddr::V4(std::net::Ipv4Addr::new(0, 0, 0, 0)),
912 port,
913 ));
914 }
915
916 if addr_str.starts_with('[') {
917 let end_bracket = addr_str.find(']').ok_or("Invalid IPv6 format")?;
919 let ip_str = &addr_str[1..end_bracket];
920 let port_str = &addr_str[end_bracket + 2..]; let ip = ip_str.parse()?;
922 let port = port_str.parse()?;
923 Ok(SocketAddr::new(ip, port))
924 } else {
925 let parts: Vec<&str> = addr_str.rsplitn(2, ':').collect();
927 if parts.len() != 2 {
928 return Err("Invalid address format".into());
929 }
930 let port = parts[0].parse()?;
931 let ip = parts[1].parse()?;
932 Ok(SocketAddr::new(ip, port))
933 }
934 }
935}
936
937impl Default for ConnectionMonitor {
938 fn default() -> Self {
939 Self::new()
940 }
941}