diff --git a/oryx-tui/src/app.rs b/oryx-tui/src/app.rs index 2015fc4..201dea8 100644 --- a/oryx-tui/src/app.rs +++ b/oryx-tui/src/app.rs @@ -23,7 +23,9 @@ use crate::filters::{ fuzzy::{self, Fuzzy}, link::LinkFilter, network::NetworkFilter, + start_menu::StartMenuBlock, transport::TransportFilter, + update_menu::UpdateFilterMenuBLock, }; use crate::help::Help; @@ -33,7 +35,7 @@ use crate::packets::{ network::{IpPacket, IpProto}, packet::AppPacket, }; -use crate::startmenu::StartMenuBlock; + use crate::stats::Stats; use crate::{alerts::alert::Alert, firewall::Firewall}; use crate::{bandwidth::Bandwidth, mode::Mode}; @@ -44,6 +46,7 @@ pub const TICK_RATE: u64 = 40; #[derive(Debug, Copy, Clone, PartialEq)] pub enum FocusedBlock { StartMenuBlock(StartMenuBlock), + UpdateFilterMenuBlock(UpdateFilterMenuBLock), Help, Main(Mode), } @@ -137,551 +140,60 @@ impl App { 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); - } else { - // Sniffing - let (settings_block, mode_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 - if self.mode == Mode::Alerts { - self.mode - .render(frame, mode_block, self.alert.title_span(true)); - } else { - self.mode - .render(frame, mode_block, self.alert.title_span(false)); - }; - match self.mode { - Mode::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), - Mode::Firewall => self.firewall.render(frame, mode_block), - } - - // Update filters - - if self.update_filters { - self.filter.update(frame, mode_block, &self.focused_block); - } - } - } - pub fn detach_ebpf(&mut self) { - self.filter - .traffic_direction - .terminate(TrafficDirection::Egress); - self.filter - .traffic_direction - .terminate(TrafficDirection::Ingress); - thread::sleep(Duration::from_millis(150)); - } - 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 + match self.focused_block { + FocusedBlock::StartMenuBlock(b) => b.render(frame, &mut self), + FocusedBlock::Main(mode) => self.render_main_section(frame, mode), + _ => { + match self.previous_focused_block { + FocusedBlock::StartMenuBlock(b) => b.render(frame, &mut self), + FocusedBlock::Main(mode) => self.render_main_section(frame, mode), + _ => {} } - } - 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 + match self.focused_block { + FocusedBlock::UpdateFilterMenuBlock(b) => b.render(frame, self), + FocusedBlock::Help => self.help.render(frame), + _ => {} } } - }; - - // 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::new().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.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() + fn render_main_section(&mut self, frame: &mut Frame, mode: Mode) { + // Build layout + let (settings_block, mode_block) = { + let chunks: std::rc::Rc<[Rect]> = Layout::default() .direction(Direction::Vertical) - .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) - .margin(2) - .split(block); + .constraints([Constraint::Length(8), Constraint::Fill(1)]) + .split(frame.area()); + (chunks[0], chunks[1]) + }; + 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]) }; - stats.render(frame, stats_block); + // Render settings + // Interface + self.interface.render_on_sniffing(frame, interface_block); + // Filters + self.filter.render_on_sniffing(frame, filter_block); - self.bandwidth.render( - frame, - bandwidth_block, - &self.interface.selected_interface.name.clone(), - ); + // Render mode section + mode.render(frame, mode_block, self); } - 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 detach_ebpf(&mut self) { + self.filter + .traffic_direction + .terminate(TrafficDirection::Egress); + self.filter + .traffic_direction + .terminate(TrafficDirection::Ingress); + thread::sleep(Duration::from_millis(150)); } pub fn process( diff --git a/oryx-tui/src/filters/filter.rs b/oryx-tui/src/filters/filter.rs index 0776156..05c16cd 100644 --- a/oryx-tui/src/filters/filter.rs +++ b/oryx-tui/src/filters/filter.rs @@ -193,80 +193,4 @@ impl Filter { frame.render_widget(table, block); } - - pub fn update(&mut self, frame: &mut Frame, block: Rect, focused_block: &FocusedBlock) { - let layout = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Fill(1), - Constraint::Length(40), - Constraint::Fill(1), - ]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(block); - - let block = Layout::default() - .direction(Direction::Horizontal) - .constraints([ - Constraint::Fill(1), - Constraint::Length(60), - Constraint::Fill(1), - ]) - .flex(ratatui::layout::Flex::SpaceBetween) - .split(layout[1])[1]; - - let ( - transport_filter_block, - network_filter_block, - link_filter_block, - traffic_direction_block, - apply_block, - ) = { - let chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - 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::SpaceBetween) - .split(block); - (chunks[0], chunks[1], chunks[2], chunks[3], chunks[4]) - }; - - frame.render_widget(Clear, block); - frame.render_widget( - Block::new() - .borders(Borders::all()) - .border_type(BorderType::Thick) - .border_style(Style::default().green()), - block, - ); - - self.transport - .render(frame, transport_filter_block, focused_block); - - self.network - .render(frame, network_filter_block, focused_block); - - self.link.render(frame, link_filter_block, focused_block); - - self.traffic_direction - .render(frame, traffic_direction_block, focused_block); - - let apply = BigText::builder() - .pixel_size(PixelSize::Sextant) - .style(if *focused_block == FocusedBlock::Start { - Style::default().white().bold() - } else { - Style::default().dark_gray() - }) - .lines(vec!["APPLY".into()]) - .centered() - .build(); - frame.render_widget(apply, apply_block); - } } diff --git a/oryx-tui/src/filters/network.rs b/oryx-tui/src/filters/network.rs index 1399953..5f13a81 100644 --- a/oryx-tui/src/filters/network.rs +++ b/oryx-tui/src/filters/network.rs @@ -144,11 +144,13 @@ impl NetworkFilter { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::NetworkFilter { - BorderType::Thick - } else { - BorderType::default() - }) + .border_type( + if *focused_block == FocusedBlock::StartMenuBlock(NetworkFilter) { + BorderType::Thick + } else { + BorderType::default() + }, + ) .border_style(Style::default().fg(Color::Green)), area, ); diff --git a/oryx-tui/src/startmenu.rs b/oryx-tui/src/filters/start_menu.rs similarity index 76% rename from oryx-tui/src/startmenu.rs rename to oryx-tui/src/filters/start_menu.rs index e73570e..b8cc3c6 100644 --- a/oryx-tui/src/startmenu.rs +++ b/oryx-tui/src/filters/start_menu.rs @@ -101,12 +101,41 @@ impl StartMenuBlock { KeyCode::Char('j') | KeyCode::Down => { self.scroll_down(app); } - KeyCode::Esc => { - if app.update_filters { - app.update_filters = false - } - } _ => {} } } + pub fn render(&mut self, frame: &mut Frame, app: &mut App) { + let (interface_block, filter_block, start_block) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(app.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 + app.interface.render_on_setup(frame, interface_block, &self); + + // Filters + app.filter.render_on_setup(frame, filter_block, &self); + + // 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); + } } diff --git a/oryx-tui/src/filters/update_menu.rs b/oryx-tui/src/filters/update_menu.rs new file mode 100644 index 0000000..c5e7f23 --- /dev/null +++ b/oryx-tui/src/filters/update_menu.rs @@ -0,0 +1,184 @@ +use crate::app::App; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum UpdateFilterMenuBLock { + TransportFilter, + NetworkFilter, + LinkFilter, + TrafficDirection, + Start, +} + +impl UpdateFilterMenuBLock { + pub fn next(&mut self, app: &mut App) { + self.unselect(app); + *self = match self { + UpdateFilterMenuBLock::TransportFilter => UpdateFilterMenuBLock::NetworkFilter, + UpdateFilterMenuBLock::NetworkFilter => UpdateFilterMenuBLock::LinkFilter, + UpdateFilterMenuBLock::LinkFilter => UpdateFilterMenuBLock::TrafficDirection, + UpdateFilterMenuBLock::TrafficDirection => UpdateFilterMenuBLock::Start, + UpdateFilterMenuBLock::Start => UpdateFilterMenuBLock::TransportFilter, + }; + self.select(app); + } + pub fn app_component(self, app: &mut App) -> Option<&mut TableState> { + match self { + UpdateFilterMenuBLock::TransportFilter => Some(&mut (*app).filter.transport.state), + UpdateFilterMenuBLock::NetworkFilter => Some(&mut (*app).filter.network.state), + UpdateFilterMenuBLock::LinkFilter => Some(&mut (*app).filter.link.state), + UpdateFilterMenuBLock::TrafficDirection => { + Some(&mut (*app).filter.traffic_direction.state) + } + UpdateFilterMenuBLock::Start => None, + } + } + pub fn previous(&mut self, app: &mut App) { + self.unselect(app); + *self = match self { + UpdateFilterMenuBLock::TransportFilter => UpdateFilterMenuBLock::Start, + UpdateFilterMenuBLock::NetworkFilter => UpdateFilterMenuBLock::TransportFilter, + UpdateFilterMenuBLock::LinkFilter => UpdateFilterMenuBLock::NetworkFilter, + UpdateFilterMenuBLock::TrafficDirection => UpdateFilterMenuBLock::LinkFilter, + UpdateFilterMenuBLock::Start => UpdateFilterMenuBLock::TrafficDirection, + }; + self.select(app); + } + + fn select(self, app: &mut App) { + match self.app_component(app) { + Some(p) => { + p.select(Some(0)); + } + None => {} + } + } + fn unselect(self, app: &mut App) { + match self.app_component(app) { + Some(p) => { + p.select(None); + } + None => {} + } + } + pub fn scroll_up(self, app: &mut App) { + match self { + UpdateFilterMenuBLock::TransportFilter => (*app).filter.transport.scroll_up(), + UpdateFilterMenuBLock::NetworkFilter => (*app).filter.network.scroll_up(), + UpdateFilterMenuBLock::LinkFilter => (*app).filter.link.scroll_up(), + UpdateFilterMenuBLock::TrafficDirection => { + (*app).filter.traffic_direction.state.select(Some(0)) + } + _ => {} + } + } + + pub fn scroll_down(self, app: &mut App) { + match self { + UpdateFilterMenuBLock::TransportFilter => (*app).filter.transport.scroll_down(), + UpdateFilterMenuBLock::NetworkFilter => (*app).filter.network.scroll_down(), + UpdateFilterMenuBLock::LinkFilter => (*app).filter.link.scroll_down(), + UpdateFilterMenuBLock::TrafficDirection => { + (*app).filter.traffic_direction.state.select(Some(1)) + } + _ => {} + } + } + pub fn handle_key_events(&mut self, key_event: KeyEvent, app: &mut App) { + match key_event.code { + KeyCode::Tab => { + self.next(app); + } + KeyCode::BackTab => { + self.previous(app); + } + KeyCode::Char('k') | KeyCode::Up => { + self.scroll_up(app); + } + KeyCode::Char('j') | KeyCode::Down => { + self.scroll_down(app); + } + + KeyCode::Esc => { + app.focused_block = app.previous_focused_block; + app.update_filters = false; + } + _ => {} + } + } + pub fn render(&mut self, frame: &mut Frame, app: App) { + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Fill(1), + Constraint::Length(40), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(frame.area()); + + let block = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Fill(1), + Constraint::Length(60), + Constraint::Fill(1), + ]) + .flex(ratatui::layout::Flex::SpaceBetween) + .split(layout[1])[1]; + + let ( + transport_filter_block, + network_filter_block, + link_filter_block, + traffic_direction_block, + apply_block, + ) = { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + 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::SpaceBetween) + .split(block); + (chunks[0], chunks[1], chunks[2], chunks[3], chunks[4]) + }; + + frame.render_widget(Clear, block); + frame.render_widget( + Block::new() + .borders(Borders::all()) + .border_type(BorderType::Thick) + .border_style(Style::default().green()), + block, + ); + + app.filter + .transport + .render(frame, transport_filter_block, self); + + app.filter.network.render(frame, network_filter_block, self); + + app.filter.link.render(frame, link_filter_block, self); + + app.filter + .traffic_direction + .render(frame, traffic_direction_block, self); + + let apply = BigText::builder() + .pixel_size(PixelSize::Sextant) + .style(if *self == UpdateFilterMenuBLock::Start { + Style::default().white().bold() + } else { + Style::default().dark_gray() + }) + .lines(vec!["APPLY".into()]) + .centered() + .build(); + frame.render_widget(apply, apply_block); + } +} diff --git a/oryx-tui/src/handler.rs b/oryx-tui/src/handler.rs index 6c84d43..f6f899b 100644 --- a/oryx-tui/src/handler.rs +++ b/oryx-tui/src/handler.rs @@ -26,19 +26,17 @@ fn handle_key_events_help(key_event: KeyEvent, app: &mut App) { } } - pub fn handle_key_events( key_event: KeyEvent, app: &mut App, sender: kanal::Sender, ) -> AppResult<()> { - // handle global key events if !app.is_editing { match key_event.code { KeyCode::Char('?') => { app.focused_block = FocusedBlock::Help; - return Ok(()) + return Ok(()); } KeyCode::Char('q') => { app.detach_ebpf(); @@ -55,7 +53,7 @@ pub fn handle_key_events( if key_event.modifiers == KeyModifiers::CONTROL { app.detach_ebpf(); sender.send(Event::Reset)?; - return Ok(()) + return Ok(()); } } @@ -64,895 +62,890 @@ pub fn handle_key_events( } match app.focused_block { FocusedBlock::Help => handle_key_events_help(key_event, app), - FocusedBlock::StartMenuBlock( &mut start_block) => start_block.handle_key_events(key_event, app), - FocusedBlock::Main( &mut mode_block) => mode_block.handle_key_events(key_event, app), - } - return Ok(()) -} - - - - - - - - - - - -/////////////////////// -// old -pub fn old_handle_key_events( - key_event: KeyEvent, - 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() { - // 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(()); - // } - - // if fuzzy.is_paused() { - // if app.manuall_scroll { - // app.manuall_scroll = false; - // } else { - // fuzzy.disable(); - // } - // } else { - // fuzzy.pause(); - // } - // } - - _ => { - 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(); - // } - // 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)); - } 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(); - // } - // 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)); - } 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(); - // } - // _ => {} - } - } - } - - _ => {} - } - } - } + FocusedBlock::StartMenuBlock(&mut start_block) => { + start_block.handle_key_events(key_event, app) } - } 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; - // } - 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('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::Char('/') => { - // if app.focused_block == FocusedBlock::Help || app.update_filters { - // return Ok(()); - // } - // if app.start_sniffing { - // fuzzy.enable(); - // fuzzy.unpause(); - // } - // } - // 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(()); - // } - - // app.show_packet_infos_popup = true; - // } - // KeyCode::Char('r') => { - // if app.focused_block == FocusedBlock::Help || app.update_filters { - // return Ok(()); - // } - // if key_event.modifiers == KeyModifiers::CONTROL { - // app.detach_interfaces(); - // sender.send(Event::Reset)?; - // } - // } - - 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 - .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))?; - } - } - } - - // 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.mode { - // Mode::Packet => app.mode = Mode::Stats, - // Mode::Stats => app.mode = Mode::Alerts, - // Mode::Alerts => app.mode = Mode::Firewall, - // Mode::Firewall => app.mode = Mode::Packet, - // }; - // } 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)); - // } - // _ => {} - // } - // } - // } - - // 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)); - // } - // _ => {} - // } - // return Ok(()); - // }; - - // match app.mode { - // Mode::Packet => app.mode = Mode::Alerts, - // Mode::Stats => app.mode = Mode::Packet, - // Mode::Alerts => app.mode = Mode::Firewall, - // Mode::Firewall => app.mode = Mode::Packet, - // }; - // } else { - // match &app.focused_block { - // FocusedBlock::Interface => { - // app.focused_block = FocusedBlock::Start; - // app.interface.state.select(None); - // } - - // FocusedBlock::TransportFilter => { - // app.focused_block = FocusedBlock::Interface; - // app.interface.state.select(Some(0)); - // 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(' ') => { - 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(); - } - } - } - 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 - } else { - i - } - } - 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)); - // } - - // FocusedBlock::Help => { - // app.help.scroll_down(); - // } - _ => {} - } - } - } - - 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 - } else { - 0 - } - } - 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)); - // } - FocusedBlock::Help => { - app.help.scroll_up(); - } - _ => {} - } - } - } - - _ => {} + FocusedBlock::UpdateFilterMenuBlock(&mut update_block) => { + update_block.handle_key_events(key_event, app) } + FocusedBlock::Main(&mut mode_block) => mode_block.handle_key_events(key_event, app), } - - Ok(()) + return Ok(()); } + +// /////////////////////// +// // old +// pub fn old_handle_key_events( +// key_event: KeyEvent, +// 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() { +// // 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(()); +// // } + +// // if fuzzy.is_paused() { +// // if app.manuall_scroll { +// // app.manuall_scroll = false; +// // } else { +// // fuzzy.disable(); +// // } +// // } else { +// // fuzzy.pause(); +// // } +// // } + +// _ => { +// 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(); +// // } +// // 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)); +// } 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(); +// // } +// // 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)); +// } 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; +// // } +// 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('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::Char('/') => { +// // if app.focused_block == FocusedBlock::Help || app.update_filters { +// // return Ok(()); +// // } +// // if app.start_sniffing { +// // fuzzy.enable(); +// // fuzzy.unpause(); +// // } +// // } +// // 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(()); +// // } + +// // app.show_packet_infos_popup = true; +// // } +// // KeyCode::Char('r') => { +// // if app.focused_block == FocusedBlock::Help || app.update_filters { +// // return Ok(()); +// // } +// // if key_event.modifiers == KeyModifiers::CONTROL { +// // app.detach_interfaces(); +// // sender.send(Event::Reset)?; +// // } +// // } + +// 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 +// .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))?; +// } +// } +// } + +// // 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.mode { +// // Mode::Packet => app.mode = Mode::Stats, +// // Mode::Stats => app.mode = Mode::Alerts, +// // Mode::Alerts => app.mode = Mode::Firewall, +// // Mode::Firewall => app.mode = Mode::Packet, +// // }; +// // } 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)); +// // } +// // _ => {} +// // } +// // } +// // } + +// // 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)); +// // } +// // _ => {} +// // } +// // return Ok(()); +// // }; + +// // match app.mode { +// // Mode::Packet => app.mode = Mode::Alerts, +// // Mode::Stats => app.mode = Mode::Packet, +// // Mode::Alerts => app.mode = Mode::Firewall, +// // Mode::Firewall => app.mode = Mode::Packet, +// // }; +// // } else { +// // match &app.focused_block { +// // FocusedBlock::Interface => { +// // app.focused_block = FocusedBlock::Start; +// // app.interface.state.select(None); +// // } + +// // FocusedBlock::TransportFilter => { +// // app.focused_block = FocusedBlock::Interface; +// // app.interface.state.select(Some(0)); +// // 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(' ') => { +// 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(); +// } +// } +// } +// 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 +// } else { +// i +// } +// } +// 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)); +// // } + +// // FocusedBlock::Help => { +// // app.help.scroll_down(); +// // } +// _ => {} +// } +// } +// } + +// 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 +// } else { +// 0 +// } +// } +// 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)); +// // } +// FocusedBlock::Help => { +// app.help.scroll_up(); +// } +// _ => {} +// } +// } +// } + +// _ => {} +// } +// } + +// Ok(()) +// } diff --git a/oryx-tui/src/interface.rs b/oryx-tui/src/interface.rs index 81d6e56..a61efc3 100644 --- a/oryx-tui/src/interface.rs +++ b/oryx-tui/src/interface.rs @@ -225,11 +225,13 @@ impl Interface { .title_style(Style::default().bold().fg(Color::Green)) .title_alignment(Alignment::Center) .borders(Borders::LEFT) - .border_type(if *focused_block == FocusedBlock::Interface { - BorderType::Thick - } else { - BorderType::default() - }) + .border_type( + if *focused_block == FocusedBlock::StartMenuBlock(Interface) { + BorderType::Thick + } else { + BorderType::default() + }, + ) .border_style(Style::default().fg(Color::Green)), area, ); diff --git a/oryx-tui/src/lib.rs b/oryx-tui/src/lib.rs index 605399c..414a63c 100644 --- a/oryx-tui/src/lib.rs +++ b/oryx-tui/src/lib.rs @@ -20,7 +20,9 @@ pub mod filters { pub mod fuzzy; pub mod link; pub mod network; + pub mod start_menu; pub mod transport; + pub mod update_menu; } pub mod notification; @@ -46,5 +48,3 @@ pub mod alerts { pub mod firewall; pub mod mode; - -pub mod startmenu; diff --git a/oryx-tui/src/mode.rs b/oryx-tui/src/mode.rs index 6ece9bc..edef0f1 100644 --- a/oryx-tui/src/mode.rs +++ b/oryx-tui/src/mode.rs @@ -33,66 +33,6 @@ impl Mode { Mode::Firewall => Mode::Alerts, }; } - pub fn render(&self, frame: &mut Frame, area: Rect, alert_span: Span<'_>) { - let header = match self { - Self::Packet => { - vec![ - Span::styled( - " Packet ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - Span::from(" Stats ").fg(Color::DarkGray), - alert_span, - Span::from(" Firewall ").fg(Color::DarkGray), - ] - } - - Self::Stats => { - vec![ - Span::from(" Packet ").fg(Color::DarkGray), - Span::styled( - " Stats ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - alert_span, - Span::from(" Firewall ").fg(Color::DarkGray), - ] - } - - Self::Alerts => { - vec![ - Span::from(" Packet ").fg(Color::DarkGray), - Span::from(" Stats ").fg(Color::DarkGray), - alert_span.bold(), - Span::from(" Firewall ").fg(Color::DarkGray), - ] - } - - Self::Firewall => { - vec![ - Span::from(" Packet ").fg(Color::DarkGray), - Span::from(" Stats ").fg(Color::DarkGray), - alert_span, - Span::styled( - " Firewall ", - Style::default().bg(Color::Green).fg(Color::White).bold(), - ), - ] - } - }; - - frame.render_widget( - Block::default() - .title(Line::from(header)) - .title_alignment(Alignment::Left) - .padding(Padding::top(2)) - .borders(Borders::ALL) - .style(Style::default()) - .border_type(BorderType::default()) - .border_style(Style::default().green()), - area, - ); - } pub fn handle_key_events(&mut self, key_event: KeyEvent, app: &mut App) { match key_event.code { @@ -196,7 +136,7 @@ impl Mode { } KeyCode::Char('f') => { app.update_filters = true; - app.focused_block = StartMenuBlock::TransportFilter; + app.focused_block = UpdateFilterMenuBLock::TransportFilter; app.focused_block.select(); app.filter.network.selected_protocols = @@ -235,4 +175,528 @@ impl Mode { } } } + + pub fn render(&self, frame: &mut Frame, area: Rect, &app: App) { + self.render_header(frame, area, app.alert.title_span(mode == Mode::Alerts)); + match self { + Mode::Packet => self.render_packets_mode(frame, mode_block, app), + Mode::Stats => self.render_stats_mode(frame, mode_block, app), + Mode::Alerts => app.alert.render(frame, mode_block), + Mode::Firewall => app.firewall.render(frame, mode_block), + } + } + + fn render_header(&self, frame: &mut Frame, area: Rect, alert_span: Span<'_>) { + let header = match self { + Self::Packet => { + vec![ + Span::styled( + " Packet ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ), + Span::from(" Stats ").fg(Color::DarkGray), + alert_span, + Span::from(" Firewall ").fg(Color::DarkGray), + ] + } + + Self::Stats => { + vec![ + Span::from(" Packet ").fg(Color::DarkGray), + Span::styled( + " Stats ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ), + alert_span, + Span::from(" Firewall ").fg(Color::DarkGray), + ] + } + + Self::Alerts => { + vec![ + Span::from(" Packet ").fg(Color::DarkGray), + Span::from(" Stats ").fg(Color::DarkGray), + alert_span.bold(), + Span::from(" Firewall ").fg(Color::DarkGray), + ] + } + + Self::Firewall => { + vec![ + Span::from(" Packet ").fg(Color::DarkGray), + Span::from(" Stats ").fg(Color::DarkGray), + alert_span, + Span::styled( + " Firewall ", + Style::default().bg(Color::Green).fg(Color::White).bold(), + ), + ] + } + }; + + frame.render_widget( + Block::default() + .title(Line::from(header)) + .title_alignment(Alignment::Left) + .padding(Padding::top(2)) + .borders(Borders::ALL) + .style(Style::default()) + .border_type(BorderType::default()) + .border_style(Style::default().green()), + area, + ); + } + fn render_stats_mode(self, frame: &mut Frame, block: Rect, &app: App) { + let stats = app.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]) + }; + + stats.render(frame, stats_block); + + app.bandwidth.render( + frame, + bandwidth_block, + &app.interface.selected_interface.name.clone(), + ); + } + + fn render_packets_mode(self, frame: &mut Frame, area: Rect, app: &mut App) { + let app_packets = app.packets.lock().unwrap(); + let mut fuzzy = app.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(area); + (chunks[0], chunks[1]) + } else { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Fill(1), Constraint::Length(1)]) + .horizontal_margin(1) + .split(area); + (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 = area.height.saturating_sub(5) as usize; + app.packet_window_size = window_size; + + // This points always to the end of the window + if app.packet_end_index < window_size { + app.packet_end_index = window_size; + } + + if fuzzy.packet_end_index < window_size { + fuzzy.packet_end_index = window_size; + } + + let packets_to_display = match app.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() { + app.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() { + app.packet_index = Some(selected_index); + } else { + app.packet_index = None; + } + &fuzzy_packets + } + } else if app_packets.len() > window_size { + if let Some(selected_index) = app.packets_table_state.selected() { + app.packet_index = + Some(app.packet_end_index.saturating_sub(window_size) + selected_index); + } + &app_packets + [app.packet_end_index.saturating_sub(window_size)..app.packet_end_index] + } else { + if let Some(selected_index) = app.packets_table_state.selected() { + app.packet_index = Some(selected_index); + } + &app_packets + } + } + false => { + if fuzzy.is_enabled() & !fuzzy.filter.value().is_empty() { + if fuzzy_packets.len() > window_size { + app.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); + &fuzzy_packets[fuzzy_packets.len().saturating_sub(window_size)..] + } else { + app.packet_index = Some(fuzzy_packets.len().saturating_sub(1)); + &fuzzy_packets + } + } else if app_packets.len() > window_size { + app.packet_index = Some(app_packets.len().saturating_sub(1)); + &app_packets[app_packets.len().saturating_sub(window_size)..] + } else { + app.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 !app.manuall_scroll { + if fuzzy.is_enabled() { + fuzzy.scroll_state.select(Some(packets_to_display.len())); + } else { + app.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 app.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::new().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 app.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 app.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 app.manuall_scroll { + if app.packet_end_index == window_size { + 0 + } else { + app.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); + } + // show packet info popup if needed + if app.show_packet_infos_popup { + self.render_packet_infos_popup(frame, app); + } + } + + fn render_packet_infos_popup(self, frame: &mut Frame, &app: App) { + 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 = app.fuzzy.lock().unwrap(); + let packets = app.packets.lock().unwrap(); + + let packet = if fuzzy.is_enabled() { + fuzzy.packets[app.packet_index.unwrap()] + } else { + packets[app.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), + }; + } }