Skip to content

Commit 5c8ca43

Browse files
authored
refactor: simplify QUIC DPI and reorganize platform code (#84)
* refactor: simplify QUIC DPI and unify SNI extraction helpers * refactor: reorganize platform code and improve code structure
1 parent 2fb66fe commit 5c8ca43

24 files changed

Lines changed: 1347 additions & 735 deletions

File tree

build.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ fn compile_ebpf_programs() {
201201
};
202202

203203
// Get bundled architecture-specific vmlinux.h
204-
let vmlinux_include_path = get_vmlinux_header(vmlinux_arch)
205-
.expect("Failed to locate bundled vmlinux.h");
204+
let vmlinux_include_path =
205+
get_vmlinux_header(vmlinux_arch).expect("Failed to locate bundled vmlinux.h");
206206

207207
SkeletonBuilder::new()
208208
.source(src)
@@ -221,4 +221,3 @@ fn compile_ebpf_programs() {
221221
fn compile_ebpf_programs() {
222222
// No-op when not on Linux or eBPF feature is not enabled
223223
}
224-

src/app.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::filter::ConnectionFilter;
1515

1616
use crate::network::{
1717
capture::{CaptureConfig, PacketReader, setup_packet_capture},
18-
interface_stats::{InterfaceStats, InterfaceStatsProvider, InterfaceRates},
18+
interface_stats::{InterfaceRates, InterfaceStats, InterfaceStatsProvider},
1919
merge::{create_connection_from_packet, merge_packet_into_connection},
2020
parser::{PacketParser, ParsedPacket, ParserConfig},
2121
platform::create_process_lookup,
@@ -24,10 +24,10 @@ use crate::network::{
2424
};
2525

2626
// Platform-specific interface stats provider
27-
#[cfg(target_os = "linux")]
28-
use crate::network::platform::LinuxStatsProvider as PlatformStatsProvider;
2927
#[cfg(target_os = "freebsd")]
3028
use crate::network::platform::FreeBSDStatsProvider as PlatformStatsProvider;
29+
#[cfg(target_os = "linux")]
30+
use crate::network::platform::LinuxStatsProvider as PlatformStatsProvider;
3131
#[cfg(target_os = "macos")]
3232
use crate::network::platform::MacOSStatsProvider as PlatformStatsProvider;
3333
#[cfg(target_os = "windows")]
@@ -967,9 +967,17 @@ impl App {
967967
self.stats.connections_tracked.load(Ordering::Relaxed),
968968
),
969969
last_update: RwLock::new(*self.stats.last_update.read().unwrap()),
970-
total_tcp_retransmits: AtomicU64::new(self.stats.total_tcp_retransmits.load(Ordering::Relaxed)),
971-
total_tcp_out_of_order: AtomicU64::new(self.stats.total_tcp_out_of_order.load(Ordering::Relaxed)),
972-
total_tcp_fast_retransmits: AtomicU64::new(self.stats.total_tcp_fast_retransmits.load(Ordering::Relaxed)),
970+
total_tcp_retransmits: AtomicU64::new(
971+
self.stats.total_tcp_retransmits.load(Ordering::Relaxed),
972+
),
973+
total_tcp_out_of_order: AtomicU64::new(
974+
self.stats.total_tcp_out_of_order.load(Ordering::Relaxed),
975+
),
976+
total_tcp_fast_retransmits: AtomicU64::new(
977+
self.stats
978+
.total_tcp_fast_retransmits
979+
.load(Ordering::Relaxed),
980+
),
973981
}
974982
}
975983

@@ -1064,13 +1072,19 @@ fn update_connection(
10641072

10651073
// Update global statistics
10661074
if new_retransmits > 0 {
1067-
_stats.total_tcp_retransmits.fetch_add(new_retransmits, Ordering::Relaxed);
1075+
_stats
1076+
.total_tcp_retransmits
1077+
.fetch_add(new_retransmits, Ordering::Relaxed);
10681078
}
10691079
if new_out_of_order > 0 {
1070-
_stats.total_tcp_out_of_order.fetch_add(new_out_of_order, Ordering::Relaxed);
1080+
_stats
1081+
.total_tcp_out_of_order
1082+
.fetch_add(new_out_of_order, Ordering::Relaxed);
10711083
}
10721084
if new_fast_retransmits > 0 {
1073-
_stats.total_tcp_fast_retransmits.fetch_add(new_fast_retransmits, Ordering::Relaxed);
1085+
_stats
1086+
.total_tcp_fast_retransmits
1087+
.fetch_add(new_fast_retransmits, Ordering::Relaxed);
10741088
}
10751089
})
10761090
.or_insert_with(|| {

src/main.rs

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -138,19 +138,19 @@ fn sort_connections(
138138
a_process.cmp(b_process)
139139
}
140140

141-
SortColumn::LocalAddress => {
142-
a.local_addr.to_string().cmp(&b.local_addr.to_string())
143-
}
141+
SortColumn::LocalAddress => a.local_addr.to_string().cmp(&b.local_addr.to_string()),
144142

145-
SortColumn::RemoteAddress => {
146-
a.remote_addr.to_string().cmp(&b.remote_addr.to_string())
147-
}
143+
SortColumn::RemoteAddress => a.remote_addr.to_string().cmp(&b.remote_addr.to_string()),
148144

149145
SortColumn::Application => {
150-
let a_app = a.dpi_info.as_ref()
146+
let a_app = a
147+
.dpi_info
148+
.as_ref()
151149
.map(|dpi| dpi.application.to_string())
152150
.unwrap_or_default();
153-
let b_app = b.dpi_info.as_ref()
151+
let b_app = b
152+
.dpi_info
153+
.as_ref()
154154
.map(|dpi| dpi.application.to_string())
155155
.unwrap_or_default();
156156
a_app.cmp(&b_app)
@@ -162,13 +162,9 @@ fn sort_connections(
162162
a_service.cmp(b_service)
163163
}
164164

165-
SortColumn::State => {
166-
a.state().cmp(&b.state())
167-
}
165+
SortColumn::State => a.state().cmp(&b.state()),
168166

169-
SortColumn::Protocol => {
170-
a.protocol.to_string().cmp(&b.protocol.to_string())
171-
}
167+
SortColumn::Protocol => a.protocol.to_string().cmp(&b.protocol.to_string()),
172168
};
173169

174170
if ascending {
@@ -199,7 +195,11 @@ fn run_ui_loop<B: ratatui::prelude::Backend>(
199195

200196
// Apply sorting (after filtering)
201197
// This sorted list MUST be used for all operations (display + navigation)
202-
sort_connections(&mut connections, ui_state.sort_column, ui_state.sort_ascending);
198+
sort_connections(
199+
&mut connections,
200+
ui_state.sort_column,
201+
ui_state.sort_ascending,
202+
);
203203

204204
let stats = app.get_stats();
205205

@@ -393,10 +393,7 @@ fn run_ui_loop<B: ratatui::prelude::Backend>(
393393
ui_state.quit_confirmation = false;
394394
// Use the SAME sorted connections list from the main loop
395395
// to ensure index consistency with the displayed table
396-
debug!(
397-
"Navigation UP: {} connections available",
398-
connections.len()
399-
);
396+
debug!("Navigation UP: {} connections available", connections.len());
400397
ui_state.move_selection_up(&connections);
401398
}
402399

@@ -468,7 +465,11 @@ fn run_ui_loop<B: ratatui::prelude::Backend>(
468465
info!(
469466
"Sort column: {} ({})",
470467
ui_state.sort_column.display_name(),
471-
if ui_state.sort_ascending { "ascending" } else { "descending" }
468+
if ui_state.sort_ascending {
469+
"ascending"
470+
} else {
471+
"descending"
472+
}
472473
);
473474
}
474475

@@ -478,7 +479,11 @@ fn run_ui_loop<B: ratatui::prelude::Backend>(
478479
ui_state.toggle_sort_direction();
479480
info!(
480481
"Sort direction: {} ({})",
481-
if ui_state.sort_ascending { "ascending" } else { "descending" },
482+
if ui_state.sort_ascending {
483+
"ascending"
484+
} else {
485+
"descending"
486+
},
482487
ui_state.sort_column.display_name()
483488
);
484489
}
@@ -547,13 +552,21 @@ fn check_privileges_early() -> Result<()> {
547552
match network::privileges::check_packet_capture_privileges() {
548553
Ok(status) if !status.has_privileges => {
549554
// Print error to stderr before TUI starts
550-
eprintln!("\n╔═══════════════════════════════════════════════════════════════════════════╗");
551-
eprintln!("║ INSUFFICIENT PRIVILEGES ║");
552-
eprintln!("╚═══════════════════════════════════════════════════════════════════════════╝");
555+
eprintln!(
556+
"\n╔═══════════════════════════════════════════════════════════════════════════╗"
557+
);
558+
eprintln!(
559+
"║ INSUFFICIENT PRIVILEGES ║"
560+
);
561+
eprintln!(
562+
"╚═══════════════════════════════════════════════════════════════════════════╝"
563+
);
553564
eprintln!();
554565
eprintln!("{}", status.error_message());
555566

556-
return Err(anyhow::anyhow!("Insufficient privileges for packet capture"));
567+
return Err(anyhow::anyhow!(
568+
"Insufficient privileges for packet capture"
569+
));
557570
}
558571
Err(e) => {
559572
// Privilege check failed - warn but continue
@@ -578,7 +591,9 @@ fn check_windows_dependencies() -> Result<()> {
578591
let packet_available = check_dll_available("Packet.dll");
579592

580593
if !wpcap_available || !packet_available {
581-
eprintln!("\n╔═══════════════════════════════════════════════════════════════════════════╗");
594+
eprintln!(
595+
"\n╔═══════════════════════════════════════════════════════════════════════════╗"
596+
);
582597
eprintln!("║ MISSING DEPENDENCY ║");
583598
eprintln!("╚═══════════════════════════════════════════════════════════════════════════╝");
584599
eprintln!();
@@ -603,7 +618,9 @@ fn check_windows_dependencies() -> Result<()> {
603618
eprintln!("After installation, restart your terminal and try again.");
604619
eprintln!();
605620

606-
return Err(anyhow!("Npcap is not installed or not in WinPcap compatible mode"));
621+
return Err(anyhow!(
622+
"Npcap is not installed or not in WinPcap compatible mode"
623+
));
607624
}
608625

609626
Ok(())
@@ -626,7 +643,9 @@ fn check_dll_available(dll_name: &str) -> bool {
626643
// Use LoadLibraryA to check if the DLL can be loaded
627644
let handle = LoadLibraryA(PCSTR(dll_cstring.as_ptr() as *const u8));
628645

629-
if let Ok(h) = handle && h != HMODULE(std::ptr::null_mut()) {
646+
if let Ok(h) = handle
647+
&& h != HMODULE(std::ptr::null_mut())
648+
{
630649
// Free the library if it was loaded
631650
let _ = FreeLibrary(h);
632651
true

src/network/dpi/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod quic;
99
mod ssh;
1010

1111
pub use cipher_suites::{format_cipher_suite, is_secure_cipher_suite};
12+
pub use quic::{is_partial_sni, try_extract_tls_from_reassembler};
1213

1314
/// Result of DPI analysis
1415
#[derive(Debug, Clone)]

0 commit comments

Comments
 (0)