From d537b362a906fa7737b7bb5a2b032795e3c6d570 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Fri, 28 Oct 2022 18:09:24 +0200 Subject: [PATCH] added footer, added bordered containers, fixed output report write after reinitialization --- LICENSE-MIT | 2 +- src/app.rs | 18 ++++++++++ src/gui_initial_page.rs | 48 +++++++++++++++++++------ src/gui_run_page.rs | 34 +++++++++++++++--- src/main.rs | 12 ++++++- src/style.rs | 71 ++++++++++++++++++++++++++++++------- src/thread_parse_packets.rs | 14 +++++--- src/thread_write_report.rs | 14 ++++---- 8 files changed, 170 insertions(+), 43 deletions(-) diff --git a/LICENSE-MIT b/LICENSE-MIT index e6078713..c33cc3e1 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 - Bellini Giuliano, Canepari Cristiano Marco +Copyright (c) 2022 - Bellini Giuliano Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/app.rs b/src/app.rs index e1337ec5..f46e7f65 100644 --- a/src/app.rs +++ b/src/app.rs @@ -21,6 +21,7 @@ pub enum Message { TransportProtocolSelection(TransProtocol), AppProtocolSelection(AppProtocol), OpenReport, + OpenGithub, Start, Reset, Style @@ -78,6 +79,23 @@ fn update(&mut self, message: Message) -> Command { .spawn( ) .unwrap( ); } + Message::OpenGithub => { + #[cfg(target_os = "windows")] + std::process::Command::new( "explorer" ) + .arg( "./sniffnet_report/report.txt" ) + .spawn( ) + .unwrap( ); + #[cfg(target_os = "macos")] + std::process::Command::new( "open" ) + .arg( "https://github.com/GyulyVGC/sniffnet" ) + .spawn( ) + .unwrap( ); + #[cfg(target_os = "linux")] + std::process::Command::new( "explorer" ) + .arg( "./sniffnet_report/report.txt" ) + .spawn( ) + .unwrap( ); + } Message::Start => { let current_capture_id = self.current_capture_id.clone(); let device = self.device.clone(); diff --git a/src/gui_initial_page.rs b/src/gui_initial_page.rs index 3ab37021..9662e048 100644 --- a/src/gui_initial_page.rs +++ b/src/gui_initial_page.rs @@ -1,8 +1,11 @@ use iced::{alignment, Alignment, Button, Column, Container, Element, Length, PickList, Radio, Row, Scrollable, Svg, Text}; -use iced::alignment::Horizontal; +use iced::alignment::{Horizontal, Vertical}; +use iced::Font::Default; +use iced::Length::FillPortion; use pcap::Device; use crate::app::Message; -use crate::{AppProtocol, FONT_SIZE_SUBTITLE, FONT_SIZE_TITLE, icon_sun_moon, Sniffer, TransProtocol}; +use crate::{AppProtocol, FONT_SIZE_SUBTITLE, FONT_SIZE_TITLE, icon_sun_moon, Mode, Sniffer, TransProtocol}; +use crate::style::{COURIER_PRIME_ITALIC, FONT_SIZE_FOOTER, HEIGHT_BODY, HEIGHT_FOOTER, HEIGHT_HEADER, icon}; pub fn initial_page(sniffer: &mut Sniffer) -> Column { @@ -20,7 +23,7 @@ pub fn initial_page(sniffer: &mut Sniffer) -> Column { .on_press(Message::Style); let header = Row::new() - .height(Length::FillPortion(3)) + .height(Length::FillPortion(HEIGHT_HEADER)) .width(Length::Fill) .align_items(Alignment::Center) .push(Container::new(Row::new()).width(Length::FillPortion(1)).align_x(Horizontal::Center)) @@ -29,7 +32,7 @@ pub fn initial_page(sniffer: &mut Sniffer) -> Column { let button_start = Button::new( &mut sniffer.start, - Text::new("Run!").vertical_alignment(alignment::Vertical::Center).horizontal_alignment(alignment::Horizontal::Center), + Text::new("Run!").size(FONT_SIZE_TITLE).vertical_alignment(alignment::Vertical::Center).horizontal_alignment(alignment::Horizontal::Center), ) .padding(10) .height(Length::Units(80)) @@ -70,20 +73,20 @@ pub fn initial_page(sniffer: &mut Sniffer) -> Column { } let col_adapter = Column::new() - .padding(20) - .spacing(10) + .padding(10) + .spacing(5) .height(Length::Fill) .width(Length::FillPortion(4)) .push(Text::new("Select network adapter to inspect").size(FONT_SIZE_TITLE)) .push(dev_str_list.iter().fold( - Scrollable::new(&mut sniffer.scroll_adapters).style(sniffer.style).padding(10).spacing(20).height(Length::FillPortion(8)), + Scrollable::new(&mut sniffer.scroll_adapters).style(sniffer.style).padding(13).spacing(5), |scroll_adapters, adapter| { - scroll_adapters.push(Radio::new( + scroll_adapters.push(Container::new(Radio::new( &adapter.0, &adapter.1, Some(&sniffer.device.clone().lock().unwrap().name), |name| Message::AdapterSelection(name.to_string()), - ).size(15).style(sniffer.style)) + ).size(15).width(Length::Fill).style(sniffer.style)).padding(10).style(Mode::Bordered)) }, )); @@ -164,17 +167,40 @@ pub fn initial_page(sniffer: &mut Sniffer) -> Column { .push(iced::Text::new("Application protocol").size(FONT_SIZE_SUBTITLE)) .push(picklist_app); - let filters = Column::new().width(Length::FillPortion(6)).padding(20).spacing(20) + let filters = Column::new().width(Length::FillPortion(6)).padding(10).spacing(15) .push(Row::new().push(Text::new("Select filters to be applied on network traffic").size(FONT_SIZE_TITLE))) .push(Row::new().height(Length::FillPortion(3)).push(col_ip).push(col_transport).push(col_app)); - let body = Row::new().height(Length::FillPortion(9)) + let body = Row::new().height(Length::FillPortion(HEIGHT_BODY)) .push(col_adapter) .push(col_space) .push(filters); + let button_github = Button::new( + &mut sniffer.git, + icon('\u{f09b}').size(30) + .horizontal_alignment(alignment::Horizontal::Center) + .vertical_alignment(alignment::Vertical::Center) + ) + .height(Length::Units(35)) + .width(Length::Units(35)) + .style(sniffer.style) + .on_press(Message::OpenGithub); + let footer_row = Row::new() + .align_items(Alignment::Center) + .push(Text::new("Sniffnet v1.0.0 - by Giuliano Bellini ").size(FONT_SIZE_FOOTER).font(COURIER_PRIME_ITALIC)) + .push(button_github) + .push(Text::new(" ")); + let footer = Container::new(footer_row) + .width(Length::Fill) + .height(FillPortion(HEIGHT_FOOTER)) + .align_y(Vertical::Center) + .align_x(Horizontal::Center) + .style(Mode::Bordered); + Column::new() .push(header) .push(body) + .push(footer) } \ No newline at end of file diff --git a/src/gui_run_page.rs b/src/gui_run_page.rs index 1bd2cf73..a1f952ba 100644 --- a/src/gui_run_page.rs +++ b/src/gui_run_page.rs @@ -1,12 +1,13 @@ use std::cmp::min; use std::fmt::format; use iced::{alignment, Alignment, Button, Column, Container, Length, Renderer, Row, Scrollable, Svg, Text}; -use iced::alignment::Horizontal; +use iced::alignment::{Horizontal, Vertical}; +use iced::Length::FillPortion; use crate::app::Message; -use crate::{FONT_SIZE_TITLE, get_app_count_string, icon_sun_moon, Sniffer}; +use crate::{FONT_SIZE_TITLE, get_app_count_string, icon_sun_moon, Mode, Sniffer}; use crate::address_port_pair::AddressPortPair; use crate::info_address_port_pair::InfoAddressPortPair; -use crate::style::icon; +use crate::style::{COURIER_PRIME_ITALIC, FONT_SIZE_FOOTER, HEIGHT_BODY, HEIGHT_FOOTER, HEIGHT_HEADER, icon}; pub fn run_page(sniffer: &mut Sniffer) -> Column { @@ -36,7 +37,7 @@ pub fn run_page(sniffer: &mut Sniffer) -> Column { .on_press(Message::Reset); let header = Row::new() - .height(Length::FillPortion(3)) + .height(Length::FillPortion(HEIGHT_HEADER)) .width(Length::Fill) .align_items(Alignment::Center) .push(Container::new(button_reset).width(Length::FillPortion(1)).align_x(Horizontal::Center)) @@ -96,14 +97,37 @@ pub fn run_page(sniffer: &mut Sniffer) -> Column { .push(col_open_report); } - let body = Column::new().height(Length::FillPortion(9)) + let body = Column::new().height(Length::FillPortion(HEIGHT_BODY)) .align_items(Alignment::Center) .spacing(10) .push(col_packets) .push(row_report); + let button_github = Button::new( + &mut sniffer.git, + icon('\u{f09b}').size(30) + .horizontal_alignment(alignment::Horizontal::Center) + .vertical_alignment(alignment::Vertical::Center) + ) + .height(Length::Units(35)) + .width(Length::Units(35)) + .style(sniffer.style) + .on_press(Message::OpenGithub); + let footer_row = Row::new() + .align_items(Alignment::Center) + .push(Text::new("Sniffnet v1.0.0 - by Giuliano Bellini ").size(FONT_SIZE_FOOTER).font(COURIER_PRIME_ITALIC)) + .push(button_github) + .push(Text::new(" ")); + let footer = Container::new(footer_row) + .width(Length::Fill) + .height(FillPortion(HEIGHT_FOOTER)) + .align_y(Vertical::Center) + .align_x(Horizontal::Center) + .style(Mode::Bordered); + Column::new() .push(header) .push(body) + .push(footer) } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9a33144b..2c719364 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,7 @@ use crate::thread_parse_packets::parse_packets_loop; use crate::thread_write_report::sleep_and_write_report_loop; use crate::thread_write_report::get_app_count_string; -use std::{thread}; +use std::{panic, process, thread}; use std::sync::{Arc, Mutex, Condvar}; use iced::{Application, button, pick_list, scrollable, Settings, window}; use crate::info_traffic::InfoTraffic; @@ -37,6 +37,7 @@ pub struct Sniffer { reset: button::State, mode: button::State, report: button::State, + git: button::State, app: pick_list::State, scroll_adapters: scrollable::State, scroll_run: scrollable::State, @@ -84,6 +85,14 @@ pub fn main() -> iced::Result { })); let filters2 = filters1.clone(); + // to kill the main thread as soon as a secondary thread panics + let orig_hook = panic::take_hook(); + panic::set_hook(Box::new(move |panic_info| { + // invoke the default handler and exit the process + orig_hook(panic_info); + process::exit(1); + })); + thread::spawn(move || { sleep_and_write_report_loop(current_capture_id2, 0, 65535, 1, found_device2, filters2, "./sniffnet_report".to_string(), @@ -113,6 +122,7 @@ pub fn main() -> iced::Result { reset: button::State::new(), mode: button::State::new(), report: button::State::new(), + git: button::State::new(), app: pick_list::State::new(), scroll_adapters: scrollable::State::new(), scroll_run: scrollable::State::new(), diff --git a/src/style.rs b/src/style.rs index 5a549f29..a4bac1a0 100644 --- a/src/style.rs +++ b/src/style.rs @@ -19,15 +19,23 @@ bytes: include_bytes!("../fonts/CourierPrimeSansItalic.ttf"), }; +pub const FONT_SIZE_FOOTER: u16 = 14; pub const FONT_SIZE_BODY: u16 = 16; pub const FONT_SIZE_SUBTITLE: u16 = 18; pub const FONT_SIZE_TITLE: u16 = 22; +pub const BORDER_WIDTH: f32 = 1.5; + +pub const HEIGHT_HEADER: u16 = 3; +pub const HEIGHT_BODY: u16 = 12; +pub const HEIGHT_FOOTER: u16 = 1; + #[derive(Copy, Eq, PartialEq)] pub enum Mode { Night, - Day + Day, + Bordered } @@ -45,14 +53,24 @@ fn style(&self) -> Style { text_color: match self { Mode::Day => Some(Color::BLACK), Mode::Night => Some(Color::WHITE), + Mode::Bordered => {None} }, background: match self { Mode::Day => {Some(Background::Color(Color{r: 0.8, g: 0.8, b: 0.8, a: 1.0,}))} Mode::Night => {Some(Background::Color(Color{r: 0.2, g: 0.2, b: 0.2, a: 1.0,}))} + Mode::Bordered => {None} }, - border_radius: 0.0, - border_width: 0.0, - border_color: Default::default() + border_radius: match self { + Mode::Night => {0.0} + Mode::Day => {0.0} + Mode::Bordered => {12.0} + }, + border_width: match self { + Mode::Night => {0.0} + Mode::Day => {0.0} + Mode::Bordered => {BORDER_WIDTH} + }, + border_color: Color::BLACK } } @@ -66,23 +84,28 @@ fn menu(&self) -> iced_style::menu::Style { text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} }, background: Background::Color(match self { Mode::Day => Color{r: 0.9, g: 0.9, b: 0.9, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} }), - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, selected_text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} }, selected_background: Background::Color(match self { Mode::Day => Color{r: 0.8, g: 0.8, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.2, g: 0.2, b: 0.2, a: 1.0,}, + _ => {Color::BLACK} }) } } @@ -92,17 +115,20 @@ fn active(&self) -> pick_list::Style { text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} }, placeholder_color: Color::BLACK, background: Background::Color(match self { Mode::Day => Color{r: 0.9, g: 0.9, b: 0.9, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} }), border_radius: 0.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, icon_size: 0.5 } @@ -113,17 +139,20 @@ fn hovered(&self) -> pick_list::Style { text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} }, placeholder_color: Color::BLACK, background: Background::Color(match self { Mode::Day => Color{r: 0.8, g: 0.8, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.2, g: 0.2, b: 0.2, a: 1.0,}, + _ => {Color::BLACK} }), border_radius: 0.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.5, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.5, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, icon_size: 0.5 } @@ -139,16 +168,19 @@ fn hovered(&self) -> iced_style::button::Style { background: Some(Background::Color(match self { Mode::Day => Color{r: 0.8, g: 0.8, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.2, g: 0.2, b: 0.2, a: 1.0,}, + _ => {Color::BLACK} })), border_radius: 12.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.5, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.5, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} } } } @@ -158,17 +190,20 @@ fn active(&self) -> button::Style { background: Some(Background::Color(match self { Mode::Day => Color{r: 0.9, g: 0.9, b: 0.9, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} })), border_radius: 12.0, - border_width: 2.0, + border_width: BORDER_WIDTH, shadow_offset: Vector::new(0.0, 0.0), text_color: match self { Mode::Day => Color::BLACK, Mode::Night => Color::WHITE, + _ => {Color::BLACK} }, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} } } } @@ -180,10 +215,12 @@ fn active(&self) -> iced_style::radio::Style { background: Background::Color(match self { Mode::Day => Color{r: 1.0, g: 1.0, b: 1.0, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} }), dot_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, border_width: 0.0, border_color: Default::default(), @@ -196,15 +233,18 @@ fn hovered(&self) -> iced_style::radio::Style { background: Background::Color(match self { Mode::Day => Color{r: 1.0, g: 1.0, b: 1.0, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} }), dot_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, text_color: None } @@ -220,6 +260,7 @@ fn active(&self) -> Scrollbar { background: Some(Background::Color(match self { Mode::Day => Color{r: 0.9, g: 0.9, b: 0.9, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} })), border_radius: 12.0, border_width: 0.0, @@ -228,9 +269,10 @@ fn active(&self) -> Scrollbar { color: match self { Mode::Day => Color{r: 0.8, g: 0.8, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.2, g: 0.2, b: 0.2, a: 1.0,}, + _ => {Color::BLACK} }, border_radius: 12.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: Color::BLACK } } @@ -241,17 +283,19 @@ fn hovered(&self) -> Scrollbar { background: Some(Background::Color(match self { Mode::Day => Color{r: 0.9, g: 0.9, b: 0.9, a: 1.0,}, Mode::Night => Color{r: 0.1, g: 0.1, b: 0.1, a: 1.0,}, + _ => {Color::BLACK} })), border_radius: 12.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: Color::BLACK, scroller: Scroller { color: match self { Mode::Day => Color{r: 0.0, g: 0.5, b: 0.8, a: 1.0,}, Mode::Night => Color{r: 0.0, g: 0.8, b: 0.5, a: 1.0,}, + _ => {Color::BLACK} }, border_radius: 12.0, - border_width: 2.0, + border_width: BORDER_WIDTH, border_color: Color::BLACK } } @@ -285,5 +329,6 @@ pub fn icon_sun_moon(style: Mode) -> Text { .horizontal_alignment(alignment::Horizontal::Center) .size(20) } + _ => {Text::new("")} } } \ No newline at end of file diff --git a/src/thread_parse_packets.rs b/src/thread_parse_packets.rs index 58ba960f..b2da84d1 100644 --- a/src/thread_parse_packets.rs +++ b/src/thread_parse_packets.rs @@ -5,6 +5,7 @@ use std::sync::{Arc, Condvar, Mutex}; use chrono::{Local}; use etherparse::{IpHeader, PacketHeaders, TransportHeader}; +use iced::Command; use pcap::{Capture, Device}; use crate::{address_port_pair::AddressPortPair, AppProtocol, Filters, info_address_port_pair::InfoAddressPortPair, InfoTraffic, Status, TransProtocol}; use crate::address_port_pair::TrafficType; @@ -62,13 +63,16 @@ pub fn parse_packets_loop(current_capture_id: Arc>, device: Arc>, lowest_p // sleep interval seconds thread::sleep(Duration::from_secs(interval)); + if *current_capture_id.lock().unwrap() != capture_id { + output = BufWriter::new(File::create(path_report.clone()).expect("Error creating output file\n\r")); + writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); + writeln!(output, "| Src IP address | Src port | Dst IP address | Dst port | Layer 4 | Layer 7 | Packets | Bytes | Initial timestamp | Final timestamp |").expect("Error writing output file\n\r"); + writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); + } + let tot_seconds = (Local::now() - time_origin).num_seconds(); if *status_pair.0.lock().expect("Error acquiring mutex\n\r") == Status::Running { @@ -120,13 +127,6 @@ pub fn sleep_and_write_report_loop(current_capture_id: Arc>, lowest_p let tot_sent_bytes = info_traffic.tot_sent_bytes; let tot_received_bytes = info_traffic.tot_received_bytes; - if *current_capture_id.lock().unwrap() != capture_id { - output = BufWriter::new(File::create(path_report.clone()).expect("Error creating output file\n\r")); - writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); - writeln!(output, "| Src IP address | Src port | Dst IP address | Dst port | Layer 4 | Layer 7 | Packets | Bytes | Initial timestamp | Final timestamp |").expect("Error writing output file\n\r"); - writeln!(output, "---------------------------------------------------------------------------------------------------------------------------------------------------------------------").expect("Error writing output file\n\r"); - } - start = Instant::now(); let mut output2 = BufWriter::new(File::create(path_statistics.clone()).expect("Error creating output file\n\r"));