From 5aaa518c2e178123cbe97ff9912bb399f81dcb50 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 30 Sep 2024 13:33:28 +0200 Subject: [PATCH 01/16] restructure the layout --- oryx-tui/src/{alerts => }/alert.rs | 7 ++-- oryx-tui/src/{alerts => alert}/syn_flood.rs | 4 +- oryx-tui/src/app.rs | 19 +++------ oryx-tui/src/export.rs | 4 +- oryx-tui/src/{filters => }/filter.rs | 15 ++++--- oryx-tui/src/{filters => filter}/direction.rs | 0 oryx-tui/src/{filters => filter}/fuzzy.rs | 40 ++++++++++++++++++- oryx-tui/src/{filters => filter}/link.rs | 0 oryx-tui/src/{filters => filter}/network.rs | 0 oryx-tui/src/{filters => filter}/transport.rs | 0 oryx-tui/src/lib.rs | 22 ++-------- oryx-tui/src/{packets => }/packet.rs | 13 +++--- oryx-tui/src/{packets => packet}/link.rs | 0 oryx-tui/src/{packets => packet}/network.rs | 0 oryx-tui/src/{packets => packet}/transport.rs | 0 oryx-tui/src/stats.rs | 4 +- 16 files changed, 74 insertions(+), 54 deletions(-) rename oryx-tui/src/{alerts => }/alert.rs (98%) rename oryx-tui/src/{alerts => alert}/syn_flood.rs (99%) rename oryx-tui/src/{filters => }/filter.rs (98%) rename oryx-tui/src/{filters => filter}/direction.rs (100%) rename oryx-tui/src/{filters => filter}/fuzzy.rs (73%) rename oryx-tui/src/{filters => filter}/link.rs (100%) rename oryx-tui/src/{filters => filter}/network.rs (100%) rename oryx-tui/src/{filters => filter}/transport.rs (100%) rename oryx-tui/src/{packets => }/packet.rs (97%) rename oryx-tui/src/{packets => packet}/link.rs (100%) rename oryx-tui/src/{packets => packet}/network.rs (100%) rename oryx-tui/src/{packets => packet}/transport.rs (100%) diff --git a/oryx-tui/src/alerts/alert.rs b/oryx-tui/src/alert.rs similarity index 98% rename from oryx-tui/src/alerts/alert.rs rename to oryx-tui/src/alert.rs index e305902..d0d7543 100644 --- a/oryx-tui/src/alerts/alert.rs +++ b/oryx-tui/src/alert.rs @@ -1,3 +1,5 @@ +mod syn_flood; + use ratatui::{ layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, style::{Color, Style, Stylize}, @@ -6,10 +8,9 @@ use ratatui::{ Frame, }; use std::sync::{atomic::Ordering, Arc, Mutex}; +use syn_flood::SynFlood; -use crate::packets::packet::AppPacket; - -use super::syn_flood::SynFlood; +use crate::packet::AppPacket; #[derive(Debug)] pub struct Alert { diff --git a/oryx-tui/src/alerts/syn_flood.rs b/oryx-tui/src/alert/syn_flood.rs similarity index 99% rename from oryx-tui/src/alerts/syn_flood.rs rename to oryx-tui/src/alert/syn_flood.rs index b50c1f1..c93dfc1 100644 --- a/oryx-tui/src/alerts/syn_flood.rs +++ b/oryx-tui/src/alert/syn_flood.rs @@ -14,9 +14,9 @@ use ratatui::{ Frame, }; -use crate::packets::{ +use crate::packet::{ network::{IpPacket, IpProto}, - packet::AppPacket, + AppPacket, }; const WIN_SIZE: usize = 100_000; diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index c9d57b7..2f1f3bf 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -16,20 +16,11 @@ use std::{ }; use tui_big_text::{BigText, PixelSize}; -use crate::alerts::alert::Alert; -use crate::bandwidth::Bandwidth; -use crate::filters::{ - filter::Filter, - fuzzy::{self, Fuzzy}, -}; -use crate::help::Help; -use crate::interface::Interface; -use crate::notification::Notification; -use crate::packets::{ - network::{IpPacket, IpProto}, - packet::AppPacket, -}; -use crate::stats::Stats; +use crate::{alert::Alert, bandwidth::Bandwidth, filter::fuzzy, packet::network::IpProto}; +use crate::{filter::fuzzy::Fuzzy, notification::Notification}; +use crate::{filter::Filter, help::Help}; +use crate::{interface::Interface, packet::AppPacket}; +use crate::{packet::network::IpPacket, stats::Stats}; pub type AppResult = std::result::Result>; diff --git a/oryx-tui/src/export.rs b/oryx-tui/src/export.rs index e93ac93..78599ca 100644 --- a/oryx-tui/src/export.rs +++ b/oryx-tui/src/export.rs @@ -6,9 +6,9 @@ use std::{ use crate::{ app::AppResult, - packets::{ + packet::{ network::{IpPacket, IpProto}, - packet::AppPacket, + AppPacket, }, }; diff --git a/oryx-tui/src/filters/filter.rs b/oryx-tui/src/filter.rs similarity index 98% rename from oryx-tui/src/filters/filter.rs rename to oryx-tui/src/filter.rs index 0776156..c92650c 100644 --- a/oryx-tui/src/filters/filter.rs +++ b/oryx-tui/src/filter.rs @@ -1,3 +1,12 @@ +pub mod direction; +pub mod fuzzy; +mod link; +mod network; +mod transport; + +use direction::TrafficDirectionFilter; +use link::LinkFilter; +use network::NetworkFilter; use oryx_common::protocols::{ Protocol, NB_LINK_PROTOCOL, NB_NETWORK_PROTOCOL, NB_TRANSPORT_PROTOCOL, }; @@ -8,15 +17,11 @@ use ratatui::{ widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table}, Frame, }; +use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; use crate::app::FocusedBlock; -use super::{ - direction::TrafficDirectionFilter, link::LinkFilter, network::NetworkFilter, - transport::TransportFilter, -}; - #[derive(Debug, Clone)] pub struct FilterChannel { pub sender: kanal::Sender<(Protocol, bool)>, diff --git a/oryx-tui/src/filters/direction.rs b/oryx-tui/src/filter/direction.rs similarity index 100% rename from oryx-tui/src/filters/direction.rs rename to oryx-tui/src/filter/direction.rs diff --git a/oryx-tui/src/filters/fuzzy.rs b/oryx-tui/src/filter/fuzzy.rs similarity index 73% rename from oryx-tui/src/filters/fuzzy.rs rename to oryx-tui/src/filter/fuzzy.rs index e26af39..124b45a 100644 --- a/oryx-tui/src/filters/fuzzy.rs +++ b/oryx-tui/src/filter/fuzzy.rs @@ -11,7 +11,7 @@ use ratatui::{ }; use tui_input::Input; -use crate::{app::TICK_RATE, packets::packet::AppPacket}; +use crate::{app::TICK_RATE, packet::AppPacket}; #[derive(Debug, Clone, Default)] pub struct Fuzzy { @@ -56,6 +56,44 @@ impl Fuzzy { fuzzy } + pub fn scroll_down(&mut self, win_size: usize) { + let i = match self.scroll_state.selected() { + Some(i) => { + if i < win_size - 1 { + i + 1 + } else if i == win_size - 1 && self.packets.len() > self.packet_end_index { + // shit the window by one + self.packet_end_index += 1; + i + 1 + } else { + i + } + } + None => self.packets.len(), + }; + + self.scroll_state.select(Some(i)); + } + + pub fn scroll_up(&mut self, win_size: usize) { + let i = match self.scroll_state.selected() { + Some(i) => { + if i > 1 { + i - 1 + } else if i == 0 && self.packet_end_index > win_size { + // shit the window by one + self.packet_end_index -= 1; + 0 + } else { + 0 + } + } + None => self.packets.len(), + }; + + self.scroll_state.select(Some(i)); + } + pub fn find(&mut self, packets: &[AppPacket]) { self.packets = packets .iter() diff --git a/oryx-tui/src/filters/link.rs b/oryx-tui/src/filter/link.rs similarity index 100% rename from oryx-tui/src/filters/link.rs rename to oryx-tui/src/filter/link.rs diff --git a/oryx-tui/src/filters/network.rs b/oryx-tui/src/filter/network.rs similarity index 100% rename from oryx-tui/src/filters/network.rs rename to oryx-tui/src/filter/network.rs diff --git a/oryx-tui/src/filters/transport.rs b/oryx-tui/src/filter/transport.rs similarity index 100% rename from oryx-tui/src/filters/transport.rs rename to oryx-tui/src/filter/transport.rs diff --git a/oryx-tui/src/lib.rs b/oryx-tui/src/lib.rs index 2534f24..f1efc6d 100644 --- a/oryx-tui/src/lib.rs +++ b/oryx-tui/src/lib.rs @@ -14,14 +14,7 @@ pub mod interface; pub mod ebpf; -pub mod filters { - pub mod direction; - pub mod filter; - pub mod fuzzy; - pub mod link; - pub mod network; - pub mod transport; -} +pub mod filter; pub mod notification; @@ -31,14 +24,5 @@ pub mod stats; pub mod bandwidth; -pub mod packets { - pub mod link; - pub mod network; - pub mod packet; - pub mod transport; -} - -pub mod alerts { - pub mod alert; - pub mod syn_flood; -} +pub mod alert; +pub mod packet; diff --git a/oryx-tui/src/packets/packet.rs b/oryx-tui/src/packet.rs similarity index 97% rename from oryx-tui/src/packets/packet.rs rename to oryx-tui/src/packet.rs index 79e3cb0..543469d 100644 --- a/oryx-tui/src/packets/packet.rs +++ b/oryx-tui/src/packet.rs @@ -1,13 +1,14 @@ +pub mod link; +pub mod network; +pub mod transport; + use std::{fmt::Display, mem, net::Ipv4Addr}; +use link::{ArpPacket, ArpType, MacAddr}; +use network::{IcmpPacket, IcmpType, IpPacket, IpProto, Ipv4Packet, Ipv6Packet}; use network_types::ip::IpHdr; use oryx_common::{ProtoHdr, RawPacket}; - -use super::{ - link::{ArpPacket, ArpType, MacAddr}, - network::{IcmpPacket, IcmpType, IpPacket, IpProto, Ipv4Packet, Ipv6Packet}, - transport::{TcpPacket, UdpPacket}, -}; +use transport::{TcpPacket, UdpPacket}; #[derive(Debug, Copy, Clone)] pub enum AppPacket { diff --git a/oryx-tui/src/packets/link.rs b/oryx-tui/src/packet/link.rs similarity index 100% rename from oryx-tui/src/packets/link.rs rename to oryx-tui/src/packet/link.rs diff --git a/oryx-tui/src/packets/network.rs b/oryx-tui/src/packet/network.rs similarity index 100% rename from oryx-tui/src/packets/network.rs rename to oryx-tui/src/packet/network.rs diff --git a/oryx-tui/src/packets/transport.rs b/oryx-tui/src/packet/transport.rs similarity index 100% rename from oryx-tui/src/packets/transport.rs rename to oryx-tui/src/packet/transport.rs diff --git a/oryx-tui/src/stats.rs b/oryx-tui/src/stats.rs index 0555cc6..a941acc 100644 --- a/oryx-tui/src/stats.rs +++ b/oryx-tui/src/stats.rs @@ -12,9 +12,9 @@ use ratatui::{ Frame, }; -use crate::packets::{ +use crate::packet::{ network::{IpPacket, IpProto}, - packet::AppPacket, + AppPacket, }; #[derive(Debug)] From 196339b40f0fe98fca176dddcf1bb826727f2d67 Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 30 Sep 2024 13:33:47 +0200 Subject: [PATCH 02/16] move fuzzy scrolls to fuzzy struct --- oryx-tui/src/handler.rs | 40 +++------------------------------------- 1 file changed, 3 insertions(+), 37 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 8efee88..a3af93a 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -7,7 +7,7 @@ use crate::{ ebpf::Ebpf, event::Event, export::export, - filters::direction::TrafficDirection, + filter::direction::TrafficDirection, notification::{Notification, NotificationLevel}, }; use ratatui::{ @@ -207,24 +207,7 @@ pub fn handle_key_events( // Record the last position. Usefull for selecting the packets to display fuzzy.packet_end_index = fuzzy.packets.len(); } - let i = match fuzzy.scroll_state.selected() { - Some(i) => { - if i < app.packet_window_size - 1 { - i + 1 - } else if i == app.packet_window_size - 1 - && fuzzy.packets.len() > fuzzy.packet_end_index - { - // shit the window by one - fuzzy.packet_end_index += 1; - i + 1 - } else { - i - } - } - None => fuzzy.packets.len(), - }; - - fuzzy.scroll_state.select(Some(i)); + fuzzy.scroll_down(app.packet_window_size); } else { match &app.focused_block { FocusedBlock::NetworkFilter => { @@ -254,24 +237,7 @@ pub fn handle_key_events( // Record the last position. Usefull for selecting the packets to display fuzzy.packet_end_index = fuzzy.packets.len(); } - let i = match fuzzy.scroll_state.selected() { - Some(i) => { - if i > 1 { - i - 1 - } else if i == 0 - && fuzzy.packet_end_index > app.packet_window_size - { - // shit the window by one - fuzzy.packet_end_index -= 1; - 0 - } else { - 0 - } - } - None => fuzzy.packets.len(), - }; - - fuzzy.scroll_state.select(Some(i)); + fuzzy.scroll_up(app.packet_window_size); } else { match &app.focused_block { FocusedBlock::NetworkFilter => { From 5a841038fba208881b43b9656fb04327734e12ef Mon Sep 17 00:00:00 2001 From: Badr Date: Mon, 30 Sep 2024 16:12:11 +0200 Subject: [PATCH 03/16] rename Mode to Section --- oryx-tui/src/app.rs | 14 +++++++------- oryx-tui/src/handler.rs | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 2f1f3bf..a69a50f 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -39,7 +39,7 @@ pub enum FocusedBlock { } #[derive(Debug, PartialEq)] -pub enum Mode { +pub enum Section { Packet, Stats, Alerts, @@ -66,7 +66,7 @@ pub struct App { pub fuzzy: Arc>, pub notifications: Vec, pub manuall_scroll: bool, - pub mode: Mode, + pub section: Section, pub stats: Arc>, pub packet_end_index: usize, pub packet_window_size: usize, @@ -115,7 +115,7 @@ impl App { fuzzy: Fuzzy::new(packets.clone()), notifications: Vec::new(), manuall_scroll: false, - mode: Mode::Packet, + section: Section::Packet, stats, packet_end_index: 0, packet_window_size: 0, @@ -191,15 +191,15 @@ impl App { self.filter.render_on_sniffing(frame, filter_block); // Packets/Stats - match self.mode { - Mode::Packet => { + match self.section { + Section::Packet => { self.render_packets_mode(frame, mode_block); if self.show_packet_infos_popup { self.render_packet_infos_popup(frame); } } - Mode::Stats => self.render_stats_mode(frame, mode_block), - Mode::Alerts => self.alert.render(frame, mode_block), + Section::Stats => self.render_stats_mode(frame, mode_block), + Section::Alerts => self.alert.render(frame, mode_block), } // Update filters diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index a3af93a..59993f0 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -3,7 +3,7 @@ use std::{thread, time::Duration}; use tui_input::backend::crossterm::EventHandler; use crate::{ - app::{App, AppResult, FocusedBlock, Mode}, + app::{App, AppResult, FocusedBlock, Section}, ebpf::Ebpf, event::Event, export::export, @@ -97,10 +97,10 @@ pub fn handle_key_events( return Ok(()); } - match app.mode { - Mode::Packet => app.mode = Mode::Stats, - Mode::Stats => app.mode = Mode::Alerts, - Mode::Alerts => app.mode = Mode::Packet, + match app.section { + Section::Packet => app.section = Section::Stats, + Section::Stats => app.section = Section::Alerts, + Section::Alerts => app.section = Section::Packet, } } @@ -668,10 +668,10 @@ pub fn handle_key_events( return Ok(()); } - match app.mode { - Mode::Packet => app.mode = Mode::Stats, - Mode::Stats => app.mode = Mode::Alerts, - Mode::Alerts => app.mode = Mode::Packet, + match app.section { + Section::Packet => app.section = Section::Stats, + Section::Stats => app.section = Section::Alerts, + Section::Alerts => app.section = Section::Packet, }; } else { match &app.focused_block { @@ -759,10 +759,10 @@ pub fn handle_key_events( return Ok(()); }; - match app.mode { - Mode::Packet => app.mode = Mode::Alerts, - Mode::Stats => app.mode = Mode::Packet, - Mode::Alerts => app.mode = Mode::Stats, + match app.section { + Section::Packet => app.section = Section::Alerts, + Section::Stats => app.section = Section::Packet, + Section::Alerts => app.section = Section::Stats, }; } else { match &app.focused_block { From 4b80ae5845b453c1155b61fd62f547203222cdeb Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 1 Oct 2024 10:20:47 +0200 Subject: [PATCH 04/16] refactor handler + filter --- oryx-tui/src/app.rs | 111 +--- oryx-tui/src/filter.rs | 528 ++++++++++++++- oryx-tui/src/filter/direction.rs | 6 +- oryx-tui/src/filter/link.rs | 6 +- oryx-tui/src/filter/network.rs | 6 +- oryx-tui/src/filter/transport.rs | 6 +- oryx-tui/src/handler.rs | 1071 ++++++------------------------ oryx-tui/src/interface.rs | 11 +- oryx-tui/src/ui.rs | 11 +- 9 files changed, 737 insertions(+), 1019 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index a69a50f..d6ac3b7 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -14,30 +14,17 @@ use std::{ sync::{Arc, Mutex}, thread, }; -use tui_big_text::{BigText, PixelSize}; +use crate::packet::AppPacket; use crate::{alert::Alert, bandwidth::Bandwidth, filter::fuzzy, packet::network::IpProto}; use crate::{filter::fuzzy::Fuzzy, notification::Notification}; use crate::{filter::Filter, help::Help}; -use crate::{interface::Interface, packet::AppPacket}; use crate::{packet::network::IpPacket, stats::Stats}; pub type AppResult = std::result::Result>; pub const TICK_RATE: u64 = 40; -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum FocusedBlock { - Interface, - TransportFilter, - NetworkFilter, - LinkFilter, - TrafficDirection, - Start, - Help, - Main, -} - #[derive(Debug, PartialEq)] pub enum Section { Packet, @@ -45,6 +32,13 @@ pub enum Section { Alerts, } +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ActivePopup { + Help, + UpdateFilters, + PacketInfos, +} + #[derive(Debug)] pub struct DataEventHandler { pub sender: kanal::Sender<[u8; RawPacket::LEN]>, @@ -55,10 +49,6 @@ pub struct DataEventHandler { pub struct App { pub running: bool, pub help: Help, - pub focused_block: FocusedBlock, - // used in setup to know which block to fall into after discarding help - pub previous_focused_block: FocusedBlock, - pub interface: Interface, pub filter: Filter, pub start_sniffing: bool, pub packets: Arc>>, @@ -70,12 +60,12 @@ pub struct App { pub stats: Arc>, pub packet_end_index: usize, pub packet_window_size: usize, - pub update_filters: bool, pub data_channel_sender: kanal::Sender<[u8; RawPacket::LEN]>, pub bandwidth: Bandwidth, - pub show_packet_infos_popup: bool, pub packet_index: Option, pub alert: Alert, + pub is_editing: bool, + pub active_popup: Option, } impl Default for App { @@ -105,9 +95,6 @@ impl App { Self { running: true, help: Help::new(), - focused_block: FocusedBlock::Interface, - previous_focused_block: FocusedBlock::Interface, - interface: Interface::default(), filter: Filter::new(), start_sniffing: false, packets: packets.clone(), @@ -119,94 +106,36 @@ impl App { stats, packet_end_index: 0, packet_window_size: 0, - update_filters: false, data_channel_sender: sender, bandwidth: Bandwidth::new(), - show_packet_infos_popup: false, packet_index: None, alert: Alert::new(packets.clone()), + is_editing: false, + active_popup: None, } } pub fn render(&mut self, frame: &mut Frame) { // Setup if !self.start_sniffing { - let (interface_block, filter_block, start_block) = { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(self.interface.interfaces.len() as u16 + 6), - Constraint::Fill(1), - Constraint::Length(4), - ]) - .margin(1) - .flex(Flex::SpaceAround) - .split(frame.area()); - (chunks[0], chunks[1], chunks[2]) - }; - - // interfaces - self.interface - .render_on_setup(frame, interface_block, &self.focused_block); - - // Filters - self.filter - .render_on_setup(frame, filter_block, &self.focused_block); - - // Start Button - let start = BigText::builder() - .pixel_size(PixelSize::Sextant) - .style(if self.focused_block == FocusedBlock::Start { - Style::default().white().bold() - } else { - Style::default().dark_gray() - }) - .lines(vec!["START".into()]) - .centered() - .build(); - frame.render_widget(start, start_block); + self.filter.render_on_setup(frame); } else { // Sniffing - let (settings_block, mode_block) = { + let (settings_block, section_block) = { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(8), Constraint::Fill(1)]) .split(frame.area()); (chunks[0], chunks[1]) }; - // Settings - let (filter_block, interface_block) = { - let chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) - .margin(1) - .split(settings_block); - (chunks[0], chunks[1]) - }; - - // Interface - self.interface.render_on_sniffing(frame, interface_block); - - // Filters - self.filter.render_on_sniffing(frame, filter_block); // Packets/Stats match self.section { - Section::Packet => { - self.render_packets_mode(frame, mode_block); - if self.show_packet_infos_popup { - self.render_packet_infos_popup(frame); - } - } - Section::Stats => self.render_stats_mode(frame, mode_block), - Section::Alerts => self.alert.render(frame, mode_block), - } - - // Update filters - - if self.update_filters { - self.filter.update(frame, mode_block, &self.focused_block); + Section::Packet => self.render_packets_mode(frame, section_block), + Section::Stats => self.render_stats_mode(frame, section_block), + Section::Alerts => self.alert.render(frame, section_block), } + self.filter.render_on_sniffing(frame, settings_block); } } @@ -652,11 +581,11 @@ impl App { self.bandwidth.render( frame, bandwidth_block, - &self.interface.selected_interface.name.clone(), + &self.filter.interface.selected_interface.name.clone(), ); } - fn render_packet_infos_popup(&self, frame: &mut Frame) { + pub fn render_packet_infos_popup(&self, frame: &mut Frame) { let layout = Layout::default() .direction(Direction::Vertical) .constraints([ diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index c92650c..3a23e94 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -4,23 +4,30 @@ mod link; mod network; mod transport; -use direction::TrafficDirectionFilter; +use std::{thread, time::Duration}; + +use crossterm::event::{KeyCode, KeyEvent}; +use direction::{TrafficDirection, TrafficDirectionFilter}; use link::LinkFilter; use network::NetworkFilter; -use oryx_common::protocols::{ - Protocol, NB_LINK_PROTOCOL, NB_NETWORK_PROTOCOL, NB_TRANSPORT_PROTOCOL, +use oryx_common::{ + protocols::{ + LinkProtocol, NetworkProtocol, Protocol, TransportProtocol, NB_LINK_PROTOCOL, + NB_NETWORK_PROTOCOL, NB_TRANSPORT_PROTOCOL, + }, + RawPacket, }; use ratatui::{ layout::{Alignment, Constraint, Direction, Flex, Layout, Rect}, style::{Style, Stylize}, text::{Line, Span}, - widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table}, + widgets::{Block, BorderType, Borders, Clear, Padding, Row, Table, TableState}, Frame, }; use transport::TransportFilter; use tui_big_text::{BigText, PixelSize}; -use crate::app::FocusedBlock; +use crate::{app::AppResult, ebpf::Ebpf, event::Event, interface::Interface}; #[derive(Debug, Clone)] pub struct FilterChannel { @@ -41,14 +48,27 @@ impl Default for FilterChannel { } } +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum FocusedBlock { + Interface, + TransportFilter, + NetworkFilter, + LinkFilter, + TrafficDirection, + Apply, +} + #[derive(Debug)] pub struct Filter { + pub interface: Interface, pub network: NetworkFilter, pub transport: TransportFilter, pub link: LinkFilter, pub traffic_direction: TrafficDirectionFilter, pub ingress_channel: FilterChannel, pub egress_channel: FilterChannel, + focused_block: FocusedBlock, + show_popup: bool, } impl Default for Filter { @@ -60,54 +80,487 @@ impl Default for Filter { impl Filter { pub fn new() -> Self { Self { + interface: Interface::new(), network: NetworkFilter::new(), transport: TransportFilter::new(), link: LinkFilter::new(), traffic_direction: TrafficDirectionFilter::new(), ingress_channel: FilterChannel::new(), egress_channel: FilterChannel::new(), + focused_block: FocusedBlock::Interface, + show_popup: false, } } - pub fn render_on_setup( + pub fn terminate(&mut self) { + self.traffic_direction.terminate(TrafficDirection::Egress); + self.traffic_direction.terminate(TrafficDirection::Ingress); + } + + pub fn start( &mut self, - frame: &mut Frame, - block: Rect, - focused_block: &FocusedBlock, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, ) { + let iface = self.interface.selected_interface.name.clone(); + + self.apply(); + + if self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Ingress) + { + Ebpf::load_ingress( + iface.clone(), + notification_sender.clone(), + data_sender.clone(), + self.ingress_channel.receiver.clone(), + self.traffic_direction.terminate_ingress.clone(), + ); + } + + if self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Egress) + { + Ebpf::load_egress( + iface, + notification_sender, + data_sender, + self.egress_channel.receiver.clone(), + self.traffic_direction.terminate_egress.clone(), + ); + } + } + + pub fn trigger(&mut self) { + self.show_popup = true; + + self.network.selected_protocols = self.network.applied_protocols.clone(); + + self.transport.selected_protocols = self.transport.applied_protocols.clone(); + + self.link.selected_protocols = self.link.applied_protocols.clone(); + + self.traffic_direction.selected_direction = + self.traffic_direction.applied_direction.clone(); + + self.transport.state = TableState::default().with_selected(0); + + self.focused_block = FocusedBlock::TransportFilter; + } + + pub fn sync(&mut self) -> AppResult<()> { + for protocol in TransportProtocol::all().iter() { + if self.transport.applied_protocols.contains(protocol) { + self.ingress_channel + .sender + .send((Protocol::Transport(*protocol), false))?; + self.egress_channel + .sender + .send((Protocol::Transport(*protocol), false))?; + } else { + self.ingress_channel + .sender + .send((Protocol::Transport(*protocol), true))?; + self.egress_channel + .sender + .send((Protocol::Transport(*protocol), true))?; + } + } + + for protocol in NetworkProtocol::all().iter() { + if self.network.applied_protocols.contains(protocol) { + self.ingress_channel + .sender + .send((Protocol::Network(*protocol), false))?; + self.egress_channel + .sender + .send((Protocol::Network(*protocol), false))?; + } else { + self.ingress_channel + .sender + .send((Protocol::Network(*protocol), true))?; + self.egress_channel + .sender + .send((Protocol::Network(*protocol), true))?; + } + } + + for protocol in LinkProtocol::all().iter() { + if self.link.applied_protocols.contains(protocol) { + self.ingress_channel + .sender + .send((Protocol::Link(*protocol), false))?; + self.egress_channel + .sender + .send((Protocol::Link(*protocol), false))?; + } else { + self.ingress_channel + .sender + .send((Protocol::Link(*protocol), true))?; + self.egress_channel + .sender + .send((Protocol::Link(*protocol), true))?; + } + } + + Ok(()) + } + + pub fn update( + &mut self, + notification_sender: kanal::Sender, + data_sender: kanal::Sender<[u8; RawPacket::LEN]>, + ) -> AppResult<()> { + // Remove egress + if self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Egress) + && !self + .traffic_direction + .selected_direction + .contains(&TrafficDirection::Egress) + { + self.traffic_direction.terminate(TrafficDirection::Egress); + } + + // Add egress + if !self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Egress) + && self + .traffic_direction + .selected_direction + .contains(&TrafficDirection::Egress) + { + self.traffic_direction + .terminate_egress + .store(false, std::sync::atomic::Ordering::Relaxed); + + let iface = self.interface.selected_interface.name.clone(); + + Ebpf::load_egress( + iface, + notification_sender.clone(), + data_sender.clone(), + self.egress_channel.receiver.clone(), + self.traffic_direction.terminate_egress.clone(), + ); + } + + // Remove ingress + if self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Ingress) + && !self + .traffic_direction + .selected_direction + .contains(&TrafficDirection::Ingress) + { + self.traffic_direction.terminate(TrafficDirection::Ingress); + } + + // Add ingress + if !self + .traffic_direction + .applied_direction + .contains(&TrafficDirection::Ingress) + && self + .traffic_direction + .selected_direction + .contains(&TrafficDirection::Ingress) + { + let iface = self.interface.selected_interface.name.clone(); + self.traffic_direction + .terminate_ingress + .store(false, std::sync::atomic::Ordering::Relaxed); + Ebpf::load_ingress( + iface, + notification_sender.clone(), + data_sender.clone(), + self.ingress_channel.receiver.clone(), + self.traffic_direction.terminate_ingress.clone(), + ); + } + + self.apply(); + + //TODO: why ? + thread::sleep(Duration::from_millis(150)); + + self.traffic_direction + .terminate_ingress + .store(false, std::sync::atomic::Ordering::Relaxed); + self.traffic_direction + .terminate_ingress + .store(false, std::sync::atomic::Ordering::Relaxed); + + self.sync()?; + self.show_popup = false; + + Ok(()) + } + + pub fn handle_key_events(&mut self, key_event: KeyEvent) { + match key_event.code { + KeyCode::Esc => { + if self.show_popup { + self.show_popup = false + } + } + + KeyCode::Tab => match self.focused_block { + FocusedBlock::Interface => { + self.focused_block = FocusedBlock::TransportFilter; + self.interface.state.select(None); + self.transport.state.select(Some(0)); + } + FocusedBlock::TransportFilter => { + self.focused_block = FocusedBlock::NetworkFilter; + self.network.state.select(Some(0)); + self.transport.state.select(None); + } + + FocusedBlock::NetworkFilter => { + self.focused_block = FocusedBlock::LinkFilter; + self.link.state.select(Some(0)); + self.network.state.select(None); + } + + FocusedBlock::LinkFilter => { + self.focused_block = FocusedBlock::TrafficDirection; + self.traffic_direction.state.select(Some(0)); + self.link.state.select(None); + } + + FocusedBlock::TrafficDirection => { + self.focused_block = FocusedBlock::Apply; + self.traffic_direction.state.select(None); + } + + FocusedBlock::Apply => { + if self.show_popup { + self.focused_block = FocusedBlock::TransportFilter; + } else { + self.focused_block = FocusedBlock::Interface; + self.interface.state.select(Some(0)); + } + } + }, + KeyCode::BackTab => match &self.focused_block { + FocusedBlock::Interface => { + self.focused_block = FocusedBlock::Apply; + self.interface.state.select(None); + } + + FocusedBlock::TransportFilter => { + if self.show_popup { + self.focused_block = FocusedBlock::Apply; + self.transport.state.select(None); + } else { + self.focused_block = FocusedBlock::Interface; + self.interface.state.select(Some(0)); + self.transport.state.select(None); + } + } + + FocusedBlock::NetworkFilter => { + self.focused_block = FocusedBlock::TransportFilter; + self.transport.state.select(Some(0)); + self.network.state.select(None); + } + + FocusedBlock::LinkFilter => { + self.focused_block = FocusedBlock::NetworkFilter; + self.network.state.select(Some(0)); + self.link.state.select(None); + } + + FocusedBlock::TrafficDirection => { + self.focused_block = FocusedBlock::LinkFilter; + self.link.state.select(Some(0)); + self.traffic_direction.state.select(None); + } + + FocusedBlock::Apply => { + self.focused_block = FocusedBlock::TrafficDirection; + self.traffic_direction.state.select(Some(0)); + } + }, + + KeyCode::Char('j') | KeyCode::Down => match &self.focused_block { + FocusedBlock::Interface => { + self.interface.scroll_down(); + } + FocusedBlock::TransportFilter => { + self.transport.scroll_down(); + } + + FocusedBlock::NetworkFilter => { + self.network.scroll_down(); + } + + FocusedBlock::LinkFilter => { + self.link.scroll_down(); + } + + FocusedBlock::TrafficDirection => { + self.traffic_direction.state.select(Some(1)); + } + _ => {} + }, + + KeyCode::Char('k') | KeyCode::Up => match self.focused_block { + FocusedBlock::Interface => { + self.interface.scroll_up(); + } + FocusedBlock::TransportFilter => { + self.transport.scroll_up(); + } + + FocusedBlock::NetworkFilter => { + self.network.scroll_up(); + } + + FocusedBlock::LinkFilter => { + self.link.scroll_up(); + } + + FocusedBlock::TrafficDirection => { + self.traffic_direction.state.select(Some(0)); + } + _ => {} + }, + + KeyCode::Char(' ') => match &self.focused_block { + FocusedBlock::Interface => { + if let Some(index) = self.interface.state.selected() { + let net_interface = self.interface.interfaces[index].clone(); + if net_interface.is_up { + self.interface.selected_interface = + self.interface.interfaces[index].clone(); + } + } + } + FocusedBlock::NetworkFilter => { + self.network.select(); + } + + FocusedBlock::TransportFilter => { + self.transport.select(); + } + + FocusedBlock::LinkFilter => { + self.link.select(); + } + + FocusedBlock::TrafficDirection => { + self.traffic_direction.select(); + } + + _ => {} + }, + + _ => {} + } + } + + pub fn apply(&mut self) { + self.network.apply(); + self.transport.apply(); + self.link.apply(); + self.traffic_direction.apply(); + } + + pub fn render_on_setup(&mut self, frame: &mut Frame) { let ( + interface_block, transport_filter_block, network_filter_block, link_filter_block, traffic_direction_block, + start_block, ) = { let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ + Constraint::Length(self.interface.interfaces.len() as u16 + 6), Constraint::Length(NB_TRANSPORT_PROTOCOL + 4), Constraint::Length(NB_NETWORK_PROTOCOL + 4), Constraint::Length(NB_LINK_PROTOCOL + 4), Constraint::Length(6), + Constraint::Length(4), ]) .margin(1) .flex(Flex::SpaceAround) - .split(block); - (chunks[0], chunks[1], chunks[2], chunks[3]) + .split(frame.area()); + ( + chunks[0], chunks[1], chunks[2], chunks[3], chunks[4], chunks[5], + ) }; - self.network - .render(frame, network_filter_block, focused_block); + self.interface.render_on_setup( + frame, + interface_block, + self.focused_block == FocusedBlock::Interface, + ); - self.transport - .render(frame, transport_filter_block, focused_block); + self.network.render( + frame, + network_filter_block, + self.focused_block == FocusedBlock::NetworkFilter, + ); - self.link.render(frame, link_filter_block, focused_block); + self.transport.render( + frame, + transport_filter_block, + self.focused_block == FocusedBlock::TransportFilter, + ); - self.traffic_direction - .render(frame, traffic_direction_block, focused_block); + self.link.render( + frame, + link_filter_block, + self.focused_block == FocusedBlock::LinkFilter, + ); + + self.traffic_direction.render( + frame, + traffic_direction_block, + self.focused_block == FocusedBlock::TrafficDirection, + ); + + let start = BigText::builder() + .pixel_size(PixelSize::Sextant) + .style(if self.focused_block == FocusedBlock::Apply { + Style::default().white().bold() + } else { + Style::default().dark_gray() + }) + .lines(vec!["START".into()]) + .centered() + .build(); + + frame.render_widget(start, start_block); } pub fn render_on_sniffing(&mut self, frame: &mut Frame, block: Rect) { + let (filter_summury_block, interface_block) = { + let chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) + .margin(1) + .split(block); + (chunks[0], chunks[1]) + }; + + self.interface.render_on_sniffing(frame, interface_block); + let widths = [Constraint::Length(10), Constraint::Fill(1)]; let filters = { [ @@ -196,10 +649,14 @@ impl Filter { .border_style(Style::default().green()), ); - frame.render_widget(table, block); + frame.render_widget(table, filter_summury_block); + + if self.show_popup { + self.render_update_popup(frame); + } } - pub fn update(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { + pub fn render_update_popup(&mut self, frame: &mut Frame) { let layout = Layout::default() .direction(Direction::Vertical) .constraints([ @@ -208,7 +665,7 @@ impl Filter { Constraint::Fill(1), ]) .flex(ratatui::layout::Flex::SpaceBetween) - .split(block); + .split(frame.area()); let block = Layout::default() .direction(Direction::Horizontal) @@ -251,20 +708,33 @@ impl Filter { block, ); - self.transport - .render(frame, transport_filter_block, focused_block); + self.network.render( + frame, + network_filter_block, + self.focused_block == FocusedBlock::NetworkFilter, + ); - self.network - .render(frame, network_filter_block, focused_block); + self.transport.render( + frame, + transport_filter_block, + self.focused_block == FocusedBlock::TransportFilter, + ); - self.link.render(frame, link_filter_block, focused_block); + self.link.render( + frame, + link_filter_block, + self.focused_block == FocusedBlock::LinkFilter, + ); - self.traffic_direction - .render(frame, traffic_direction_block, focused_block); + self.traffic_direction.render( + frame, + traffic_direction_block, + self.focused_block == FocusedBlock::TrafficDirection, + ); let apply = BigText::builder() .pixel_size(PixelSize::Sextant) - .style(if *focused_block == FocusedBlock::Start { + .style(if self.focused_block == FocusedBlock::Apply { Style::default().white().bold() } else { Style::default().dark_gray() diff --git a/oryx-tui/src/filter/direction.rs b/oryx-tui/src/filter/direction.rs index 9107a40..95104ce 100644 --- a/oryx-tui/src/filter/direction.rs +++ b/oryx-tui/src/filter/direction.rs @@ -13,8 +13,6 @@ use ratatui::{ Frame, }; -use crate::app::FocusedBlock; - #[derive(Debug)] pub struct TrafficDirectionFilter { pub state: TableState, @@ -84,7 +82,7 @@ impl TrafficDirectionFilter { self.selected_direction.clear(); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { + pub fn render(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) .constraints([ @@ -130,7 +128,7 @@ impl TrafficDirectionFilter { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::TrafficDirection { + .border_type(if is_focused { BorderType::Thick } else { BorderType::default() diff --git a/oryx-tui/src/filter/link.rs b/oryx-tui/src/filter/link.rs index 2623ac3..cfe09cd 100644 --- a/oryx-tui/src/filter/link.rs +++ b/oryx-tui/src/filter/link.rs @@ -6,8 +6,6 @@ use ratatui::{ Frame, }; -use crate::app::FocusedBlock; - #[derive(Debug)] pub struct LinkFilter { pub state: TableState, @@ -75,7 +73,7 @@ impl LinkFilter { self.selected_protocols.clear(); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { + pub fn render(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) .constraints( @@ -112,7 +110,7 @@ impl LinkFilter { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::LinkFilter { + .border_type(if is_focused { BorderType::Thick } else { BorderType::default() diff --git a/oryx-tui/src/filter/network.rs b/oryx-tui/src/filter/network.rs index 1399953..0bac490 100644 --- a/oryx-tui/src/filter/network.rs +++ b/oryx-tui/src/filter/network.rs @@ -6,8 +6,6 @@ use ratatui::{ Frame, }; -use crate::app::FocusedBlock; - #[derive(Debug)] pub struct NetworkFilter { pub state: TableState, @@ -85,7 +83,7 @@ impl NetworkFilter { self.selected_protocols.clear(); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { + pub fn render(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) .constraints( @@ -144,7 +142,7 @@ impl NetworkFilter { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::NetworkFilter { + .border_type(if is_focused { BorderType::Thick } else { BorderType::default() diff --git a/oryx-tui/src/filter/transport.rs b/oryx-tui/src/filter/transport.rs index 1a0da9c..47fac48 100644 --- a/oryx-tui/src/filter/transport.rs +++ b/oryx-tui/src/filter/transport.rs @@ -6,8 +6,6 @@ use ratatui::{ Frame, }; -use crate::app::FocusedBlock; - #[derive(Debug)] pub struct TransportFilter { pub state: TableState, @@ -80,7 +78,7 @@ impl TransportFilter { self.selected_protocols.clear(); } - pub fn render(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { + pub fn render(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) .constraints( @@ -129,7 +127,7 @@ impl TransportFilter { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::TransportFilter { + .border_type(if is_focused { BorderType::Thick } else { BorderType::default() diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 59993f0..43f4627 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -1,21 +1,16 @@ -use oryx_common::protocols::{LinkProtocol, NetworkProtocol, Protocol, TransportProtocol}; use std::{thread, time::Duration}; use tui_input::backend::crossterm::EventHandler; use crate::{ - app::{App, AppResult, FocusedBlock, Section}, - ebpf::Ebpf, + app::{ActivePopup, App, AppResult, Section}, event::Event, export::export, filter::direction::TrafficDirection, notification::{Notification, NotificationLevel}, }; -use ratatui::{ - crossterm::{ - self, - event::{KeyCode, KeyEvent, KeyModifiers}, - }, - widgets::TableState, +use ratatui::crossterm::{ + self, + event::{KeyCode, KeyEvent, KeyModifiers}, }; pub fn handle_key_events( @@ -23,937 +18,273 @@ pub fn handle_key_events( app: &mut App, sender: kanal::Sender, ) -> AppResult<()> { - if app.show_packet_infos_popup { - if key_event.code == KeyCode::Esc { - app.show_packet_infos_popup = false; - } - - return Ok(()); - } - - let fuzzy = app.fuzzy.clone(); - let mut fuzzy = fuzzy.lock().unwrap(); - - if fuzzy.is_enabled() { + // Start Phase + if !app.start_sniffing { match key_event.code { - KeyCode::Esc => { - if app.focused_block == FocusedBlock::Help { - app.focused_block = FocusedBlock::Main; - return Ok(()); - } - - if app.update_filters { - app.update_filters = false; - return Ok(()); - } + KeyCode::Enter => { + app.filter + .start(sender.clone(), app.data_channel_sender.clone()); - if fuzzy.is_paused() { - if app.manuall_scroll { - app.manuall_scroll = false; - } else { - fuzzy.disable(); - } - } else { - fuzzy.pause(); - } + app.start_sniffing = true; } - KeyCode::Tab => { - if app.focused_block == FocusedBlock::Help { - return Ok(()); - } - - if app.update_filters { - match &app.focused_block { - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.filter.network.state.select(Some(0)); - app.filter.transport.state.select(Some(0)); - } - - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::LinkFilter; - app.filter.link.state.select(Some(0)); - app.filter.network.state.select(None); - } - - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::TrafficDirection; - app.filter.traffic_direction.state.select(Some(0)); - app.filter.link.state.select(None); - } - - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::Start; - app.filter.traffic_direction.state.select(None); - } - - FocusedBlock::Start => { - app.focused_block = FocusedBlock::TransportFilter; - } - _ => {} - }; - - return Ok(()); - } - - match app.section { - Section::Packet => app.section = Section::Stats, - Section::Stats => app.section = Section::Alerts, - Section::Alerts => app.section = Section::Packet, - } + KeyCode::Esc => { + app.active_popup = None; } - KeyCode::BackTab => { - if app.start_sniffing { - if app.focused_block == FocusedBlock::Help { - return Ok(()); - } - - if app.update_filters { - match &app.focused_block { - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::Start; - app.filter.transport.state.select(None); - } - - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::TransportFilter; - app.filter.transport.state.select(Some(0)); - app.filter.network.state.select(None); - } - - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.filter.network.state.select(Some(0)); - app.filter.link.state.select(None); - } - - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::LinkFilter; - app.filter.link.state.select(Some(0)); - app.filter.traffic_direction.state.select(None); - } - - FocusedBlock::Start => { - app.focused_block = FocusedBlock::TrafficDirection; - app.filter.traffic_direction.state.select(Some(0)); - } - _ => {} - } - } - } + KeyCode::Char('?') => { + app.active_popup = Some(ActivePopup::Help); } - _ => { - if app.focused_block == FocusedBlock::Help { - return Ok(()); - } - if !fuzzy.is_paused() && !app.update_filters { - fuzzy - .filter - .handle_event(&crossterm::event::Event::Key(key_event)); - } else { - match key_event.code { - KeyCode::Char('/') => { - if !app.update_filters { - fuzzy.unpause(); - } - } - - KeyCode::Char('?') => { - app.focused_block = FocusedBlock::Help; - } - - KeyCode::Char('i') => { - if app.focused_block == FocusedBlock::Help || app.update_filters { - return Ok(()); - } - if app.packet_index.is_none() || fuzzy.packets.is_empty() { - return Ok(()); - } - - app.show_packet_infos_popup = true; - } - - KeyCode::Char('f') => { - if app.focused_block != FocusedBlock::Help - && app.start_sniffing - && !app.update_filters - { - app.update_filters = true; - app.focused_block = FocusedBlock::TransportFilter; - - app.filter.network.selected_protocols = - app.filter.network.applied_protocols.clone(); - - app.filter.transport.selected_protocols = - app.filter.transport.applied_protocols.clone(); - - app.filter.link.selected_protocols = - app.filter.link.applied_protocols.clone(); - - app.filter.traffic_direction.selected_direction = - app.filter.traffic_direction.applied_direction.clone(); - - app.filter.transport.state = TableState::default().with_selected(0); - } - } - - KeyCode::Char('j') | KeyCode::Down => { - if !app.update_filters { - if !app.manuall_scroll { - app.manuall_scroll = true; - // Record the last position. Usefull for selecting the packets to display - fuzzy.packet_end_index = fuzzy.packets.len(); - } - fuzzy.scroll_down(app.packet_window_size); - } else { - match &app.focused_block { - FocusedBlock::NetworkFilter => { - app.filter.network.scroll_down(); - } - - FocusedBlock::TransportFilter => { - app.filter.transport.scroll_down(); - } - - FocusedBlock::LinkFilter => { - app.filter.link.scroll_down(); - } - - FocusedBlock::TrafficDirection => { - app.filter.traffic_direction.state.select(Some(1)); - } - - _ => {} - }; - } - } - KeyCode::Char('k') | KeyCode::Up => { - if !app.update_filters { - if !app.manuall_scroll { - app.manuall_scroll = true; - // Record the last position. Usefull for selecting the packets to display - fuzzy.packet_end_index = fuzzy.packets.len(); - } - fuzzy.scroll_up(app.packet_window_size); - } else { - match &app.focused_block { - FocusedBlock::NetworkFilter => { - app.filter.network.scroll_up(); - } - - FocusedBlock::TransportFilter => { - app.filter.transport.scroll_up(); - } - - FocusedBlock::LinkFilter => { - app.filter.link.scroll_up(); - } - - FocusedBlock::TrafficDirection => { - app.filter.traffic_direction.state.select(Some(0)); - } - - FocusedBlock::Help => { - app.help.scroll_up(); - } - _ => {} - } - } - } - _ => {} - } - } - } - } - } else { - match key_event.code { KeyCode::Char('q') => { - app.filter - .traffic_direction - .terminate(TrafficDirection::Egress); - app.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - thread::sleep(Duration::from_millis(110)); app.quit(); } KeyCode::Char('c') | KeyCode::Char('C') => { if key_event.modifiers == KeyModifiers::CONTROL { - app.filter - .traffic_direction - .terminate(TrafficDirection::Egress); - app.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - thread::sleep(Duration::from_millis(110)); app.quit(); } } - - KeyCode::Esc => { - if app.focused_block == FocusedBlock::Help { - if app.start_sniffing { - app.focused_block = FocusedBlock::Main - } else { - app.focused_block = app.previous_focused_block; - } - return Ok(()); - } - - if app.update_filters { - app.update_filters = false; - return Ok(()); - } - - if app.manuall_scroll { - app.manuall_scroll = false; - } - } - - KeyCode::Char('?') => { - app.focused_block = FocusedBlock::Help; + _ => { + app.filter.handle_key_events(key_event); } + } + return Ok(()); + } - KeyCode::Char('f') => { - if app.focused_block != FocusedBlock::Help - && app.start_sniffing - && !app.update_filters - { - app.update_filters = true; - - app.focused_block = FocusedBlock::TransportFilter; - - app.filter.network.selected_protocols = - app.filter.network.applied_protocols.clone(); - - app.filter.transport.selected_protocols = - app.filter.transport.applied_protocols.clone(); - - app.filter.link.selected_protocols = app.filter.link.applied_protocols.clone(); - - app.filter.traffic_direction.selected_direction = - app.filter.traffic_direction.applied_direction.clone(); + // Sniff Phase - app.filter.transport.state = TableState::default().with_selected(0); + if let Some(popup) = app.active_popup { + match key_event.code { + KeyCode::Esc => { + app.active_popup = None; + if popup == ActivePopup::UpdateFilters { + app.filter.handle_key_events(key_event); } } - - KeyCode::Char('s') => { - if app.focused_block == FocusedBlock::Help || app.update_filters { - return Ok(()); - } - - if app.start_sniffing { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - sender, - )?; - } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - sender, - )?; - } - Err(e) => { - Notification::send( - e.to_string(), - NotificationLevel::Error, - sender, - )?; - } - } - } + KeyCode::Enter => { + if popup == ActivePopup::UpdateFilters { + app.filter + .update(sender.clone(), app.data_channel_sender.clone())?; + app.active_popup = None; } } - - KeyCode::Char('/') => { - if app.focused_block == FocusedBlock::Help || app.update_filters { - return Ok(()); - } - if app.start_sniffing { - fuzzy.enable(); - fuzzy.unpause(); + _ => { + if popup == ActivePopup::UpdateFilters { + app.filter.handle_key_events(key_event); } } + } - KeyCode::Char('i') => { - if app.focused_block == FocusedBlock::Help || app.update_filters { - return Ok(()); - } - let packets = app.packets.lock().unwrap(); - - if app.packet_index.is_none() || packets.is_empty() { - return Ok(()); - } + return Ok(()); + } - app.show_packet_infos_popup = true; - } + let fuzzy = app.fuzzy.clone(); + let mut fuzzy = fuzzy.lock().unwrap(); - KeyCode::Char('r') => { - if app.focused_block == FocusedBlock::Help || app.update_filters { - return Ok(()); - } - if key_event.modifiers == KeyModifiers::CONTROL { - app.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - app.filter - .traffic_direction - .terminate(TrafficDirection::Egress); - thread::sleep(Duration::from_millis(150)); - sender.send(Event::Reset)?; + if app.is_editing { + match key_event.code { + KeyCode::Esc => { + app.is_editing = false; + if !fuzzy.is_paused() { + fuzzy.pause(); } } - - KeyCode::Enter => { - if app.focused_block == FocusedBlock::Start && !app.start_sniffing { - let iface = app.interface.selected_interface.name.clone(); - - app.filter.network.apply(); - app.filter.transport.apply(); - app.filter.link.apply(); - app.filter.traffic_direction.apply(); - - if app - .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Ingress) - { - Ebpf::load_ingress( - iface.clone(), - sender.clone(), - app.data_channel_sender.clone(), - app.filter.ingress_channel.receiver.clone(), - app.filter.traffic_direction.terminate_ingress.clone(), - ); - } - - if app - .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Egress) - { - Ebpf::load_egress( - iface, - sender.clone(), - app.data_channel_sender.clone(), - app.filter.egress_channel.receiver.clone(), - app.filter.traffic_direction.terminate_egress.clone(), - ); - } - - app.start_sniffing = true; - app.focused_block = FocusedBlock::TransportFilter; - } else if app.start_sniffing && app.update_filters { - // Remove egress - if app - .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Egress) - && !app - .filter - .traffic_direction - .selected_direction - .contains(&TrafficDirection::Egress) - { - app.filter - .traffic_direction - .terminate(TrafficDirection::Egress); - } - - // Add egress - if !app - .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Egress) - && app - .filter - .traffic_direction - .selected_direction - .contains(&TrafficDirection::Egress) - { - app.filter - .traffic_direction - .terminate_egress - .store(false, std::sync::atomic::Ordering::Relaxed); - let iface = app.interface.selected_interface.name.clone(); - Ebpf::load_egress( - iface, - sender.clone(), - app.data_channel_sender.clone(), - app.filter.egress_channel.receiver.clone(), - app.filter.traffic_direction.terminate_egress.clone(), - ); - } - - // Remove ingress - if app - .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Ingress) - && !app - .filter - .traffic_direction - .selected_direction - .contains(&TrafficDirection::Ingress) - { - app.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - } - - // Add ingress - if !app + _ => { + if !fuzzy.is_paused() { + fuzzy .filter - .traffic_direction - .applied_direction - .contains(&TrafficDirection::Ingress) - && app - .filter - .traffic_direction - .selected_direction - .contains(&TrafficDirection::Ingress) - { - let iface = app.interface.selected_interface.name.clone(); - app.filter - .traffic_direction - .terminate_ingress - .store(false, std::sync::atomic::Ordering::Relaxed); - Ebpf::load_ingress( - iface, - sender.clone(), - app.data_channel_sender.clone(), - app.filter.ingress_channel.receiver.clone(), - app.filter.traffic_direction.terminate_ingress.clone(), - ); - } - app.filter.network.apply(); - app.filter.transport.apply(); - app.filter.link.apply(); - app.filter.traffic_direction.apply(); - - thread::sleep(Duration::from_millis(150)); - app.filter - .traffic_direction - .terminate_ingress - .store(false, std::sync::atomic::Ordering::Relaxed); - app.filter - .traffic_direction - .terminate_ingress - .store(false, std::sync::atomic::Ordering::Relaxed); - - app.update_filters = false; - } - - for protocol in TransportProtocol::all().iter() { - if app.filter.transport.applied_protocols.contains(protocol) { - app.filter - .ingress_channel - .sender - .send((Protocol::Transport(*protocol), false))?; - app.filter - .egress_channel - .sender - .send((Protocol::Transport(*protocol), false))?; - } else { - app.filter - .ingress_channel - .sender - .send((Protocol::Transport(*protocol), true))?; - app.filter - .egress_channel - .sender - .send((Protocol::Transport(*protocol), true))?; - } - } - - for protocol in NetworkProtocol::all().iter() { - if app.filter.network.applied_protocols.contains(protocol) { - app.filter - .ingress_channel - .sender - .send((Protocol::Network(*protocol), false))?; - app.filter - .egress_channel - .sender - .send((Protocol::Network(*protocol), false))?; - } else { - app.filter - .ingress_channel - .sender - .send((Protocol::Network(*protocol), true))?; - app.filter - .egress_channel - .sender - .send((Protocol::Network(*protocol), true))?; - } - } - - for protocol in LinkProtocol::all().iter() { - if app.filter.link.applied_protocols.contains(protocol) { - app.filter - .ingress_channel - .sender - .send((Protocol::Link(*protocol), false))?; - app.filter - .egress_channel - .sender - .send((Protocol::Link(*protocol), false))?; - } else { - app.filter - .ingress_channel - .sender - .send((Protocol::Link(*protocol), true))?; - app.filter - .egress_channel - .sender - .send((Protocol::Link(*protocol), true))?; - } + .handle_event(&crossterm::event::Event::Key(key_event)); } } + } + return Ok(()); + } - KeyCode::Tab => { - if app.start_sniffing { - if app.focused_block == FocusedBlock::Help { - return Ok(()); - } - - if app.update_filters { - match &app.focused_block { - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.filter.network.state.select(Some(0)); - app.filter.transport.state.select(None); - } - - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::LinkFilter; - app.filter.link.state.select(Some(0)); - app.filter.network.state.select(None); - } - - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::TrafficDirection; - app.filter.traffic_direction.state.select(Some(0)); - app.filter.link.state.select(None); - } - - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::Start; - app.filter.traffic_direction.state.select(None); - } - - FocusedBlock::Start => { - app.focused_block = FocusedBlock::TransportFilter; - app.filter.transport.state.select(Some(0)); - } - _ => {} - }; - - return Ok(()); - } - - match app.section { - Section::Packet => app.section = Section::Stats, - Section::Stats => app.section = Section::Alerts, - Section::Alerts => app.section = Section::Packet, - }; + match key_event.code { + KeyCode::Esc => { + if fuzzy.is_paused() { + if app.manuall_scroll { + app.manuall_scroll = false; } else { - match &app.focused_block { - FocusedBlock::Interface => { - app.focused_block = FocusedBlock::TransportFilter; - app.previous_focused_block = app.focused_block; - app.interface.state.select(None); - app.filter.transport.state.select(Some(0)); - } - - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.previous_focused_block = app.focused_block; - app.filter.network.state.select(Some(0)); - app.filter.transport.state.select(None); - } - - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::LinkFilter; - app.previous_focused_block = app.focused_block; - app.filter.link.state.select(Some(0)); - app.filter.network.state.select(None); - } - - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::TrafficDirection; - app.previous_focused_block = app.focused_block; - app.filter.traffic_direction.state.select(Some(0)); - app.filter.link.state.select(None); - } - - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::Start; - app.previous_focused_block = app.focused_block; - app.filter.traffic_direction.state.select(None); - } - - FocusedBlock::Start => { - app.focused_block = FocusedBlock::Interface; - app.previous_focused_block = app.focused_block; - app.interface.state.select(Some(0)); - } - _ => {} - } + fuzzy.disable(); } + } else { + fuzzy.pause(); } + } - KeyCode::BackTab => { - if app.start_sniffing { - if app.focused_block == FocusedBlock::Help { - return Ok(()); - } - - if app.update_filters { - match &app.focused_block { - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::Start; - app.filter.transport.state.select(None); - } - - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::TransportFilter; - app.filter.transport.state.select(Some(0)); - app.filter.network.state.select(None); - } - - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.filter.network.state.select(Some(0)); - app.filter.link.state.select(None); - } + KeyCode::Tab => match app.section { + Section::Packet => app.section = Section::Stats, + Section::Stats => app.section = Section::Alerts, + Section::Alerts => app.section = Section::Packet, + }, + + KeyCode::BackTab => { + match app.section { + Section::Packet => app.section = Section::Alerts, + Section::Stats => app.section = Section::Packet, + Section::Alerts => app.section = Section::Stats, + }; + } - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::LinkFilter; - app.filter.link.state.select(Some(0)); - app.filter.traffic_direction.state.select(None); - } + KeyCode::Char('?') => { + app.active_popup = Some(ActivePopup::Help); + } - FocusedBlock::Start => { - app.focused_block = FocusedBlock::TrafficDirection; - app.filter.traffic_direction.state.select(Some(0)); - } - _ => {} - } - return Ok(()); - }; + KeyCode::Char('f') => { + if app.active_popup.is_none() { + app.active_popup = Some(ActivePopup::UpdateFilters); + app.filter.trigger(); + } + } - match app.section { - Section::Packet => app.section = Section::Alerts, - Section::Stats => app.section = Section::Packet, - Section::Alerts => app.section = Section::Stats, - }; - } else { - match &app.focused_block { - FocusedBlock::Interface => { - app.focused_block = FocusedBlock::Start; - app.interface.state.select(None); - } + KeyCode::Char('r') => { + if key_event.modifiers == KeyModifiers::CONTROL { + app.filter + .traffic_direction + .terminate(TrafficDirection::Ingress); + app.filter + .traffic_direction + .terminate(TrafficDirection::Egress); + thread::sleep(Duration::from_millis(150)); + sender.send(Event::Reset)?; + } + } - FocusedBlock::TransportFilter => { - app.focused_block = FocusedBlock::Interface; - app.interface.state.select(Some(0)); - app.filter.transport.state.select(None); - } + KeyCode::Char('q') => { + app.filter.terminate(); + thread::sleep(Duration::from_millis(110)); + app.quit(); + } - FocusedBlock::NetworkFilter => { - app.focused_block = FocusedBlock::TransportFilter; - app.filter.transport.state.select(Some(0)); - app.filter.network.state.select(None); - } + KeyCode::Char('c') | KeyCode::Char('C') => { + if key_event.modifiers == KeyModifiers::CONTROL { + app.filter.terminate(); + thread::sleep(Duration::from_millis(110)); + app.quit(); + } + } - FocusedBlock::LinkFilter => { - app.focused_block = FocusedBlock::NetworkFilter; - app.filter.network.state.select(Some(0)); - app.filter.link.state.select(None); - } + _ => { + if app.section == Section::Packet { + match key_event.code { + KeyCode::Char('/') => { + fuzzy.enable(); + fuzzy.unpause(); + app.is_editing = true; + } - FocusedBlock::TrafficDirection => { - app.focused_block = FocusedBlock::LinkFilter; - app.filter.link.state.select(Some(0)); - app.filter.traffic_direction.state.select(None); + KeyCode::Char('i') => { + if app.packet_index.is_none() && fuzzy.packets.is_empty() { + return Ok(()); } - FocusedBlock::Start => { - app.focused_block = FocusedBlock::TrafficDirection; - app.filter.traffic_direction.state.select(Some(0)); - } - _ => {} + app.active_popup = Some(ActivePopup::PacketInfos); } - } - } - KeyCode::Char(' ') => { - if !app.start_sniffing || app.update_filters { - match &app.focused_block { - FocusedBlock::Interface => { - if let Some(index) = app.interface.state.selected() { - let net_interface = app.interface.interfaces[index].clone(); - if net_interface.is_up { - app.interface.selected_interface = - app.interface.interfaces[index].clone(); + KeyCode::Char('s') => { + if app.start_sniffing { + let app_packets = app.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + sender, + )?; + } else { + match export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + sender, + )?; + } + Err(e) => { + Notification::send( + e.to_string(), + NotificationLevel::Error, + sender, + )?; + } } } } - FocusedBlock::NetworkFilter => { - app.filter.network.select(); - } - - FocusedBlock::TransportFilter => { - app.filter.transport.select(); - } - - FocusedBlock::LinkFilter => { - app.filter.link.select(); - } - - FocusedBlock::TrafficDirection => { - app.filter.traffic_direction.select(); - } - - _ => {} } - } - } - KeyCode::Char('j') | KeyCode::Down => { - if let FocusedBlock::Help = app.focused_block { - return Ok(()); - } - let app_packets = app.packets.lock().unwrap(); - // Sniff mode - if app.start_sniffing && !app.update_filters { - if !app.manuall_scroll { - app.manuall_scroll = true; - // Record the last position. Usefull for selecting the packets to display - app.packet_end_index = app_packets.len(); - } - let i = match app.packets_table_state.selected() { - Some(i) => { - if i < app.packet_window_size - 1 { - i + 1 - } else if i == app.packet_window_size - 1 - && app_packets.len() > app.packet_end_index - { - // shit the window by one - app.packet_end_index += 1; - i + 1 + KeyCode::Char('j') | KeyCode::Down => { + let app_packets = app.packets.lock().unwrap(); + + if !app.manuall_scroll { + app.manuall_scroll = true; + if fuzzy.is_enabled() { + fuzzy.packet_end_index = fuzzy.packets.len(); } else { - i + app.packet_end_index = app_packets.len(); } } - None => app_packets.len(), - }; - - app.packets_table_state.select(Some(i)); - } else { - match &app.focused_block { - FocusedBlock::Interface => { - app.interface.scroll_down(); - } - - FocusedBlock::NetworkFilter => { - app.filter.network.scroll_down(); - } - - FocusedBlock::TransportFilter => { - app.filter.transport.scroll_down(); - } - - FocusedBlock::LinkFilter => { - app.filter.link.scroll_down(); - } - - FocusedBlock::TrafficDirection => { - app.filter.traffic_direction.state.select(Some(1)); - } + if fuzzy.is_enabled() { + fuzzy.scroll_down(app.packet_window_size); + } else { + let i = match app.packets_table_state.selected() { + Some(i) => { + if i < app.packet_window_size - 1 { + i + 1 + } else if i == app.packet_window_size - 1 + && app_packets.len() > app.packet_end_index + { + // shit the window by one + app.packet_end_index += 1; + i + 1 + } else { + i + } + } + None => app_packets.len(), + }; - FocusedBlock::Help => { - app.help.scroll_down(); + app.packets_table_state.select(Some(i)); } - _ => {} } - } - } - KeyCode::Char('k') | KeyCode::Up => { - let app_packets = app.packets.lock().unwrap(); - if let FocusedBlock::Help = app.focused_block { - return Ok(()); - } - if app.start_sniffing && !app.update_filters { - if !app.manuall_scroll { - app.manuall_scroll = true; - // Record the last position. Usefull for selecting the packets to display - app.packet_end_index = app_packets.len(); - } - let i = match app.packets_table_state.selected() { - Some(i) => { - if i > 1 { - i - 1 - } else if i == 0 && app.packet_end_index > app.packet_window_size { - // shit the window by one - app.packet_end_index -= 1; - 0 + KeyCode::Char('k') | KeyCode::Up => { + let app_packets = app.packets.lock().unwrap(); + if !app.manuall_scroll { + app.manuall_scroll = true; + // Record the last position. Usefull for selecting the packets to display + if fuzzy.is_enabled() { + fuzzy.packet_end_index = fuzzy.packets.len(); } else { - 0 + app.packet_end_index = app_packets.len(); } } - None => app.packet_window_size, - }; - - app.packets_table_state.select(Some(i)); - } else { - match &app.focused_block { - FocusedBlock::Interface => { - app.interface.scroll_up(); - } - FocusedBlock::NetworkFilter => { - app.filter.network.scroll_up(); - } - - FocusedBlock::TransportFilter => { - app.filter.transport.scroll_up(); - } - - FocusedBlock::LinkFilter => { - app.filter.link.scroll_up(); - } - - FocusedBlock::TrafficDirection => { - app.filter.traffic_direction.state.select(Some(0)); - } + if fuzzy.is_enabled() { + fuzzy.scroll_up(app.packet_window_size); + } else { + let i = match app.packets_table_state.selected() { + Some(i) => { + if i > 1 { + i - 1 + } else if i == 0 + && app.packet_end_index > app.packet_window_size + { + // shit the window by one + app.packet_end_index -= 1; + 0 + } else { + 0 + } + } + None => app.packet_window_size, + }; - FocusedBlock::Help => { - app.help.scroll_up(); + app.packets_table_state.select(Some(i)); } - _ => {} } + _ => {} } } - - _ => {} } } diff --git a/oryx-tui/src/interface.rs b/oryx-tui/src/interface.rs index 81d6e56..808613c 100644 --- a/oryx-tui/src/interface.rs +++ b/oryx-tui/src/interface.rs @@ -14,8 +14,6 @@ use std::{ path::PathBuf, }; -use crate::app::FocusedBlock; - #[derive(Debug, Clone)] pub struct NetworkInterface { pub name: String, @@ -150,12 +148,7 @@ impl Interface { self.state.select(Some(i)); } - pub fn render_on_setup( - &mut self, - frame: &mut Frame, - block: Rect, - focused_block: &FocusedBlock, - ) { + pub fn render_on_setup(&mut self, frame: &mut Frame, block: Rect, is_focused: bool) { let layout = Layout::default() .direction(Direction::Horizontal) .constraints([ @@ -225,7 +218,7 @@ impl Interface { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::Interface { + .border_type(if is_focused { BorderType::Thick } else { BorderType::default() diff --git a/oryx-tui/src/ui.rs b/oryx-tui/src/ui.rs index cee7227..fa41912 100644 --- a/oryx-tui/src/ui.rs +++ b/oryx-tui/src/ui.rs @@ -1,14 +1,17 @@ use ratatui::Frame; -use crate::app::{App, FocusedBlock}; +use crate::app::{ActivePopup, App}; pub fn render(app: &mut App, frame: &mut Frame) { app.render(frame); - if let FocusedBlock::Help = app.focused_block { - app.help.render(frame); + if let Some(popup) = &app.active_popup { + match popup { + ActivePopup::Help => app.help.render(frame), + ActivePopup::PacketInfos => app.render_packet_infos_popup(frame), + _ => {} + } } - for (index, notification) in app.notifications.iter().enumerate() { notification.render(index, frame); } From fa301399397bd96618b80d73d1cb3b150e5c6765 Mon Sep 17 00:00:00 2001 From: Badr Date: Tue, 1 Oct 2024 16:14:47 +0200 Subject: [PATCH 05/16] justfile: add profile target --- Justfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Justfile b/Justfile index e3d6b7e..355a469 100644 --- a/Justfile +++ b/Justfile @@ -21,3 +21,7 @@ run: # Build oryx build: cargo xtask build + +# Profile +profile: + CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root --bin oryx From c090486760e00492451621e3d5b5640b81074440 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 01:46:01 +0200 Subject: [PATCH 06/16] refactor sections --- oryx-tui/src/alert.rs | 56 ++-- oryx-tui/src/app.rs | 584 ++---------------------------------- oryx-tui/src/filter.rs | 5 - oryx-tui/src/handler.rs | 196 +++--------- oryx-tui/src/inspection.rs | 596 +++++++++++++++++++++++++++++++++++++ oryx-tui/src/lib.rs | 7 +- oryx-tui/src/section.rs | 132 ++++++++ oryx-tui/src/stats.rs | 234 +++++++++------ oryx-tui/src/ui.rs | 4 +- 9 files changed, 953 insertions(+), 861 deletions(-) create mode 100644 oryx-tui/src/inspection.rs create mode 100644 oryx-tui/src/section.rs diff --git a/oryx-tui/src/alert.rs b/oryx-tui/src/alert.rs index d0d7543..e3c5fef 100644 --- a/oryx-tui/src/alert.rs +++ b/oryx-tui/src/alert.rs @@ -1,10 +1,9 @@ mod syn_flood; use ratatui::{ - layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, + layout::{Constraint, Direction, Layout, Rect}, style::{Color, Style, Stylize}, - text::{Line, Span, Text}, - widgets::{Block, BorderType, Borders, Padding}, + text::{Span, Text}, Frame, }; use std::sync::{atomic::Ordering, Arc, Mutex}; @@ -39,40 +38,6 @@ impl Alert { } pub fn render(&self, frame: &mut Frame, block: Rect) { - frame.render_widget( - Block::default() - .title({ - Line::from(vec![ - Span::from(" Packet ").fg(Color::DarkGray), - Span::from(" Stats ").fg(Color::DarkGray), - { - if self.detected { - if self.flash_count % 12 == 0 { - Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) - } else { - Span::from(" Alert 󰐼 ").bg(Color::Red) - } - } else { - Span::styled( - " Alert ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ) - } - }, - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - block.inner(Margin { - horizontal: 1, - vertical: 0, - }), - ); - if !self.detected { let text_block = Layout::default() .direction(Direction::Vertical) @@ -111,8 +76,21 @@ impl Alert { self.syn_flood.render(frame, syn_flood_block); } - pub fn title_span(&self) -> Span<'_> { - if self.detected { + pub fn title_span(&self, is_focused: bool) -> Span<'_> { + if is_focused { + if self.detected { + if self.flash_count % 12 == 0 { + Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) + } else { + Span::from(" Alert 󰐼 ").bg(Color::Red) + } + } else { + Span::styled( + " Alert ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ) + } + } else if self.detected { if self.flash_count % 12 == 0 { Span::from(" Alert 󰐼 ").fg(Color::White).bg(Color::Red) } else { diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index d6ac3b7..922db39 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -1,12 +1,6 @@ use oryx_common::RawPacket; use ratatui::{ - layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect}, - style::{Color, Style, Stylize}, - text::{Line, Span}, - widgets::{ - Block, BorderType, Borders, Cell, Clear, HighlightSpacing, Padding, Paragraph, Row, - Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, - }, + layout::{Constraint, Direction, Layout}, Frame, }; use std::{ @@ -15,23 +9,14 @@ use std::{ thread, }; -use crate::packet::AppPacket; -use crate::{alert::Alert, bandwidth::Bandwidth, filter::fuzzy, packet::network::IpProto}; -use crate::{filter::fuzzy::Fuzzy, notification::Notification}; +use crate::notification::Notification; use crate::{filter::Filter, help::Help}; -use crate::{packet::network::IpPacket, stats::Stats}; +use crate::{packet::AppPacket, section::Section}; pub type AppResult = std::result::Result>; pub const TICK_RATE: u64 = 40; -#[derive(Debug, PartialEq)] -pub enum Section { - Packet, - Stats, - Alerts, -} - #[derive(Debug, Copy, Clone, PartialEq)] pub enum ActivePopup { Help, @@ -39,6 +24,12 @@ pub enum ActivePopup { PacketInfos, } +#[non_exhaustive] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum EditingBlock { + Fuzzy, +} + #[derive(Debug)] pub struct DataEventHandler { pub sender: kanal::Sender<[u8; RawPacket::LEN]>, @@ -52,19 +43,10 @@ pub struct App { pub filter: Filter, pub start_sniffing: bool, pub packets: Arc>>, - pub packets_table_state: TableState, - pub fuzzy: Arc>, pub notifications: Vec, - pub manuall_scroll: bool, pub section: Section, - pub stats: Arc>, - pub packet_end_index: usize, - pub packet_window_size: usize, pub data_channel_sender: kanal::Sender<[u8; RawPacket::LEN]>, - pub bandwidth: Bandwidth, - pub packet_index: Option, - pub alert: Alert, - pub is_editing: bool, + pub editing_block: Option, pub active_popup: Option, } @@ -77,17 +59,18 @@ impl Default for App { impl App { pub fn new() -> Self { let packets = Arc::new(Mutex::new(Vec::with_capacity(AppPacket::LEN * 1024 * 1024))); - let stats = Arc::new(Mutex::new(Stats::default())); let (sender, receiver) = kanal::unbounded(); - thread::spawn({ let packets = packets.clone(); - let stats = stats.clone(); - move || loop { if let Ok(raw_packet) = receiver.recv() { - App::process(packets.clone(), stats.clone(), AppPacket::from(raw_packet)); + let app_packet = AppPacket::from(raw_packet); + let mut packets = packets.lock().unwrap(); + if packets.len() == packets.capacity() { + packets.reserve(1024 * 1024); + } + packets.push(app_packet); } } }); @@ -98,19 +81,10 @@ impl App { filter: Filter::new(), start_sniffing: false, packets: packets.clone(), - packets_table_state: TableState::default(), - fuzzy: Fuzzy::new(packets.clone()), notifications: Vec::new(), - manuall_scroll: false, - section: Section::Packet, - stats, - packet_end_index: 0, - packet_window_size: 0, + section: Section::new(packets.clone()), data_channel_sender: sender, - bandwidth: Bandwidth::new(), - packet_index: None, - alert: Alert::new(packets.clone()), - is_editing: false, + editing_block: None, active_popup: None, } } @@ -129,531 +103,21 @@ impl App { (chunks[0], chunks[1]) }; - // Packets/Stats - match self.section { - Section::Packet => self.render_packets_mode(frame, section_block), - Section::Stats => self.render_stats_mode(frame, section_block), - Section::Alerts => self.alert.render(frame, section_block), - } - self.filter.render_on_sniffing(frame, settings_block); - } - } - - pub fn render_packets_mode(&mut self, frame: &mut Frame, packet_mode_block: Rect) { - let app_packets = self.packets.lock().unwrap(); - let mut fuzzy = self.fuzzy.lock().unwrap(); - let fuzzy_packets = fuzzy.clone().packets.clone(); - - //TODO: ugly - let pattern = fuzzy.clone(); - let pattern = pattern.filter.value(); - - let (packet_block, fuzzy_block) = { - if fuzzy.is_enabled() { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(3)]) - .horizontal_margin(1) - .split(packet_mode_block); - (chunks[0], chunks[1]) - } else { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(1)]) - .horizontal_margin(1) - .split(packet_mode_block); - (chunks[0], chunks[1]) - } - }; - - let widths = [ - Constraint::Min(19), // Source Address - Constraint::Length(11), // Source Port - Constraint::Min(19), // Destination Address - Constraint::Length(16), // Destination Port - Constraint::Length(8), // Protocol - Constraint::Length(2), // Protocol - ]; - - // The size of the window where to display packets - let window_size = packet_mode_block.height.saturating_sub(5) as usize; - self.packet_window_size = window_size; - - // This points always to the end of the window - if self.packet_end_index < window_size { - self.packet_end_index = window_size; - } - - if fuzzy.packet_end_index < window_size { - fuzzy.packet_end_index = window_size; - } - - let packets_to_display = match self.manuall_scroll { - true => { - if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { - if fuzzy_packets.len() > window_size { - if let Some(selected_index) = fuzzy.scroll_state.selected() { - self.packet_index = Some( - fuzzy.packet_end_index.saturating_sub(window_size) + selected_index, - ); - } - &fuzzy_packets[fuzzy.packet_end_index.saturating_sub(window_size) - ..fuzzy.packet_end_index] - } else { - if let Some(selected_index) = fuzzy.scroll_state.selected() { - self.packet_index = Some(selected_index); - } else { - self.packet_index = None; - } - &fuzzy_packets - } - } else if app_packets.len() > window_size { - if let Some(selected_index) = self.packets_table_state.selected() { - self.packet_index = Some( - self.packet_end_index.saturating_sub(window_size) + selected_index, - ); - } - &app_packets - [self.packet_end_index.saturating_sub(window_size)..self.packet_end_index] - } else { - if let Some(selected_index) = self.packets_table_state.selected() { - self.packet_index = Some(selected_index); - } - &app_packets - } - } - false => { - if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { - if fuzzy_packets.len() > window_size { - self.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); - &fuzzy_packets[fuzzy_packets.len().saturating_sub(window_size)..] - } else { - self.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); - &fuzzy_packets - } - } else if app_packets.len() > window_size { - self.packet_index = Some(app_packets.len().saturating_sub(1)); - &app_packets[app_packets.len().saturating_sub(window_size)..] - } else { - self.packet_index = Some(app_packets.len().saturating_sub(1)); - &app_packets - } - } - }; - - // Style the packets - let packets: Vec = if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { - packets_to_display - .iter() - .map(|app_packet| match app_packet { - AppPacket::Arp(packet) => Row::new(vec![ - fuzzy::highlight(pattern, packet.src_mac.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, packet.dst_mac.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, "ARP".to_string()).cyan(), - ]), - AppPacket::Ip(packet) => match packet { - IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { - IpProto::Tcp(p) => Row::new(vec![ - fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), - fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), - fuzzy::highlight(pattern, "TCP".to_string()).cyan(), - ]), - IpProto::Udp(p) => Row::new(vec![ - fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), - fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), - fuzzy::highlight(pattern, "UDP".to_string()).cyan(), - ]), - IpProto::Icmp(_) => Row::new(vec![ - fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), - ]), - }, - IpPacket::V6(ipv6_packet) => match ipv6_packet.proto { - IpProto::Tcp(p) => Row::new(vec![ - fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), - fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), - fuzzy::highlight(pattern, "TCP".to_string()).cyan(), - ]), - IpProto::Udp(p) => Row::new(vec![ - fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), - fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), - fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), - fuzzy::highlight(pattern, "UDP".to_string()).cyan(), - ]), - IpProto::Icmp(_) => Row::new(vec![ - fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), - Cell::from(Line::from("-").centered()).yellow(), - fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), - ]), - }, - }, - }) - .collect() - } else { - packets_to_display - .iter() - .map(|app_packet| match app_packet { - AppPacket::Arp(packet) => Row::new(vec![ - Span::from(packet.src_mac.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from(packet.dst_mac.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from("ARP".to_string()).into_centered_line().cyan(), - ]), - AppPacket::Ip(packet) => match packet { - IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { - IpProto::Tcp(p) => Row::new(vec![ - Span::from(ipv4_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.src_port.to_string()) - .into_centered_line() - .yellow(), - Span::from(ipv4_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.dst_port.to_string()) - .into_centered_line() - .yellow(), - Span::from("TCP".to_string()).into_centered_line().cyan(), - ]), - IpProto::Udp(p) => Row::new(vec![ - Span::from(ipv4_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.src_port.to_string()) - .into_centered_line() - .yellow(), - Span::from(ipv4_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.dst_port.to_string()) - .into_centered_line() - .yellow(), - Span::from("UDP".to_string()).into_centered_line().cyan(), - ]), - IpProto::Icmp(_) => Row::new(vec![ - Span::from(ipv4_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from(ipv4_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from("ICMP".to_string()).into_centered_line().cyan(), - ]), - }, - IpPacket::V6(ipv6_packet) => match ipv6_packet.proto { - IpProto::Tcp(p) => Row::new(vec![ - Span::from(ipv6_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.src_port.to_string()) - .into_centered_line() - .yellow(), - Span::from(ipv6_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.dst_port.to_string()) - .into_centered_line() - .yellow(), - Span::from("TCP".to_string()).into_centered_line().cyan(), - ]), - IpProto::Udp(p) => Row::new(vec![ - Span::from(ipv6_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.src_port.to_string()) - .into_centered_line() - .yellow(), - Span::from(ipv6_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from(p.dst_port.to_string()) - .into_centered_line() - .yellow(), - Span::from("UDP".to_string()).into_centered_line().cyan(), - ]), - IpProto::Icmp(_) => Row::new(vec![ - Span::from(ipv6_packet.src_ip.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from(ipv6_packet.dst_ip.to_string()) - .into_centered_line() - .blue(), - Span::from("-").into_centered_line().yellow(), - Span::from("ICMP".to_string()).into_centered_line().cyan(), - ]), - }, - }, - }) - .collect() - }; - - // Always select the last packet - if !self.manuall_scroll { - if fuzzy.is_enabled() { - fuzzy.scroll_state.select(Some(packets_to_display.len())); - } else { - self.packets_table_state - .select(Some(packets_to_display.len())); - } - } - - let table = Table::new(packets, widths) - .header( - Row::new(vec![ - Line::from("Source Address").centered(), - Line::from("Source Port").centered(), - Line::from("Destination Address").centered(), - Line::from("Destination Port").centered(), - Line::from("Protocol").centered(), - { - if self.manuall_scroll { - Line::from(" ").centered().yellow() - } else { - Line::from("").centered() - } - }, - ]) - .style(Style::new().bold()) - .bottom_margin(1), - ) - .column_spacing(2) - .flex(Flex::SpaceBetween) - .highlight_style(Style::new().bg(ratatui::style::Color::DarkGray)) - .highlight_spacing(HighlightSpacing::Always) - .block( - Block::default() - .title({ - Line::from(vec![ - Span::styled( - " Packet ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - Span::from(" Stats ").fg(Color::DarkGray), - self.alert.title_span(), - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), + self.section.render( + frame, + section_block, + &self.filter.interface.selected_interface.name, ); - if fuzzy.is_enabled() { - frame.render_stateful_widget(table, packet_block, &mut fuzzy.scroll_state); - } else { - frame.render_stateful_widget(table, packet_block, &mut self.packets_table_state); - } - - // Scrollbar - - let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight) - .begin_symbol(Some("↑")) - .end_symbol(Some("↓")); - - let mut scrollbar_state = if fuzzy.is_enabled() && fuzzy_packets.len() > window_size { - ScrollbarState::new(fuzzy_packets.len()).position({ - if self.manuall_scroll { - if fuzzy.packet_end_index == window_size { - 0 - } else { - fuzzy.packet_end_index - } - } else { - fuzzy.packets.len() - } - }) - } else if !fuzzy.is_enabled() && app_packets.len() > window_size { - ScrollbarState::new(app_packets.len()).position({ - if self.manuall_scroll { - if self.packet_end_index == window_size { - 0 - } else { - self.packet_end_index - } - } else { - app_packets.len() - } - }) - } else { - ScrollbarState::default() - }; - - frame.render_stateful_widget( - scrollbar, - packet_block.inner(Margin { - vertical: 1, - horizontal: 0, - }), - &mut scrollbar_state, - ); - - if fuzzy.is_enabled() { - let fuzzy = Paragraph::new(format!("> {}", fuzzy.filter.value())) - .alignment(Alignment::Left) - .style(Style::default().white()) - .block( - Block::new() - .borders(Borders::all()) - .title(" Search  ") - .title_style({ - if fuzzy.is_paused() { - Style::default().bold().green() - } else { - Style::default().bold().yellow() - } - }) - .border_style({ - if fuzzy.is_paused() { - Style::default().green() - } else { - Style::default().yellow() - } - }), - ); - - frame.render_widget(fuzzy, fuzzy_block); - } - } - - pub fn render_stats_mode(&mut self, frame: &mut Frame, block: Rect) { - let stats = self.stats.lock().unwrap(); - - let (bandwidth_block, stats_block) = { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .margin(2) - .split(block); - (chunks[0], chunks[1]) - }; - - frame.render_widget( - Block::default() - .title({ - Line::from(vec![ - Span::from(" Packet ").fg(Color::DarkGray), - Span::styled( - " Stats ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - self.alert.title_span(), - ]) - }) - .title_alignment(Alignment::Left) - .padding(Padding::top(1)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - block.inner(Margin { - horizontal: 1, - vertical: 0, - }), - ); - - stats.render(frame, stats_block); - - self.bandwidth.render( - frame, - bandwidth_block, - &self.filter.interface.selected_interface.name.clone(), - ); - } - - pub fn render_packet_infos_popup(&self, frame: &mut Frame) { - let layout = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Fill(1), - Constraint::Length(36), - Constraint::Fill(1), - ]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(frame.area()); - - let block = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Fill(1), - Constraint::Max(80), - Constraint::Fill(1), - ]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(layout[1])[1]; - - let fuzzy = self.fuzzy.lock().unwrap(); - let packets = self.packets.lock().unwrap(); - - let packet = if fuzzy.is_enabled() { - fuzzy.packets[self.packet_index.unwrap()] - } else { - packets[self.packet_index.unwrap()] - }; - - frame.render_widget(Clear, block); - frame.render_widget( - Block::new() - .title(" Packet Infos 󰋼 ") - .title_style(Style::new().bold().green()) - .title_alignment(Alignment::Center) - .borders(Borders::all()) - .border_style(Style::new().green()) - .border_type(BorderType::Thick), - block, - ); - match packet { - AppPacket::Ip(ip_packet) => ip_packet.render(block, frame), - AppPacket::Arp(arp_packet) => arp_packet.render(block, frame), - }; - } - - pub fn process( - packets: Arc>>, - stats: Arc>, - app_packet: AppPacket, - ) { - let mut packets = packets.lock().unwrap(); - - if packets.len() == packets.capacity() { - packets.reserve(1024 * 1024); + self.filter.render_on_sniffing(frame, settings_block); } - - packets.push(app_packet); - - let mut stats = stats.lock().unwrap(); - stats.refresh(&app_packet); } pub fn tick(&mut self) { self.notifications.iter_mut().for_each(|n| n.ttl -= 1); self.notifications.retain(|n| n.ttl > 0); - self.alert.check(); + self.section.alert.check(); } pub fn quit(&mut self) { diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 3a23e94..4a9740a 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -291,7 +291,6 @@ impl Filter { self.apply(); - //TODO: why ? thread::sleep(Duration::from_millis(150)); self.traffic_direction @@ -650,10 +649,6 @@ impl Filter { ); frame.render_widget(table, filter_summury_block); - - if self.show_popup { - self.render_update_popup(frame); - } } pub fn render_update_popup(&mut self, frame: &mut Frame) { diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 43f4627..43a0380 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -1,17 +1,13 @@ use std::{thread, time::Duration}; -use tui_input::backend::crossterm::EventHandler; use crate::{ - app::{ActivePopup, App, AppResult, Section}, + app::{ActivePopup, App, AppResult, EditingBlock}, event::Event, export::export, filter::direction::TrafficDirection, notification::{Notification, NotificationLevel}, }; -use ratatui::crossterm::{ - self, - event::{KeyCode, KeyEvent, KeyModifiers}, -}; +use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; pub fn handle_key_events( key_event: KeyEvent, @@ -79,55 +75,18 @@ pub fn handle_key_events( return Ok(()); } - let fuzzy = app.fuzzy.clone(); - let mut fuzzy = fuzzy.lock().unwrap(); - - if app.is_editing { + if app.editing_block.is_some() { match key_event.code { KeyCode::Esc => { - app.is_editing = false; - if !fuzzy.is_paused() { - fuzzy.pause(); - } - } - _ => { - if !fuzzy.is_paused() { - fuzzy - .filter - .handle_event(&crossterm::event::Event::Key(key_event)); - } + app.editing_block = None; } + _ => {} } + app.section.handle_keys(key_event); return Ok(()); } match key_event.code { - KeyCode::Esc => { - if fuzzy.is_paused() { - if app.manuall_scroll { - app.manuall_scroll = false; - } else { - fuzzy.disable(); - } - } else { - fuzzy.pause(); - } - } - - KeyCode::Tab => match app.section { - Section::Packet => app.section = Section::Stats, - Section::Stats => app.section = Section::Alerts, - Section::Alerts => app.section = Section::Packet, - }, - - KeyCode::BackTab => { - match app.section { - Section::Packet => app.section = Section::Alerts, - Section::Stats => app.section = Section::Packet, - Section::Alerts => app.section = Section::Stats, - }; - } - KeyCode::Char('?') => { app.active_popup = Some(ActivePopup::Help); } @@ -166,126 +125,43 @@ pub fn handle_key_events( } } - _ => { - if app.section == Section::Packet { - match key_event.code { - KeyCode::Char('/') => { - fuzzy.enable(); - fuzzy.unpause(); - app.is_editing = true; - } - - KeyCode::Char('i') => { - if app.packet_index.is_none() && fuzzy.packets.is_empty() { - return Ok(()); - } - - app.active_popup = Some(ActivePopup::PacketInfos); - } - - KeyCode::Char('s') => { - if app.start_sniffing { - let app_packets = app.packets.lock().unwrap(); - if app_packets.is_empty() { - Notification::send( - "There is no packets".to_string(), - NotificationLevel::Info, - sender, - )?; - } else { - match export(&app_packets) { - Ok(_) => { - Notification::send( - "Packets exported to ~/oryx/capture file".to_string(), - NotificationLevel::Info, - sender, - )?; - } - Err(e) => { - Notification::send( - e.to_string(), - NotificationLevel::Error, - sender, - )?; - } - } - } - } - } - - KeyCode::Char('j') | KeyCode::Down => { - let app_packets = app.packets.lock().unwrap(); + KeyCode::Char('/') => { + app.editing_block = Some(EditingBlock::Fuzzy); + app.section.handle_keys(key_event); + } - if !app.manuall_scroll { - app.manuall_scroll = true; - if fuzzy.is_enabled() { - fuzzy.packet_end_index = fuzzy.packets.len(); - } else { - app.packet_end_index = app_packets.len(); - } - } - if fuzzy.is_enabled() { - fuzzy.scroll_down(app.packet_window_size); - } else { - let i = match app.packets_table_state.selected() { - Some(i) => { - if i < app.packet_window_size - 1 { - i + 1 - } else if i == app.packet_window_size - 1 - && app_packets.len() > app.packet_end_index - { - // shit the window by one - app.packet_end_index += 1; - i + 1 - } else { - i - } - } - None => app_packets.len(), - }; + KeyCode::Char('i') => { + if app.section.inspection.can_show_popup() { + app.active_popup = Some(ActivePopup::PacketInfos); + } + } - app.packets_table_state.select(Some(i)); - } + KeyCode::Char('s') => { + let app_packets = app.packets.lock().unwrap(); + if app_packets.is_empty() { + Notification::send( + "There is no packets".to_string(), + NotificationLevel::Info, + sender, + )?; + } else { + match export(&app_packets) { + Ok(_) => { + Notification::send( + "Packets exported to ~/oryx/capture file".to_string(), + NotificationLevel::Info, + sender, + )?; } - - KeyCode::Char('k') | KeyCode::Up => { - let app_packets = app.packets.lock().unwrap(); - if !app.manuall_scroll { - app.manuall_scroll = true; - // Record the last position. Usefull for selecting the packets to display - if fuzzy.is_enabled() { - fuzzy.packet_end_index = fuzzy.packets.len(); - } else { - app.packet_end_index = app_packets.len(); - } - } - if fuzzy.is_enabled() { - fuzzy.scroll_up(app.packet_window_size); - } else { - let i = match app.packets_table_state.selected() { - Some(i) => { - if i > 1 { - i - 1 - } else if i == 0 - && app.packet_end_index > app.packet_window_size - { - // shit the window by one - app.packet_end_index -= 1; - 0 - } else { - 0 - } - } - None => app.packet_window_size, - }; - - app.packets_table_state.select(Some(i)); - } + Err(e) => { + Notification::send(e.to_string(), NotificationLevel::Error, sender)?; } - _ => {} } } } + _ => { + app.section.handle_keys(key_event); + } } Ok(()) diff --git a/oryx-tui/src/inspection.rs b/oryx-tui/src/inspection.rs new file mode 100644 index 0000000..0b2e91a --- /dev/null +++ b/oryx-tui/src/inspection.rs @@ -0,0 +1,596 @@ +use std::sync::{Arc, Mutex}; + +use crossterm::event::{KeyCode, KeyEvent}; +use ratatui::{ + layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect}, + style::{Style, Stylize}, + text::{Line, Span}, + widgets::{ + Block, BorderType, Borders, Cell, Clear, HighlightSpacing, Padding, Paragraph, Row, + Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, + }, + Frame, +}; +use tui_input::backend::crossterm::EventHandler; + +use crate::{ + filter::fuzzy::{self, Fuzzy}, + packet::{ + network::{IpPacket, IpProto}, + AppPacket, + }, +}; + +#[derive(Debug)] +pub struct Inspection { + pub packets: Arc>>, + pub state: TableState, + pub fuzzy: Arc>, + pub manuall_scroll: bool, + pub packet_end_index: usize, + pub packet_window_size: usize, + pub packet_index: Option, +} + +impl Inspection { + pub fn new(packets: Arc>>) -> Self { + Self { + packets: packets.clone(), + state: TableState::default(), + fuzzy: Fuzzy::new(packets.clone()), + manuall_scroll: false, + packet_end_index: 0, + packet_window_size: 0, + packet_index: None, + } + } + + pub fn can_show_popup(&mut self) -> bool { + let packets = self.packets.lock().unwrap(); + let fuzzy = self.fuzzy.lock().unwrap(); + + if fuzzy.is_enabled() { + return !fuzzy.packets.is_empty(); + } else { + return !packets.is_empty(); + } + } + + pub fn handle_keys(&mut self, key_event: KeyEvent) { + match key_event.code { + KeyCode::Esc => { + let mut fuzzy = self.fuzzy.lock().unwrap(); + if fuzzy.is_paused() { + if self.manuall_scroll { + self.manuall_scroll = false; + } else { + fuzzy.disable(); + } + } else { + fuzzy.pause(); + } + } + + KeyCode::Char('/') => { + let mut fuzzy = self.fuzzy.lock().unwrap(); + fuzzy.enable(); + fuzzy.unpause(); + } + + KeyCode::Char('j') => { + self.scroll_down(); + } + + KeyCode::Char('k') => { + self.scroll_up(); + } + + _ => { + let mut fuzzy = self.fuzzy.lock().unwrap(); + if !fuzzy.is_paused() { + fuzzy + .filter + .handle_event(&crossterm::event::Event::Key(key_event)); + } + } + } + } + + pub fn scroll_up(&mut self) { + let app_packets = self.packets.lock().unwrap(); + let mut fuzzy = self.fuzzy.lock().unwrap(); + if !self.manuall_scroll { + self.manuall_scroll = true; + // Record the last position. Usefull for selecting the packets to display + if fuzzy.is_enabled() { + fuzzy.packet_end_index = fuzzy.packets.len(); + } else { + self.packet_end_index = app_packets.len(); + } + } + if fuzzy.is_enabled() { + fuzzy.scroll_up(self.packet_window_size); + } else { + let i = match self.state.selected() { + Some(i) => { + if i > 1 { + i - 1 + } else if i == 0 && self.packet_end_index > self.packet_window_size { + // shit the window by one + self.packet_end_index -= 1; + 0 + } else { + 0 + } + } + None => self.packet_window_size, + }; + + self.state.select(Some(i)); + } + } + + pub fn scroll_down(&mut self) { + let app_packets = self.packets.lock().unwrap(); + let mut fuzzy = self.fuzzy.lock().unwrap(); + + if !self.manuall_scroll { + self.manuall_scroll = true; + if fuzzy.is_enabled() { + fuzzy.packet_end_index = fuzzy.packets.len(); + } else { + self.packet_end_index = app_packets.len(); + } + } + if fuzzy.is_enabled() { + fuzzy.scroll_down(self.packet_window_size); + } else { + let i = match self.state.selected() { + Some(i) => { + if i < self.packet_window_size - 1 { + i + 1 + } else if i == self.packet_window_size - 1 + && app_packets.len() > self.packet_end_index + { + // shit the window by one + self.packet_end_index += 1; + i + 1 + } else { + i + } + } + None => app_packets.len(), + }; + + self.state.select(Some(i)); + } + } + + pub fn render(&mut self, frame: &mut Frame, block: Rect) { + let app_packets = self.packets.lock().unwrap(); + let mut fuzzy = self.fuzzy.lock().unwrap(); + let fuzzy_packets = fuzzy.clone().packets.clone(); + + let pattern = fuzzy.clone(); + let pattern = pattern.filter.value(); + + let (packet_block, fuzzy_block) = { + if fuzzy.is_enabled() { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .horizontal_margin(1) + .split(block); + (chunks[0], chunks[1]) + } else { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(1)]) + .horizontal_margin(1) + .split(block); + (chunks[0], chunks[1]) + } + }; + + let widths = [ + Constraint::Min(19), // Source Address + Constraint::Length(11), // Source Port + Constraint::Min(19), // Destination Address + Constraint::Length(16), // Destination Port + Constraint::Length(8), // Protocol + Constraint::Length(3), // manual scroll sign + ]; + + // The size of the window where to display packets + let window_size = block.height.saturating_sub(5) as usize; + self.packet_window_size = window_size; + + // This points always to the end of the window + if self.packet_end_index < window_size { + self.packet_end_index = window_size; + } + + if fuzzy.packet_end_index < window_size { + fuzzy.packet_end_index = window_size; + } + + let packets_to_display = match self.manuall_scroll { + true => { + if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { + if fuzzy_packets.len() > window_size { + if let Some(selected_index) = fuzzy.scroll_state.selected() { + self.packet_index = Some( + fuzzy.packet_end_index.saturating_sub(window_size) + selected_index, + ); + } + &fuzzy_packets[fuzzy.packet_end_index.saturating_sub(window_size) + ..fuzzy.packet_end_index] + } else { + if let Some(selected_index) = fuzzy.scroll_state.selected() { + self.packet_index = Some(selected_index); + } else { + self.packet_index = None; + } + &fuzzy_packets + } + } else if app_packets.len() > window_size { + if let Some(selected_index) = self.state.selected() { + self.packet_index = Some( + self.packet_end_index.saturating_sub(window_size) + selected_index, + ); + } + &app_packets + [self.packet_end_index.saturating_sub(window_size)..self.packet_end_index] + } else { + if let Some(selected_index) = self.state.selected() { + self.packet_index = Some(selected_index); + } + &app_packets + } + } + false => { + if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { + if fuzzy_packets.len() > window_size { + self.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); + &fuzzy_packets[fuzzy_packets.len().saturating_sub(window_size)..] + } else { + self.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); + &fuzzy_packets + } + } else if app_packets.len() > window_size { + self.packet_index = Some(app_packets.len().saturating_sub(1)); + &app_packets[app_packets.len().saturating_sub(window_size)..] + } else { + self.packet_index = Some(app_packets.len().saturating_sub(1)); + &app_packets + } + } + }; + + // Style the packets + let packets: Vec = if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { + packets_to_display + .iter() + .map(|app_packet| match app_packet { + AppPacket::Arp(packet) => Row::new(vec![ + fuzzy::highlight(pattern, packet.src_mac.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, packet.dst_mac.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, "ARP".to_string()).cyan(), + ]), + AppPacket::Ip(packet) => match packet { + IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { + IpProto::Tcp(p) => Row::new(vec![ + fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), + fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + fuzzy::highlight(pattern, "TCP".to_string()).cyan(), + ]), + IpProto::Udp(p) => Row::new(vec![ + fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), + fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + fuzzy::highlight(pattern, "UDP".to_string()).cyan(), + ]), + IpProto::Icmp(_) => Row::new(vec![ + fuzzy::highlight(pattern, ipv4_packet.src_ip.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, ipv4_packet.dst_ip.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), + ]), + }, + IpPacket::V6(ipv6_packet) => match ipv6_packet.proto { + IpProto::Tcp(p) => Row::new(vec![ + fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), + fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + fuzzy::highlight(pattern, "TCP".to_string()).cyan(), + ]), + IpProto::Udp(p) => Row::new(vec![ + fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.src_port.to_string()).yellow(), + fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), + fuzzy::highlight(pattern, p.dst_port.to_string()).yellow(), + fuzzy::highlight(pattern, "UDP".to_string()).cyan(), + ]), + IpProto::Icmp(_) => Row::new(vec![ + fuzzy::highlight(pattern, ipv6_packet.src_ip.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, ipv6_packet.dst_ip.to_string()).blue(), + Cell::from(Line::from("-").centered()).yellow(), + fuzzy::highlight(pattern, "ICMP".to_string()).cyan(), + ]), + }, + }, + }) + .collect() + } else { + packets_to_display + .iter() + .map(|app_packet| match app_packet { + AppPacket::Arp(packet) => Row::new(vec![ + Span::from(packet.src_mac.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from(packet.dst_mac.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from("ARP".to_string()).into_centered_line().cyan(), + ]), + AppPacket::Ip(packet) => match packet { + IpPacket::V4(ipv4_packet) => match ipv4_packet.proto { + IpProto::Tcp(p) => Row::new(vec![ + Span::from(ipv4_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.src_port.to_string()) + .into_centered_line() + .yellow(), + Span::from(ipv4_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.dst_port.to_string()) + .into_centered_line() + .yellow(), + Span::from("TCP".to_string()).into_centered_line().cyan(), + ]), + IpProto::Udp(p) => Row::new(vec![ + Span::from(ipv4_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.src_port.to_string()) + .into_centered_line() + .yellow(), + Span::from(ipv4_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.dst_port.to_string()) + .into_centered_line() + .yellow(), + Span::from("UDP".to_string()).into_centered_line().cyan(), + ]), + IpProto::Icmp(_) => Row::new(vec![ + Span::from(ipv4_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from(ipv4_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from("ICMP".to_string()).into_centered_line().cyan(), + ]), + }, + IpPacket::V6(ipv6_packet) => match ipv6_packet.proto { + IpProto::Tcp(p) => Row::new(vec![ + Span::from(ipv6_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.src_port.to_string()) + .into_centered_line() + .yellow(), + Span::from(ipv6_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.dst_port.to_string()) + .into_centered_line() + .yellow(), + Span::from("TCP".to_string()).into_centered_line().cyan(), + ]), + IpProto::Udp(p) => Row::new(vec![ + Span::from(ipv6_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.src_port.to_string()) + .into_centered_line() + .yellow(), + Span::from(ipv6_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from(p.dst_port.to_string()) + .into_centered_line() + .yellow(), + Span::from("UDP".to_string()).into_centered_line().cyan(), + ]), + IpProto::Icmp(_) => Row::new(vec![ + Span::from(ipv6_packet.src_ip.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from(ipv6_packet.dst_ip.to_string()) + .into_centered_line() + .blue(), + Span::from("-").into_centered_line().yellow(), + Span::from("ICMP".to_string()).into_centered_line().cyan(), + ]), + }, + }, + }) + .collect() + }; + + // Always select the last packet + if !self.manuall_scroll { + if fuzzy.is_enabled() { + fuzzy.scroll_state.select(Some(packets_to_display.len())); + } else { + self.state.select(Some(packets_to_display.len())); + } + } + + let table = Table::new(packets, widths) + .header( + Row::new(vec![ + Line::from("Source Address").centered(), + Line::from("Source Port").centered(), + Line::from("Destination Address").centered(), + Line::from("Destination Port").centered(), + Line::from("Protocol").centered(), + { + if self.manuall_scroll { + Line::from(" ").centered().yellow() + } else { + Line::from("").centered() + } + }, + ]) + .style(Style::new().bold()) + .bottom_margin(1), + ) + .column_spacing(2) + .flex(Flex::SpaceBetween) + .highlight_style(Style::new().bg(ratatui::style::Color::DarkGray)) + .highlight_spacing(HighlightSpacing::Always) + .block(Block::default().padding(Padding::top(2))); + + if fuzzy.is_enabled() { + frame.render_stateful_widget(table, packet_block, &mut fuzzy.scroll_state); + } else { + frame.render_stateful_widget(table, packet_block, &mut self.state); + } + + // Scrollbar + + let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight) + .begin_symbol(Some("↑")) + .end_symbol(Some("↓")); + + let mut scrollbar_state = if fuzzy.is_enabled() && fuzzy_packets.len() > window_size { + ScrollbarState::new(fuzzy_packets.len()).position({ + if self.manuall_scroll { + if fuzzy.packet_end_index == window_size { + 0 + } else { + fuzzy.packet_end_index + } + } else { + fuzzy.packets.len() + } + }) + } else if !fuzzy.is_enabled() && app_packets.len() > window_size { + ScrollbarState::new(app_packets.len()).position({ + if self.manuall_scroll { + if self.packet_end_index == window_size { + 0 + } else { + self.packet_end_index + } + } else { + app_packets.len() + } + }) + } else { + ScrollbarState::default() + }; + + frame.render_stateful_widget( + scrollbar, + packet_block.inner(Margin { + vertical: 1, + horizontal: 0, + }), + &mut scrollbar_state, + ); + + if fuzzy.is_enabled() { + let fuzzy = Paragraph::new(format!("> {}", fuzzy.filter.value())) + .alignment(Alignment::Left) + .style(Style::default().white()) + .block( + Block::new() + .borders(Borders::all()) + .title(" Search  ") + .title_style({ + if fuzzy.is_paused() { + Style::default().bold().green() + } else { + Style::default().bold().yellow() + } + }) + .border_style({ + if fuzzy.is_paused() { + Style::default().green() + } else { + Style::default().yellow() + } + }), + ); + + frame.render_widget(fuzzy, fuzzy_block); + } + } + + pub fn render_packet_infos_popup(&self, frame: &mut Frame) { + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Fill(1), + Constraint::Length(36), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + let block = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Fill(1), + Constraint::Max(80), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(layout[1])[1]; + + let fuzzy = self.fuzzy.lock().unwrap(); + let packets = self.packets.lock().unwrap(); + + let packet = if fuzzy.is_enabled() { + fuzzy.packets[self.packet_index.unwrap()] + } else { + packets[self.packet_index.unwrap()] + }; + + frame.render_widget(Clear, block); + frame.render_widget( + Block::new() + .title(" Packet Infos 󰋼 ") + .title_style(Style::new().bold().green()) + .title_alignment(Alignment::Center) + .borders(Borders::all()) + .border_style(Style::new().green()) + .border_type(BorderType::Thick), + block, + ); + match packet { + AppPacket::Ip(ip_packet) => ip_packet.render(block, frame), + AppPacket::Arp(arp_packet) => arp_packet.render(block, frame), + }; + } +} diff --git a/oryx-tui/src/lib.rs b/oryx-tui/src/lib.rs index f1efc6d..a5a4292 100644 --- a/oryx-tui/src/lib.rs +++ b/oryx-tui/src/lib.rs @@ -20,9 +20,12 @@ pub mod notification; pub mod export; -pub mod stats; - pub mod bandwidth; pub mod alert; pub mod packet; +pub mod stats; + +pub mod section; + +pub mod inspection; diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs new file mode 100644 index 0000000..31a5c75 --- /dev/null +++ b/oryx-tui/src/section.rs @@ -0,0 +1,132 @@ +use std::sync::{Arc, Mutex}; + +use crossterm::event::{KeyCode, KeyEvent}; + +use ratatui::{ + layout::{Alignment, Rect}, + style::{Color, Style, Stylize}, + text::{Line, Span}, + widgets::{Block, BorderType, Borders, Padding}, + Frame, +}; + +use crate::{alert::Alert, inspection::Inspection, packet::AppPacket, stats::Stats}; + +#[derive(Debug, PartialEq)] +pub enum FocusedSection { + Inspection, + Stats, + Alerts, +} + +#[derive(Debug)] +pub struct Section { + focused_section: FocusedSection, + pub inspection: Inspection, + pub stats: Stats, + pub alert: Alert, +} + +impl Section { + pub fn new(packets: Arc>>) -> Self { + Self { + focused_section: FocusedSection::Inspection, + inspection: Inspection::new(packets.clone()), + stats: Stats::new(packets.clone()), + alert: Alert::new(packets.clone()), + } + } + + pub fn render(&mut self, frame: &mut Frame, block: Rect, network_interace: &str) { + match self.focused_section { + FocusedSection::Inspection => { + frame.render_widget( + Block::default() + .title({ + Line::from(vec![ + Span::styled( + " Inspection ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ), + Span::from(" Stats ").fg(Color::DarkGray), + self.alert.title_span(false), + ]) + }) + .title_alignment(Alignment::Left) + .padding(Padding::top(1)) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::default()) + .border_style(Style::default().green()), + block, + ); + self.inspection.render(frame, block); + } + FocusedSection::Stats => { + frame.render_widget( + Block::default() + .title({ + Line::from(vec![ + Span::from(" Inspection ").fg(Color::DarkGray), + Span::styled( + " Stats ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ), + self.alert.title_span(false), + ]) + }) + .title_alignment(Alignment::Left) + .padding(Padding::top(1)) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::default()) + .border_style(Style::default().green()), + block, + ); + self.stats.render(frame, block, network_interace) + } + FocusedSection::Alerts => { + frame.render_widget( + Block::default() + .title({ + Line::from(vec![ + Span::from(" Inspection ").fg(Color::DarkGray), + Span::from(" Stats ").fg(Color::DarkGray), + self.alert.title_span(true), + ]) + }) + .title_alignment(Alignment::Left) + .padding(Padding::top(1)) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::default()) + .border_style(Style::default().green()), + block, + ); + + self.alert.render(frame, block); + } + } + } + + pub fn handle_keys(&mut self, key_event: KeyEvent) { + match key_event.code { + KeyCode::Tab => match self.focused_section { + FocusedSection::Inspection => self.focused_section = FocusedSection::Stats, + FocusedSection::Stats => self.focused_section = FocusedSection::Alerts, + FocusedSection::Alerts => self.focused_section = FocusedSection::Inspection, + }, + + KeyCode::BackTab => match self.focused_section { + FocusedSection::Inspection => self.focused_section = FocusedSection::Alerts, + FocusedSection::Stats => self.focused_section = FocusedSection::Inspection, + FocusedSection::Alerts => self.focused_section = FocusedSection::Stats, + }, + + _ => match self.focused_section { + FocusedSection::Inspection => self.inspection.handle_keys(key_event), + _ => {} + }, + } + } +} diff --git a/oryx-tui/src/stats.rs b/oryx-tui/src/stats.rs index a941acc..a37df11 100644 --- a/oryx-tui/src/stats.rs +++ b/oryx-tui/src/stats.rs @@ -2,6 +2,9 @@ use dns_lookup::lookup_addr; use std::{ collections::HashMap, net::{IpAddr, Ipv4Addr}, + sync::{Arc, Mutex}, + thread, + time::Duration, }; use ratatui::{ @@ -12,13 +15,16 @@ use ratatui::{ Frame, }; -use crate::packet::{ - network::{IpPacket, IpProto}, - AppPacket, +use crate::{ + bandwidth::Bandwidth, + packet::{ + network::{IpPacket, IpProto}, + AppPacket, + }, }; -#[derive(Debug)] -pub struct Stats { +#[derive(Debug, Default)] +pub struct PacketStats { pub total: usize, pub filtered: usize, pub network: NetworkStats, @@ -27,81 +33,118 @@ pub struct Stats { pub addresses: HashMap, usize)>, } -impl Default for Stats { - fn default() -> Self { - Self::new() - } +#[derive(Debug)] +pub struct Stats { + pub packet_stats: Arc>, + pub bandwidth: Bandwidth, } impl Stats { - pub fn new() -> Self { - Self { - total: 0, - filtered: 0, - network: NetworkStats::default(), - transport: TransportStats::default(), - link: LinkStats::default(), - addresses: HashMap::with_capacity(1024), - } - } - pub fn get_top_10(&self) -> Vec<(&Ipv4Addr, &(Option, usize))> { - let mut items: Vec<(&Ipv4Addr, &(Option, usize))> = self.addresses.iter().collect(); - items.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); - items.into_iter().take(10).collect() - } + pub fn new(packets: Arc>>) -> Self { + let packet_stats: Arc> = Arc::new(Mutex::new(PacketStats::default())); - pub fn refresh(&mut self, packet: &AppPacket) { - match packet { - AppPacket::Arp(_) => { - self.link.arp += 1; - } - AppPacket::Ip(packet) => match packet { - IpPacket::V4(ipv4_packet) => { - self.network.ipv4 += 1; + thread::spawn({ + let packet_stats = packet_stats.clone(); + move || { + let mut last_index = 0; + loop { + thread::sleep(Duration::from_millis(160)); + let packets = packets.lock().unwrap(); - if !ipv4_packet.dst_ip.is_private() && !ipv4_packet.dst_ip.is_loopback() { - if let Some((_, counts)) = self.addresses.get_mut(&ipv4_packet.dst_ip) { - *counts += 1; - } else if let Ok(host) = lookup_addr(&IpAddr::V4(ipv4_packet.dst_ip)) { - self.addresses.insert(ipv4_packet.dst_ip, (Some(host), 1)); - } else { - self.addresses.insert(ipv4_packet.dst_ip, (None, 1)); - } + if packets.is_empty() { + continue; } + let mut packet_stats = packet_stats.lock().unwrap(); + for packet in packets[last_index..].iter() { + match packet { + AppPacket::Arp(_) => { + packet_stats.link.arp += 1; + } + AppPacket::Ip(packet) => match packet { + IpPacket::V4(ipv4_packet) => { + packet_stats.network.ipv4 += 1; - match ipv4_packet.proto { - IpProto::Tcp(_) => { - self.transport.tcp += 1; - } - IpProto::Udp(_) => { - self.transport.udp += 1; - } - IpProto::Icmp(_) => { - self.network.icmp += 1; - } - } - } - IpPacket::V6(ipv6_packet) => { - self.network.ipv6 += 1; - match ipv6_packet.proto { - IpProto::Tcp(_) => { - self.transport.tcp += 1; - } - IpProto::Udp(_) => { - self.transport.udp += 1; - } - IpProto::Icmp(_) => { - self.network.icmp += 1; + if !ipv4_packet.dst_ip.is_private() + && !ipv4_packet.dst_ip.is_loopback() + { + if let Some((_, counts)) = + packet_stats.addresses.get_mut(&ipv4_packet.dst_ip) + { + *counts += 1; + } else if let Ok(host) = + lookup_addr(&IpAddr::V4(ipv4_packet.dst_ip)) + { + packet_stats + .addresses + .insert(ipv4_packet.dst_ip, (Some(host), 1)); + } else { + packet_stats + .addresses + .insert(ipv4_packet.dst_ip, (None, 1)); + } + } + + match ipv4_packet.proto { + IpProto::Tcp(_) => { + packet_stats.transport.tcp += 1; + } + IpProto::Udp(_) => { + packet_stats.transport.udp += 1; + } + IpProto::Icmp(_) => { + packet_stats.network.icmp += 1; + } + } + } + IpPacket::V6(ipv6_packet) => { + packet_stats.network.ipv6 += 1; + match ipv6_packet.proto { + IpProto::Tcp(_) => { + packet_stats.transport.tcp += 1; + } + IpProto::Udp(_) => { + packet_stats.transport.udp += 1; + } + IpProto::Icmp(_) => { + packet_stats.network.icmp += 1; + } + } + } + }, } + + packet_stats.total += 1; } + + last_index = packets.len() - 1; } - }, - } + } + }); - self.total += 1; + Self { + packet_stats, + bandwidth: Bandwidth::new(), + } + } + pub fn get_top_10( + &self, + addresses: HashMap, usize)>, + ) -> Vec<(Ipv4Addr, (Option, usize))> { + let mut items: Vec<(Ipv4Addr, (Option, usize))> = addresses.into_iter().collect(); + items.sort_by(|a, b| b.1 .1.cmp(&a.1 .1)); + items.into_iter().take(10).collect() } - pub fn render(&self, frame: &mut Frame, stats_block: Rect) { + pub fn render(&self, frame: &mut Frame, block: Rect, network_interface: &str) { + let (bandwidth_block, stats_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) + .margin(1) + .split(block); + (chunks[0], chunks[1]) + }; + let (address_block, network_block, transport_block, link_block) = { let chunks = Layout::default() .direction(Direction::Horizontal) @@ -120,6 +163,8 @@ impl Stats { (chunks[0], chunks[1], chunks[2], chunks[3]) }; + let packet_stats = self.packet_stats.lock().unwrap(); + let link_chart = BarChart::default() .bar_width(3) .bar_gap(1) @@ -128,13 +173,13 @@ impl Stats { .label("ARP".into()) .style(Style::new().fg(Color::LightYellow)) .value_style(Style::new().fg(Color::Black).bg(Color::LightYellow)) - .text_value(if self.total != 0 { - format!("{}%", self.link.arp * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.link.arp * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.link.arp * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.link.arp * 100 / packet_stats.total) as u64 } else { 0 })]), @@ -151,13 +196,13 @@ impl Stats { .label("TCP".into()) .style(Style::new().fg(Color::LightBlue)) .value_style(Style::new().fg(Color::Black).bg(Color::LightBlue)) - .text_value(if self.total != 0 { - format!("{}%", self.transport.tcp * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.transport.tcp * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.transport.tcp * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.transport.tcp * 100 / packet_stats.total) as u64 } else { 0 }), @@ -165,13 +210,13 @@ impl Stats { .label("UDP".into()) .style(Style::new().fg(Color::LightGreen)) .value_style(Style::new().fg(Color::Black).bg(Color::LightGreen)) - .text_value(if self.total != 0 { - format!("{}%", self.transport.udp * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.transport.udp * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.transport.udp * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.transport.udp * 100 / packet_stats.total) as u64 } else { 0 }), @@ -179,13 +224,13 @@ impl Stats { .label("ICMP".into()) .style(Style::new().fg(Color::LightGreen)) .value_style(Style::new().fg(Color::Black).bg(Color::LightGreen)) - .text_value(if self.total != 0 { - format!("{}%", self.network.icmp * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.network.icmp * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.network.icmp * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.network.icmp * 100 / packet_stats.total) as u64 } else { 0 }), @@ -203,13 +248,13 @@ impl Stats { .label("IPv4".into()) .style(Style::new().fg(Color::LightRed)) .value_style(Style::new().fg(Color::Black).bg(Color::LightRed)) - .text_value(if self.total != 0 { - format!("{}%", self.network.ipv4 * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.network.ipv4 * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.network.ipv4 * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.network.ipv4 * 100 / packet_stats.total) as u64 } else { 0 }), @@ -217,13 +262,13 @@ impl Stats { .label("IPv6".into()) .style(Style::new().fg(Color::LightCyan)) .value_style(Style::new().fg(Color::Black).bg(Color::LightCyan)) - .text_value(if self.total != 0 { - format!("{}%", self.network.ipv6 * 100 / self.total) + .text_value(if packet_stats.total != 0 { + format!("{}%", packet_stats.network.ipv6 * 100 / packet_stats.total) } else { "0%".to_string() }) - .value(if self.total != 0 { - (self.network.ipv6 * 100 / self.total) as u64 + .value(if packet_stats.total != 0 { + (packet_stats.network.ipv6 * 100 / packet_stats.total) as u64 } else { 0 }), @@ -239,7 +284,7 @@ impl Stats { .data( BarGroup::default().bars( &self - .get_top_10() + .get_top_10(packet_stats.addresses.clone()) .into_iter() .map(|(ip, (host, count))| { Bar::default() @@ -247,7 +292,7 @@ impl Stats { .style(Style::new().fg(Color::LightYellow)) .value_style(Style::new().fg(Color::Black).bg(Color::LightYellow)) .text_value(host.clone().unwrap_or(ip.to_string())) - .value(*count as u64) + .value(count as u64) }) .collect::>(), ), @@ -264,6 +309,9 @@ impl Stats { frame.render_widget(transport_chart, transport_block); frame.render_widget(network_chart, network_block); frame.render_widget(link_chart, link_block); + + self.bandwidth + .render(frame, bandwidth_block, network_interface); } } diff --git a/oryx-tui/src/ui.rs b/oryx-tui/src/ui.rs index fa41912..d30b9ca 100644 --- a/oryx-tui/src/ui.rs +++ b/oryx-tui/src/ui.rs @@ -8,8 +8,8 @@ pub fn render(app: &mut App, frame: &mut Frame) { if let Some(popup) = &app.active_popup { match popup { ActivePopup::Help => app.help.render(frame), - ActivePopup::PacketInfos => app.render_packet_infos_popup(frame), - _ => {} + ActivePopup::PacketInfos => app.section.inspection.render_packet_infos_popup(frame), + ActivePopup::UpdateFilters => app.filter.render_update_popup(frame), } } for (index, notification) in app.notifications.iter().enumerate() { From db4cca24940a7af22084f7e98fdbf785953209fd Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 01:54:38 +0200 Subject: [PATCH 07/16] fix linting --- oryx-tui/src/handler.rs | 8 +++----- oryx-tui/src/inspection.rs | 4 ++-- oryx-tui/src/section.rs | 9 +++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 43a0380..0b3fc7d 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -76,12 +76,10 @@ pub fn handle_key_events( } if app.editing_block.is_some() { - match key_event.code { - KeyCode::Esc => { - app.editing_block = None; - } - _ => {} + if key_event.code == KeyCode::Esc { + app.editing_block = None } + app.section.handle_keys(key_event); return Ok(()); } diff --git a/oryx-tui/src/inspection.rs b/oryx-tui/src/inspection.rs index 0b2e91a..840586a 100644 --- a/oryx-tui/src/inspection.rs +++ b/oryx-tui/src/inspection.rs @@ -50,9 +50,9 @@ impl Inspection { let fuzzy = self.fuzzy.lock().unwrap(); if fuzzy.is_enabled() { - return !fuzzy.packets.is_empty(); + !fuzzy.packets.is_empty() } else { - return !packets.is_empty(); + !packets.is_empty() } } diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index 31a5c75..f4243e5 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -123,10 +123,11 @@ impl Section { FocusedSection::Alerts => self.focused_section = FocusedSection::Stats, }, - _ => match self.focused_section { - FocusedSection::Inspection => self.inspection.handle_keys(key_event), - _ => {} - }, + _ => { + if self.focused_section == FocusedSection::Inspection { + self.inspection.handle_keys(key_event); + } + } } } } From 2edbc2a33a27b0145682e53acdc45001907dcfe1 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 08:20:41 +0200 Subject: [PATCH 08/16] use is_editing flag --- oryx-tui/src/app.rs | 10 ++-------- oryx-tui/src/handler.rs | 8 ++++---- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 922db39..ae1cc1c 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -24,12 +24,6 @@ pub enum ActivePopup { PacketInfos, } -#[non_exhaustive] -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum EditingBlock { - Fuzzy, -} - #[derive(Debug)] pub struct DataEventHandler { pub sender: kanal::Sender<[u8; RawPacket::LEN]>, @@ -46,7 +40,7 @@ pub struct App { pub notifications: Vec, pub section: Section, pub data_channel_sender: kanal::Sender<[u8; RawPacket::LEN]>, - pub editing_block: Option, + pub is_editing: bool, pub active_popup: Option, } @@ -84,7 +78,7 @@ impl App { notifications: Vec::new(), section: Section::new(packets.clone()), data_channel_sender: sender, - editing_block: None, + is_editing: false, active_popup: None, } } diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 0b3fc7d..c4906ee 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -1,7 +1,7 @@ use std::{thread, time::Duration}; use crate::{ - app::{ActivePopup, App, AppResult, EditingBlock}, + app::{ActivePopup, App, AppResult}, event::Event, export::export, filter::direction::TrafficDirection, @@ -75,9 +75,9 @@ pub fn handle_key_events( return Ok(()); } - if app.editing_block.is_some() { + if app.is_editing { if key_event.code == KeyCode::Esc { - app.editing_block = None + app.is_editing = false } app.section.handle_keys(key_event); @@ -124,7 +124,7 @@ pub fn handle_key_events( } KeyCode::Char('/') => { - app.editing_block = Some(EditingBlock::Fuzzy); + app.is_editing = true; app.section.handle_keys(key_event); } From 3273ea95285a49daaca373dbeda18191de2ad668 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 09:52:58 +0200 Subject: [PATCH 09/16] fix scrolling for inspection --- oryx-tui/src/inspection.rs | 167 ++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/oryx-tui/src/inspection.rs b/oryx-tui/src/inspection.rs index 840586a..fde6779 100644 --- a/oryx-tui/src/inspection.rs +++ b/oryx-tui/src/inspection.rs @@ -57,113 +57,130 @@ impl Inspection { } pub fn handle_keys(&mut self, key_event: KeyEvent) { - match key_event.code { - KeyCode::Esc => { - let mut fuzzy = self.fuzzy.lock().unwrap(); - if fuzzy.is_paused() { - if self.manuall_scroll { + let fuzzy_is_enabled = { self.fuzzy.lock().unwrap().is_enabled() }; + + if fuzzy_is_enabled { + let mut fuzzy = self.fuzzy.lock().unwrap(); + match key_event.code { + KeyCode::Esc => { + if !fuzzy.is_paused() { + fuzzy.pause(); + } else if self.manuall_scroll { self.manuall_scroll = false; } else { fuzzy.disable(); } - } else { - fuzzy.pause(); } - } + _ => { + if !fuzzy.is_paused() { + fuzzy + .filter + .handle_event(&crossterm::event::Event::Key(key_event)); + } else { + match key_event.code { + KeyCode::Char('j') => { + if !self.manuall_scroll { + self.manuall_scroll = true; + fuzzy.packet_end_index = fuzzy.packets.len(); + } + fuzzy.scroll_down(self.packet_window_size); + } - KeyCode::Char('/') => { - let mut fuzzy = self.fuzzy.lock().unwrap(); - fuzzy.enable(); - fuzzy.unpause(); - } + KeyCode::Char('/') => { + fuzzy.enable(); + fuzzy.unpause(); + } - KeyCode::Char('j') => { - self.scroll_down(); - } + KeyCode::Char('k') => { + if !self.manuall_scroll { + self.manuall_scroll = true; + fuzzy.packet_end_index = fuzzy.packets.len(); + } + fuzzy.scroll_up(self.packet_window_size); + } - KeyCode::Char('k') => { - self.scroll_up(); + _ => {} + } + } + } } + } else { + match key_event.code { + KeyCode::Esc => { + if self.manuall_scroll { + self.manuall_scroll = false; + } + } + + KeyCode::Char('j') => { + self.scroll_down(); + } - _ => { - let mut fuzzy = self.fuzzy.lock().unwrap(); - if !fuzzy.is_paused() { - fuzzy - .filter - .handle_event(&crossterm::event::Event::Key(key_event)); + KeyCode::Char('/') => { + let mut fuzzy = self.fuzzy.lock().unwrap(); + fuzzy.enable(); + fuzzy.unpause(); } + + KeyCode::Char('k') => { + self.scroll_up(); + } + + _ => {} } } } pub fn scroll_up(&mut self) { let app_packets = self.packets.lock().unwrap(); - let mut fuzzy = self.fuzzy.lock().unwrap(); if !self.manuall_scroll { self.manuall_scroll = true; // Record the last position. Usefull for selecting the packets to display - if fuzzy.is_enabled() { - fuzzy.packet_end_index = fuzzy.packets.len(); - } else { - self.packet_end_index = app_packets.len(); - } + self.packet_end_index = app_packets.len(); } - if fuzzy.is_enabled() { - fuzzy.scroll_up(self.packet_window_size); - } else { - let i = match self.state.selected() { - Some(i) => { - if i > 1 { - i - 1 - } else if i == 0 && self.packet_end_index > self.packet_window_size { - // shit the window by one - self.packet_end_index -= 1; - 0 - } else { - 0 - } + let i = match self.state.selected() { + Some(i) => { + if i > 1 { + i - 1 + } else if i == 0 && self.packet_end_index > self.packet_window_size { + // shit the window by one + self.packet_end_index -= 1; + 0 + } else { + 0 } - None => self.packet_window_size, - }; + } + None => self.packet_window_size, + }; - self.state.select(Some(i)); - } + self.state.select(Some(i)); } pub fn scroll_down(&mut self) { let app_packets = self.packets.lock().unwrap(); - let mut fuzzy = self.fuzzy.lock().unwrap(); if !self.manuall_scroll { self.manuall_scroll = true; - if fuzzy.is_enabled() { - fuzzy.packet_end_index = fuzzy.packets.len(); - } else { - self.packet_end_index = app_packets.len(); - } + self.packet_end_index = app_packets.len(); } - if fuzzy.is_enabled() { - fuzzy.scroll_down(self.packet_window_size); - } else { - let i = match self.state.selected() { - Some(i) => { - if i < self.packet_window_size - 1 { - i + 1 - } else if i == self.packet_window_size - 1 - && app_packets.len() > self.packet_end_index - { - // shit the window by one - self.packet_end_index += 1; - i + 1 - } else { - i - } + let i = match self.state.selected() { + Some(i) => { + if i < self.packet_window_size - 1 { + i + 1 + } else if i == self.packet_window_size - 1 + && app_packets.len() > self.packet_end_index + { + // shit the window by one + self.packet_end_index += 1; + i + 1 + } else { + i } - None => app_packets.len(), - }; + } + None => app_packets.len(), + }; - self.state.select(Some(i)); - } + self.state.select(Some(i)); } pub fn render(&mut self, frame: &mut Frame, block: Rect) { @@ -525,7 +542,7 @@ impl Inspection { .style(Style::default().white()) .block( Block::new() - .borders(Borders::all()) + .borders(Borders::TOP) .title(" Search  ") .title_style({ if fuzzy.is_paused() { From 459b8d286a275bd7445484b1fc541c64c8d2e089 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 11:28:17 +0200 Subject: [PATCH 10/16] fixes --- oryx-tui/src/filter.rs | 3 ++- oryx-tui/src/handler.rs | 28 +++++++++++++--------------- oryx-tui/src/inspection.rs | 8 ++++---- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 4a9740a..73e6821 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -67,7 +67,8 @@ pub struct Filter { pub traffic_direction: TrafficDirectionFilter, pub ingress_channel: FilterChannel, pub egress_channel: FilterChannel, - focused_block: FocusedBlock, + pub focused_block: FocusedBlock, + //TODO: maybe remove show_popup: bool, } diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index c4906ee..3a4c4c7 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -4,7 +4,7 @@ use crate::{ app::{ActivePopup, App, AppResult}, event::Event, export::export, - filter::direction::TrafficDirection, + filter::FocusedBlock, notification::{Notification, NotificationLevel}, }; use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; @@ -18,10 +18,12 @@ pub fn handle_key_events( if !app.start_sniffing { match key_event.code { KeyCode::Enter => { - app.filter - .start(sender.clone(), app.data_channel_sender.clone()); + if app.filter.focused_block == FocusedBlock::Apply { + app.filter + .start(sender.clone(), app.data_channel_sender.clone()); - app.start_sniffing = true; + app.start_sniffing = true; + } } KeyCode::Esc => { @@ -59,9 +61,12 @@ pub fn handle_key_events( } } KeyCode::Enter => { - if popup == ActivePopup::UpdateFilters { + if popup == ActivePopup::UpdateFilters + && app.filter.focused_block == FocusedBlock::Apply + { app.filter .update(sender.clone(), app.data_channel_sender.clone())?; + app.active_popup = None; } } @@ -90,20 +95,13 @@ pub fn handle_key_events( } KeyCode::Char('f') => { - if app.active_popup.is_none() { - app.active_popup = Some(ActivePopup::UpdateFilters); - app.filter.trigger(); - } + app.active_popup = Some(ActivePopup::UpdateFilters); + app.filter.trigger(); } KeyCode::Char('r') => { if key_event.modifiers == KeyModifiers::CONTROL { - app.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - app.filter - .traffic_direction - .terminate(TrafficDirection::Egress); + app.filter.terminate(); thread::sleep(Duration::from_millis(150)); sender.send(Event::Reset)?; } diff --git a/oryx-tui/src/inspection.rs b/oryx-tui/src/inspection.rs index fde6779..1a3df18 100644 --- a/oryx-tui/src/inspection.rs +++ b/oryx-tui/src/inspection.rs @@ -78,7 +78,7 @@ impl Inspection { .handle_event(&crossterm::event::Event::Key(key_event)); } else { match key_event.code { - KeyCode::Char('j') => { + KeyCode::Char('j') | KeyCode::Down => { if !self.manuall_scroll { self.manuall_scroll = true; fuzzy.packet_end_index = fuzzy.packets.len(); @@ -91,7 +91,7 @@ impl Inspection { fuzzy.unpause(); } - KeyCode::Char('k') => { + KeyCode::Char('k') | KeyCode::Up => { if !self.manuall_scroll { self.manuall_scroll = true; fuzzy.packet_end_index = fuzzy.packets.len(); @@ -112,7 +112,7 @@ impl Inspection { } } - KeyCode::Char('j') => { + KeyCode::Char('j') | KeyCode::Down => { self.scroll_down(); } @@ -122,7 +122,7 @@ impl Inspection { fuzzy.unpause(); } - KeyCode::Char('k') => { + KeyCode::Char('k') | KeyCode::Up => { self.scroll_up(); } From 3aa6225bba7394d28ffa95635adccc2e3ec385e1 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 11:35:43 +0200 Subject: [PATCH 11/16] remove filter.show_popup --- oryx-tui/src/filter.rs | 18 +++--------------- oryx-tui/src/handler.rs | 6 +++--- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/oryx-tui/src/filter.rs b/oryx-tui/src/filter.rs index 73e6821..d6c9ea6 100644 --- a/oryx-tui/src/filter.rs +++ b/oryx-tui/src/filter.rs @@ -68,8 +68,6 @@ pub struct Filter { pub ingress_channel: FilterChannel, pub egress_channel: FilterChannel, pub focused_block: FocusedBlock, - //TODO: maybe remove - show_popup: bool, } impl Default for Filter { @@ -89,7 +87,6 @@ impl Filter { ingress_channel: FilterChannel::new(), egress_channel: FilterChannel::new(), focused_block: FocusedBlock::Interface, - show_popup: false, } } @@ -137,8 +134,6 @@ impl Filter { } pub fn trigger(&mut self) { - self.show_popup = true; - self.network.selected_protocols = self.network.applied_protocols.clone(); self.transport.selected_protocols = self.transport.applied_protocols.clone(); @@ -302,19 +297,12 @@ impl Filter { .store(false, std::sync::atomic::Ordering::Relaxed); self.sync()?; - self.show_popup = false; Ok(()) } - pub fn handle_key_events(&mut self, key_event: KeyEvent) { + pub fn handle_key_events(&mut self, key_event: KeyEvent, is_update_popup_displayed: bool) { match key_event.code { - KeyCode::Esc => { - if self.show_popup { - self.show_popup = false - } - } - KeyCode::Tab => match self.focused_block { FocusedBlock::Interface => { self.focused_block = FocusedBlock::TransportFilter; @@ -345,7 +333,7 @@ impl Filter { } FocusedBlock::Apply => { - if self.show_popup { + if is_update_popup_displayed { self.focused_block = FocusedBlock::TransportFilter; } else { self.focused_block = FocusedBlock::Interface; @@ -360,7 +348,7 @@ impl Filter { } FocusedBlock::TransportFilter => { - if self.show_popup { + if is_update_popup_displayed { self.focused_block = FocusedBlock::Apply; self.transport.state.select(None); } else { diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 3a4c4c7..6ffa71d 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -44,7 +44,7 @@ pub fn handle_key_events( } } _ => { - app.filter.handle_key_events(key_event); + app.filter.handle_key_events(key_event, false); } } return Ok(()); @@ -57,7 +57,7 @@ pub fn handle_key_events( KeyCode::Esc => { app.active_popup = None; if popup == ActivePopup::UpdateFilters { - app.filter.handle_key_events(key_event); + app.filter.handle_key_events(key_event, true); } } KeyCode::Enter => { @@ -72,7 +72,7 @@ pub fn handle_key_events( } _ => { if popup == ActivePopup::UpdateFilters { - app.filter.handle_key_events(key_event); + app.filter.handle_key_events(key_event, true); } } } From c56af31b109a7016137d0dd1722d797893820857 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 11:41:44 +0200 Subject: [PATCH 12/16] restructure sections --- oryx-tui/src/lib.rs | 4 ---- oryx-tui/src/section.rs | 9 ++++++++- oryx-tui/src/{ => section}/alert.rs | 0 oryx-tui/src/{ => section}/alert/syn_flood.rs | 0 oryx-tui/src/{ => section}/inspection.rs | 0 oryx-tui/src/{ => section}/stats.rs | 0 6 files changed, 8 insertions(+), 5 deletions(-) rename oryx-tui/src/{ => section}/alert.rs (100%) rename oryx-tui/src/{ => section}/alert/syn_flood.rs (100%) rename oryx-tui/src/{ => section}/inspection.rs (100%) rename oryx-tui/src/{ => section}/stats.rs (100%) diff --git a/oryx-tui/src/lib.rs b/oryx-tui/src/lib.rs index a5a4292..2af7a70 100644 --- a/oryx-tui/src/lib.rs +++ b/oryx-tui/src/lib.rs @@ -22,10 +22,6 @@ pub mod export; pub mod bandwidth; -pub mod alert; pub mod packet; -pub mod stats; pub mod section; - -pub mod inspection; diff --git a/oryx-tui/src/section.rs b/oryx-tui/src/section.rs index f4243e5..3cc132a 100644 --- a/oryx-tui/src/section.rs +++ b/oryx-tui/src/section.rs @@ -1,7 +1,13 @@ +pub mod alert; +pub mod inspection; +pub mod stats; + use std::sync::{Arc, Mutex}; +use alert::Alert; use crossterm::event::{KeyCode, KeyEvent}; +use inspection::Inspection; use ratatui::{ layout::{Alignment, Rect}, style::{Color, Style, Stylize}, @@ -9,8 +15,9 @@ use ratatui::{ widgets::{Block, BorderType, Borders, Padding}, Frame, }; +use stats::Stats; -use crate::{alert::Alert, inspection::Inspection, packet::AppPacket, stats::Stats}; +use crate::packet::AppPacket; #[derive(Debug, PartialEq)] pub enum FocusedSection { diff --git a/oryx-tui/src/alert.rs b/oryx-tui/src/section/alert.rs similarity index 100% rename from oryx-tui/src/alert.rs rename to oryx-tui/src/section/alert.rs diff --git a/oryx-tui/src/alert/syn_flood.rs b/oryx-tui/src/section/alert/syn_flood.rs similarity index 100% rename from oryx-tui/src/alert/syn_flood.rs rename to oryx-tui/src/section/alert/syn_flood.rs diff --git a/oryx-tui/src/inspection.rs b/oryx-tui/src/section/inspection.rs similarity index 100% rename from oryx-tui/src/inspection.rs rename to oryx-tui/src/section/inspection.rs diff --git a/oryx-tui/src/stats.rs b/oryx-tui/src/section/stats.rs similarity index 100% rename from oryx-tui/src/stats.rs rename to oryx-tui/src/section/stats.rs From 1a10ddb13068a4fe9db766477bc99740c87372d2 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 11:52:02 +0200 Subject: [PATCH 13/16] update justfile --- Justfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Justfile b/Justfile index 355a469..0c8de83 100644 --- a/Justfile +++ b/Justfile @@ -14,8 +14,12 @@ show interface: sudo tc filter show dev $interface ingress sudo tc filter show dev $interface egress -# Run oryx +# Run oryx debug run: + cargo xtask run + +# Run oryx debug +release: cargo xtask run --release # Build oryx From b0fa6f463b018086305ce49c1f29cabd7502d8b6 Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 12:44:08 +0200 Subject: [PATCH 14/16] update fuzzy render style --- oryx-tui/src/section/inspection.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/oryx-tui/src/section/inspection.rs b/oryx-tui/src/section/inspection.rs index 1a3df18..1219164 100644 --- a/oryx-tui/src/section/inspection.rs +++ b/oryx-tui/src/section/inspection.rs @@ -546,16 +546,23 @@ impl Inspection { .title(" Search  ") .title_style({ if fuzzy.is_paused() { + Style::default().bold().yellow() + } else { Style::default().bold().green() + } + }) + .border_type({ + if fuzzy.is_paused() { + BorderType::default() } else { - Style::default().bold().yellow() + BorderType::Thick } }) .border_style({ if fuzzy.is_paused() { - Style::default().green() - } else { Style::default().yellow() + } else { + Style::default().green() } }), ); From 7cad371515fcdd210f094ef1b64879eedc68229c Mon Sep 17 00:00:00 2001 From: Badr Date: Wed, 2 Oct 2024 14:02:47 +0200 Subject: [PATCH 15/16] update inspection section layout --- oryx-tui/src/section/inspection.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/oryx-tui/src/section/inspection.rs b/oryx-tui/src/section/inspection.rs index 1219164..6bca420 100644 --- a/oryx-tui/src/section/inspection.rs +++ b/oryx-tui/src/section/inspection.rs @@ -195,17 +195,25 @@ impl Inspection { if fuzzy.is_enabled() { let chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(3)]) + .constraints([ + Constraint::Length(1), + Constraint::Fill(1), + Constraint::Length(3), + ]) .horizontal_margin(1) .split(block); - (chunks[0], chunks[1]) + (chunks[1], chunks[2]) } else { let chunks = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Fill(1), Constraint::Length(1)]) + .constraints([ + Constraint::Length(1), + Constraint::Fill(1), + Constraint::Length(1), + ]) .horizontal_margin(1) .split(block); - (chunks[0], chunks[1]) + (chunks[1], chunks[2]) } }; @@ -485,7 +493,7 @@ impl Inspection { .flex(Flex::SpaceBetween) .highlight_style(Style::new().bg(ratatui::style::Color::DarkGray)) .highlight_spacing(HighlightSpacing::Always) - .block(Block::default().padding(Padding::top(2))); + .block(Block::default().padding(Padding::uniform(1))); if fuzzy.is_enabled() { frame.render_stateful_widget(table, packet_block, &mut fuzzy.scroll_state); @@ -544,6 +552,7 @@ impl Inspection { Block::new() .borders(Borders::TOP) .title(" Search  ") + .padding(Padding::horizontal(1)) .title_style({ if fuzzy.is_paused() { Style::default().bold().yellow() From 2420f4122c920fab0d1b838912ba0fb423fa14ef Mon Sep 17 00:00:00 2001 From: adrien gaultier Date: Wed, 2 Oct 2024 16:36:27 +0200 Subject: [PATCH 16/16] biggest refacto so far --- oryx-tui/src/section/inspection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oryx-tui/src/section/inspection.rs b/oryx-tui/src/section/inspection.rs index 6bca420..79bb9ae 100644 --- a/oryx-tui/src/section/inspection.rs +++ b/oryx-tui/src/section/inspection.rs @@ -480,7 +480,7 @@ impl Inspection { Line::from("Protocol").centered(), { if self.manuall_scroll { - Line::from(" ").centered().yellow() + Line::from("󰹆").centered().yellow() } else { Line::from("").centered() }