From ab16ea1bca26cd7345c436c38b4778c72ebce491 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Tue, 19 Dec 2023 16:00:39 +0100 Subject: [PATCH 1/7] ICMP support initial implementation (WIP) --- src/gui/pages/connection_details_page.rs | 2 +- src/networking/manage_packets.rs | 40 +++++++++++++------ src/networking/types/address_port_pair.rs | 36 ++++++++++++----- src/networking/types/packet_filters_fields.rs | 8 ++-- src/networking/types/port_collection.rs | 11 +++-- src/networking/types/protocol.rs | 4 +- src/utils/formatted_strings.rs | 16 +++++--- 7 files changed, 80 insertions(+), 37 deletions(-) diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 48581fe18..9041f2ea1 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -294,7 +294,7 @@ fn get_local_tooltip( fn get_src_or_dest_col( caption: Row<'static, Message, Renderer>, ip: &String, - port: u16, + port: Option, mac: &str, font: Font, language: Language, diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index f3867c8bc..68518e02c 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -114,33 +114,49 @@ fn analyze_network_header( /// Returns false if packet has to be skipped. fn analyze_transport_header( transport_header: Option, - port1: &mut u16, - port2: &mut u16, + port1: &mut Option, + port2: &mut Option, protocol: &mut Protocol, ) -> bool { match transport_header { Some(TransportHeader::Udp(udp_header)) => { - *port1 = udp_header.source_port; - *port2 = udp_header.destination_port; + *port1 = Some(udp_header.source_port); + *port2 = Some(udp_header.destination_port); *protocol = Protocol::UDP; true } Some(TransportHeader::Tcp(tcp_header)) => { - *port1 = tcp_header.source_port; - *port2 = tcp_header.destination_port; + *port1 = Some(tcp_header.source_port); + *port2 = Some(tcp_header.destination_port); *protocol = Protocol::TCP; true } + Some(TransportHeader::Icmpv4(icmpv4_header)) => { + *port1 = None; + *port2 = None; + *protocol = Protocol::ICMP; + true + } + Some(TransportHeader::Icmpv6(icmpv6_header)) => { + *port1 = None; + *port2 = None; + *protocol = Protocol::ICMP; + true + } _ => false, } } -pub fn get_app_protocol(src_port: u16, dst_port: u16) -> AppProtocol { - let mut application_protocol = from_port_to_application_protocol(src_port); - if (application_protocol).eq(&AppProtocol::Other) { - application_protocol = from_port_to_application_protocol(dst_port); - } - application_protocol +pub fn get_app_protocol(src_port: Option, dst_port: Option) -> AppProtocol { + return if src_port.is_some() && dst_port.is_some() { + let mut application_protocol = from_port_to_application_protocol(src_port.unwrap()); + if (application_protocol).eq(&AppProtocol::Other) { + application_protocol = from_port_to_application_protocol(dst_port.unwrap()); + } + application_protocol + } else { + AppProtocol::Other + }; } /// Function to insert the source and destination of a packet into the shared map containing the analyzed traffic. diff --git a/src/networking/types/address_port_pair.rs b/src/networking/types/address_port_pair.rs index fec42ac00..47fabc1eb 100644 --- a/src/networking/types/address_port_pair.rs +++ b/src/networking/types/address_port_pair.rs @@ -10,11 +10,11 @@ pub struct AddressPortPair { /// Network layer IPv4 or IPv6 source address. pub address1: String, /// Transport layer source port number (in the range 0..=65535). - pub port1: u16, + pub port1: Option, /// Network layer IPv4 or IPv6 destination address. pub address2: String, /// Transport layer destination port number (in the range 0..=65535). - pub port2: u16, + pub port2: Option, /// Transport layer protocol carried through the associate address:port pair (TCP or UPD). pub protocol: Protocol, } @@ -29,34 +29,50 @@ impl AddressPortPair { /// * `port` - An integer representing the transport layer port number (in the range 0..=65535). pub fn new( address1: String, - port1: u16, + port1: Option, address2: String, - port2: u16, - trans_protocol: Protocol, + port2: Option, + protocol: Protocol, ) -> Self { AddressPortPair { address1, port1, address2, port2, - protocol: trans_protocol, + protocol, } } } impl fmt::Display for AddressPortPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (port_1_str, port_2_str) = if self.port1.is_some() && self.port2.is_some() { + ( + self.port1.unwrap().to_string(), + self.port2.unwrap().to_string(), + ) + } else { + ("-".to_string(), "-".to_string()) + }; if self.address1.len() > 25 || self.address2.len() > 25 { write!( f, - "{:^45}{:>8} {:^45}{:>8} {} ", - self.address1, self.port1, self.address2, self.port2, self.protocol + "{:^45}{:>8} {:^45}{:>8} {:>4} ", + self.address1, + port_1_str, + self.address2, + port_2_str, + self.protocol.to_string() ) } else { write!( f, - "{:^25}{:>8} {:^25}{:>8} {} ", - self.address1, self.port1, self.address2, self.port2, self.protocol + "{:^25}{:>8} {:^25}{:>8} {:>4} ", + self.address1, + port_1_str, + self.address2, + port_2_str, + self.protocol.to_string() ) } } diff --git a/src/networking/types/packet_filters_fields.rs b/src/networking/types/packet_filters_fields.rs index cba9c55c5..268989f9e 100644 --- a/src/networking/types/packet_filters_fields.rs +++ b/src/networking/types/packet_filters_fields.rs @@ -15,9 +15,9 @@ pub struct PacketFiltersFields { /// Destination IP address pub dest: IpAddr, /// Source port - pub sport: u16, + pub sport: Option, /// Destination port - pub dport: u16, + pub dport: Option, } impl Default for PacketFiltersFields { @@ -27,8 +27,8 @@ impl Default for PacketFiltersFields { protocol: Protocol::TCP, source: IpAddr::from_str("::").unwrap(), dest: IpAddr::from_str("::").unwrap(), - sport: 0, - dport: 0, + sport: None, + dport: None, } } } diff --git a/src/networking/types/port_collection.rs b/src/networking/types/port_collection.rs index 71ebb0452..9a186e818 100644 --- a/src/networking/types/port_collection.rs +++ b/src/networking/types/port_collection.rs @@ -56,13 +56,18 @@ impl PortCollection { Some(Self { ports, ranges }) } - pub(crate) fn contains(&self, port: u16) -> bool { + pub(crate) fn contains(&self, port: Option) -> bool { + // ignore port filter in case of ICMP + if port.is_none() { + return true; + } + for range in &self.ranges { - if range.contains(&port) { + if range.contains(&port.unwrap()) { return true; } } - self.ports.contains(&port) + self.ports.contains(&port.unwrap()) } } diff --git a/src/networking/types/protocol.rs b/src/networking/types/protocol.rs index 322c0c063..b144b1f7f 100644 --- a/src/networking/types/protocol.rs +++ b/src/networking/types/protocol.rs @@ -8,6 +8,8 @@ pub enum Protocol { TCP, /// User Datagram Protocol UDP, + /// Internet Control Message Protocol + ICMP, } impl fmt::Display for Protocol { @@ -17,5 +19,5 @@ impl fmt::Display for Protocol { } impl Protocol { - pub(crate) const ALL: [Protocol; 2] = [Protocol::TCP, Protocol::UDP]; + pub(crate) const ALL: [Protocol; 3] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP]; } diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index d061929cd..b227634e6 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -199,12 +199,16 @@ pub fn get_domain_from_r_dns(r_dns: String) -> String { } } -pub fn get_socket_address(address: &String, port: u16) -> String { - if address.contains(':') { - // IPv6 - format!("[{address}]:{port}") +pub fn get_socket_address(address: &String, port: Option) -> String { + if let Some(res) = port { + if address.contains(':') { + // IPv6 + format!("[{address}]:{res}") + } else { + // IPv4 + format!("{address}:{res}") + } } else { - //IPv4 - format!("{address}:{port}") + format!("{address}") } } From d3104be1fcd79b939a4c3d10dd3b9c30ff6e16fc Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Tue, 19 Dec 2023 19:02:10 +0100 Subject: [PATCH 2/7] refining ICMP support (WIP)... --- src/countries/types/country.rs | 2 +- src/gui/pages/connection_details_page.rs | 38 ++++++++++++++----- src/gui/pages/overview_page.rs | 4 +- src/networking/manage_packets.rs | 4 +- src/networking/types/app_protocol.rs | 2 +- .../types/info_address_port_pair.rs | 9 ++--- src/report/get_report_entries.rs | 2 +- src/secondary_threads/check_updates.rs | 4 +- src/utils/formatted_strings.rs | 2 +- 9 files changed, 42 insertions(+), 25 deletions(-) diff --git a/src/countries/types/country.rs b/src/countries/types/country.rs index 27eaf330e..4a4e0a0ec 100644 --- a/src/countries/types/country.rs +++ b/src/countries/types/country.rs @@ -264,7 +264,7 @@ impl Default for Country { impl fmt::Display for Country { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if self.eq(&Country::ZZ) { - write!(f, "") + write!(f, "?") } else { write!(f, "{self:?}") } diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 9041f2ea1..4cd386fbe 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -22,8 +22,9 @@ use crate::networking::types::host::Host; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::networking::types::traffic_direction::TrafficDirection; use crate::translations::translations::{ - application_protocol_translation, incoming_translation, outgoing_translation, - packets_translation, transport_protocol_translation, + address_translation, application_protocol_translation, incoming_translation, + outgoing_translation, packets_translation, protocol_translation, + transport_protocol_translation, }; use crate::translations::translations_2::{ administrative_entity_translation, connection_details_translation, destination_translation, @@ -186,7 +187,14 @@ fn col_info( font: Font, language: Language, ) -> Column<'static, Message, Renderer> { - Column::new() + let is_icmp = key.port1.is_none() || key.port2.is_none(); + let protocol_caption = if is_icmp { + protocol_translation(language) + } else { + transport_protocol_translation(language) + }; + + let mut ret_val = Column::new() .spacing(10) .padding([0, 0, 0, 40]) .width(Length::FillPortion(2)) @@ -202,15 +210,20 @@ fn col_info( ), ) .push(TextType::highlighted_subtitle_with_desc( - transport_protocol_translation(language), + protocol_caption, &key.protocol.to_string(), font, - )) - .push(TextType::highlighted_subtitle_with_desc( + )); + + if !is_icmp { + ret_val = ret_val.push(TextType::highlighted_subtitle_with_desc( application_protocol_translation(language), &val.app_protocol.to_string(), font, - )) + )); + } + + ret_val = ret_val .push(TextType::highlighted_subtitle_with_desc( &format!( "{} ({})", @@ -229,7 +242,9 @@ fn col_info( ), font, )) - .push(vertical_space(Length::FillPortion(1))) + .push(vertical_space(Length::FillPortion(1))); + + ret_val } fn get_host_info_col( @@ -300,6 +315,11 @@ fn get_src_or_dest_col( language: Language, timing_events: &TimingEvents, ) -> Column<'static, Message, Renderer> { + let address_caption = if port.is_some() { + socket_address_translation(language) + } else { + address_translation(language) + }; Column::new() .spacing(4) .push( @@ -313,7 +333,7 @@ fn get_src_or_dest_col( .spacing(10) .align_items(Alignment::End) .push(TextType::highlighted_subtitle_with_desc( - socket_address_translation(language), + address_caption, &get_socket_address(ip, port), font, )) diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs index 6456f4656..de7ae3bec 100644 --- a/src/gui/pages/overview_page.rs +++ b/src/gui/pages/overview_page.rs @@ -375,7 +375,7 @@ fn col_app(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer Column<'static, Message, Renderer, dst_port: Option) -> AppProtocol { - return if src_port.is_some() && dst_port.is_some() { + if src_port.is_some() && dst_port.is_some() { let mut application_protocol = from_port_to_application_protocol(src_port.unwrap()); if (application_protocol).eq(&AppProtocol::Other) { application_protocol = from_port_to_application_protocol(dst_port.unwrap()); @@ -156,7 +156,7 @@ pub fn get_app_protocol(src_port: Option, dst_port: Option) -> AppProt application_protocol } else { AppProtocol::Other - }; + } } /// Function to insert the source and destination of a packet into the shared map containing the analyzed traffic. diff --git a/src/networking/types/app_protocol.rs b/src/networking/types/app_protocol.rs index 46ab5e4bf..f15d57b9a 100644 --- a/src/networking/types/app_protocol.rs +++ b/src/networking/types/app_protocol.rs @@ -111,7 +111,7 @@ pub fn from_port_to_application_protocol(port: u16) -> AppProtocol { impl fmt::Display for AppProtocol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.eq(&AppProtocol::Other) { - write!(f, "-") + write!(f, "?") } else { write!(f, "{self:?}") } diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index ef7ad120b..b91f308ea 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -51,15 +51,12 @@ impl fmt::Display for InfoAddressPortPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bytes_string = get_formatted_bytes_string(self.transmitted_bytes); - let app_string = match self.app_protocol { - AppProtocol::Other => "Other".to_string(), - _ => self.app_protocol.to_string(), - }; - write!( f, "{:^9}{:>10} {:>9} ", - app_string, self.transmitted_packets, bytes_string, + self.app_protocol.to_string(), + self.transmitted_packets, + bytes_string, ) } } diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs index a7713ad2b..4c8be9e6c 100644 --- a/src/report/get_report_entries.rs +++ b/src/report/get_report_entries.rs @@ -43,7 +43,7 @@ pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec, usize) { } // check application protocol filter let searched_app = &*sniffer.search.app.to_lowercase(); - let app = format!("{:?}", value.app_protocol).to_lowercase(); + let app = value.app_protocol.to_string().to_lowercase(); if !searched_app.is_empty() && app.ne(searched_app) { return false; } diff --git a/src/secondary_threads/check_updates.rs b/src/secondary_threads/check_updates.rs index 96ccf033e..819552986 100644 --- a/src/secondary_threads/check_updates.rs +++ b/src/secondary_threads/check_updates.rs @@ -23,7 +23,7 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O let client = reqwest::blocking::Client::new(); let response = client .get("https://api.github.com/repos/GyulyVGC/sniffnet/releases/latest") - .header("User-agent", format!("sniffnet-{}", APP_VERSION)) + .header("User-agent", format!("sniffnet-{APP_VERSION}")) .header("Accept", "application/vnd.github+json") .header("X-GitHub-Api-Version", "2022-11-28") .send(); @@ -35,7 +35,7 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O if result_json.is_err() { let response2 = client .get("https://api.github.com/repos/GyulyVGC/sniffnet/releases/latest") - .header("User-agent", format!("sniffnet-{}", APP_VERSION)) + .header("User-agent", format!("sniffnet-{APP_VERSION}")) .header("Accept", "application/vnd.github+json") .header("X-GitHub-Api-Version", "2022-11-28") .send(); diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index b227634e6..4e3aa6ea3 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -209,6 +209,6 @@ pub fn get_socket_address(address: &String, port: Option) -> String { format!("{address}:{res}") } } else { - format!("{address}") + address.to_owned() } } From 21662dd227d6cb5e14e4951d5163a372bf7f0c25 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Wed, 20 Dec 2023 16:34:09 +0100 Subject: [PATCH 3/7] added ICMP message types --- Cargo.lock | 46 +-- src/gui/components/button.rs | 9 +- src/gui/pages/connection_details_page.rs | 70 ++-- src/networking/manage_packets.rs | 19 ++ src/networking/types/icmp_type.rs | 310 ++++++++++++++++++ .../types/info_address_port_pair.rs | 5 + src/networking/types/mod.rs | 1 + src/secondary_threads/parse_packets.rs | 4 + src/translations/translations_3.rs | 8 + 9 files changed, 417 insertions(+), 55 deletions(-) create mode 100644 src/networking/types/icmp_type.rs diff --git a/Cargo.lock b/Cargo.lock index 17fe1fdce..e23afc4de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -663,7 +663,7 @@ checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" dependencies = [ "cfg-if", "libc", - "socket2 0.5.5", + "socket2", "windows-sys 0.48.0", ] @@ -1207,9 +1207,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1222,7 +1222,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1757,20 +1757,20 @@ dependencies = [ [[package]] name = "lyon_tessellation" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f5bcf02928361d18e6edb8ad3bc5b93cba8aa57e2508deb072c2d2ade8bbd0d" +checksum = "8c7c67b5bc8123b352b2e7e742b47d1f236a13fe77619433be9568fbd888e9c0" dependencies = [ "float_next_after", "lyon_path", - "thiserror", + "num-traits", ] [[package]] name = "mach2" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ "libc", ] @@ -2535,9 +2535,9 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de09527cd2ea2c2d59fb6c2f8c1ab8c71709ed9d1b6d60b0e1c9fbb6fdcb33c" +checksum = "d135ede8821cf6376eb7a64148901e1690b788c11ae94dc297ae917dbc91dc0e" [[package]] name = "quick-xml" @@ -3040,9 +3040,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -3170,16 +3170,6 @@ dependencies = [ "winres", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -3493,9 +3483,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -3503,7 +3493,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2", "windows-sys 0.48.0", ] @@ -4542,9 +4532,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.5.28" +version = "0.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" dependencies = [ "memchr", ] diff --git a/src/gui/components/button.rs b/src/gui/components/button.rs index 58606d325..14d5ca012 100644 --- a/src/gui/components/button.rs +++ b/src/gui/components/button.rs @@ -1,12 +1,13 @@ -use crate::gui::styles::container::ContainerType; -use crate::gui::types::message::Message; -use crate::translations::translations::hide_translation; -use crate::{Language, StyleType}; use iced::alignment::{Horizontal, Vertical}; use iced::widget::tooltip::Position; use iced::widget::{button, Text, Tooltip}; use iced::{Font, Length, Renderer}; +use crate::gui::styles::container::ContainerType; +use crate::gui::types::message::Message; +use crate::translations::translations::hide_translation; +use crate::{Language, StyleType}; + #[allow(clippy::module_name_repetitions)] pub fn button_hide( message: Message, diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 4cd386fbe..1c6ec5b51 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -1,8 +1,9 @@ use std::net::IpAddr; use iced::alignment::{Horizontal, Vertical}; +use iced::widget::scrollable::Direction; use iced::widget::tooltip::Position; -use iced::widget::{button, horizontal_space, lazy, vertical_space, Rule}; +use iced::widget::{button, horizontal_space, lazy, vertical_space, Rule, Scrollable}; use iced::widget::{Column, Container, Row, Text, Tooltip}; use iced::Length::Fixed; use iced::{Alignment, Font, Length, Renderer}; @@ -11,6 +12,7 @@ use crate::countries::country_utils::{get_computer_tooltip, get_flag_tooltip}; use crate::countries::flags_pictures::FLAGS_WIDTH_BIG; use crate::gui::components::button::button_hide; use crate::gui::styles::container::ContainerType; +use crate::gui::styles::scrollbar::ScrollbarType; use crate::gui::styles::style_constants::{get_font, get_font_headers, FONT_SIZE_TITLE}; use crate::gui::styles::text::TextType; use crate::gui::styles::types::gradient_type::GradientType; @@ -19,6 +21,7 @@ use crate::gui::types::timing_events::TimingEvents; use crate::networking::manage_packets::{get_address_to_lookup, get_traffic_type, is_my_address}; use crate::networking::types::address_port_pair::AddressPortPair; use crate::networking::types::host::Host; +use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::networking::types::traffic_direction::TrafficDirection; use crate::translations::translations::{ @@ -31,7 +34,7 @@ use crate::translations::translations_2::{ fqdn_translation, mac_address_translation, socket_address_translation, source_translation, transmitted_data_translation, }; -use crate::translations::translations_3::copy_translation; +use crate::translations::translations_3::{copy_translation, messages_translation}; use crate::utils::formatted_strings::{get_formatted_bytes_string_with_b, get_socket_address}; use crate::utils::types::icon::Icon; use crate::{Language, Sniffer, StyleType}; @@ -196,7 +199,7 @@ fn col_info( let mut ret_val = Column::new() .spacing(10) - .padding([0, 0, 0, 40]) + .padding([20, 10, 20, 40]) .width(Length::FillPortion(2)) .push(vertical_space(Length::FillPortion(1))) .push( @@ -223,26 +226,47 @@ fn col_info( )); } - ret_val = ret_val - .push(TextType::highlighted_subtitle_with_desc( - &format!( - "{} ({})", - transmitted_data_translation(language), - if val.traffic_direction.eq(&TrafficDirection::Outgoing) { - outgoing_translation(language).to_lowercase() - } else { - incoming_translation(language).to_lowercase() - } - ), - &format!( - "{}\n {} {}", - get_formatted_bytes_string_with_b(val.transmitted_bytes), - val.transmitted_packets, - packets_translation(language) - ), - font, - )) - .push(vertical_space(Length::FillPortion(1))); + ret_val = ret_val.push(TextType::highlighted_subtitle_with_desc( + &format!( + "{} ({})", + transmitted_data_translation(language), + if val.traffic_direction.eq(&TrafficDirection::Outgoing) { + outgoing_translation(language).to_lowercase() + } else { + incoming_translation(language).to_lowercase() + } + ), + &format!( + "{}\n {} {}", + get_formatted_bytes_string_with_b(val.transmitted_bytes), + val.transmitted_packets, + packets_translation(language) + ), + font, + )); + + if is_icmp { + ret_val = + ret_val.push( + Column::new() + .push( + Text::new(format!("{}:", messages_translation(language))) + .style(TextType::Subtitle) + .font(font), + ) + .push( + Scrollable::new(Column::new().padding([0, 10, 10, 0]).push( + Text::new(IcmpType::pretty_print_types(&val.icmp_types)).font(font), + )) + .direction(Direction::Both { + vertical: ScrollbarType::properties(), + horizontal: ScrollbarType::properties(), + }), + ), + ); + } + + ret_val = ret_val.push(vertical_space(Length::FillPortion(1))); ret_val } diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index fb5ff761b..739762b91 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::sync::{Arc, Mutex}; @@ -13,6 +14,7 @@ use crate::networking::types::address_port_pair::AddressPortPair; use crate::networking::types::app_protocol::from_port_to_application_protocol; use crate::networking::types::data_info_host::DataInfoHost; use crate::networking::types::host::Host; +use crate::networking::types::icmp_type::{IcmpType, IcmpTypeV4, IcmpTypeV6}; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::networking::types::my_device::MyDevice; use crate::networking::types::packet_filters_fields::PacketFiltersFields; @@ -28,6 +30,7 @@ pub fn analyze_headers( headers: PacketHeaders, mac_addresses: &mut (String, String), exchanged_bytes: &mut u128, + icmp_type: &mut IcmpType, packet_filters_fields: &mut PacketFiltersFields, ) -> Option { if !analyze_link_header(headers.link, &mut mac_addresses.0, &mut mac_addresses.1) { @@ -49,6 +52,7 @@ pub fn analyze_headers( &mut packet_filters_fields.sport, &mut packet_filters_fields.dport, &mut packet_filters_fields.protocol, + icmp_type, ) { return None; } @@ -117,6 +121,7 @@ fn analyze_transport_header( port1: &mut Option, port2: &mut Option, protocol: &mut Protocol, + icmp_type: &mut IcmpType, ) -> bool { match transport_header { Some(TransportHeader::Udp(udp_header)) => { @@ -135,12 +140,14 @@ fn analyze_transport_header( *port1 = None; *port2 = None; *protocol = Protocol::ICMP; + *icmp_type = IcmpTypeV4::from_etherparse(&icmpv4_header.icmp_type); true } Some(TransportHeader::Icmpv6(icmpv6_header)) => { *port1 = None; *port2 = None; *protocol = Protocol::ICMP; + *icmp_type = IcmpTypeV6::from_etherparse(&icmpv6_header.icmp_type); true } _ => false, @@ -165,6 +172,7 @@ pub fn modify_or_insert_in_map( key: &AddressPortPair, my_device: &MyDevice, mac_addresses: (String, String), + icmp_type: IcmpType, exchanged_bytes: u128, application_protocol: AppProtocol, ) -> InfoAddressPortPair { @@ -203,6 +211,12 @@ pub fn modify_or_insert_in_map( info.transmitted_bytes += exchanged_bytes; info.transmitted_packets += 1; info.final_timestamp = now; + if key.protocol.eq(&Protocol::ICMP) { + info.icmp_types + .entry(icmp_type) + .and_modify(|n| *n += 1) + .or_insert(1); + } }) .or_insert(InfoAddressPortPair { mac_address1: mac_addresses.0, @@ -213,6 +227,11 @@ pub fn modify_or_insert_in_map( final_timestamp: now, app_protocol: application_protocol, traffic_direction, + icmp_types: if key.protocol.eq(&Protocol::ICMP) { + HashMap::from([(icmp_type, 1)]) + } else { + HashMap::new() + }, }) .clone(); diff --git a/src/networking/types/icmp_type.rs b/src/networking/types/icmp_type.rs new file mode 100644 index 000000000..a3e39fc5d --- /dev/null +++ b/src/networking/types/icmp_type.rs @@ -0,0 +1,310 @@ +use std::collections::HashMap; +use std::fmt::{Display, Formatter}; + +use etherparse::{Icmpv4Type, Icmpv6Type}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum IcmpType { + V4(IcmpTypeV4), + V6(IcmpTypeV6), +} + +impl IcmpType { + pub fn pretty_print_types(map: &HashMap) -> String { + let mut ret_val = String::new(); + + let mut vec: Vec<(&IcmpType, &usize)> = map.iter().collect(); + vec.sort_by(|(_, a), (_, b)| b.cmp(a)); + + for (icmp_type, n) in vec { + ret_val.push_str(&format!(" {icmp_type} ({n})\n")); + } + ret_val + } +} + +impl Default for IcmpType { + fn default() -> Self { + Self::V4(IcmpTypeV4::default()) + } +} + +impl Display for IcmpType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + IcmpType::V4(icmpv4_type) => { + icmpv4_type.to_string() + } + IcmpType::V6(icmpv6_type) => { + icmpv6_type.to_string() + } + } + ) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +#[allow(clippy::module_name_repetitions)] +pub enum IcmpTypeV4 { + EchoReply, + DestinationUnreachable, + SourceQuench, + Redirect, + AlternateHostAddress, + Echo, + RouterAdvertisement, + RouterSolicitation, + TimeExceeded, + ParameterProblem, + Timestamp, + TimestampReply, + InformationRequest, + InformationReply, + AddressMaskRequest, + AddressMaskReply, + Traceroute, + DatagramConversionError, + MobileHostRedirect, + IPv6WhereAreYou, + IPv6IAmHere, + MobileRegistrationRequest, + MobileRegistrationReply, + DomainNameRequest, + DomainNameReply, + Skip, + Photuris, + ExtendedEchoRequest, + ExtendedEchoReply, + #[default] + Unknown, +} + +impl IcmpTypeV4 { + pub fn from_etherparse(icmpv4type: &Icmpv4Type) -> IcmpType { + IcmpType::V4(match icmpv4type { + Icmpv4Type::EchoReply(_) => Self::EchoReply, + Icmpv4Type::DestinationUnreachable(_) => Self::DestinationUnreachable, + Icmpv4Type::Redirect(_) => Self::Redirect, + Icmpv4Type::EchoRequest(_) => Self::Echo, + Icmpv4Type::TimeExceeded(_) => Self::TimeExceeded, + Icmpv4Type::ParameterProblem(_) => Self::ParameterProblem, + Icmpv4Type::TimestampRequest(_) => Self::Timestamp, + Icmpv4Type::TimestampReply(_) => Self::TimestampReply, + Icmpv4Type::Unknown { type_u8: id, .. } => match id { + 4 => Self::SourceQuench, + 6 => Self::AlternateHostAddress, + 9 => Self::RouterAdvertisement, + 10 => Self::RouterSolicitation, + 15 => Self::InformationRequest, + 16 => Self::InformationReply, + 17 => Self::AddressMaskRequest, + 18 => Self::AddressMaskReply, + 30 => Self::Traceroute, + 31 => Self::DatagramConversionError, + 32 => Self::MobileHostRedirect, + 33 => Self::IPv6WhereAreYou, + 34 => Self::IPv6IAmHere, + 35 => Self::MobileRegistrationRequest, + 36 => Self::MobileRegistrationReply, + 37 => Self::DomainNameRequest, + 38 => Self::DomainNameReply, + 39 => Self::Skip, + 40 => Self::Photuris, + 42 => Self::ExtendedEchoRequest, + 43 => Self::ExtendedEchoReply, + _ => Self::Unknown, + }, + }) + } +} + +impl Display for IcmpTypeV4 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + IcmpTypeV4::EchoReply => "Echo Reply", + IcmpTypeV4::DestinationUnreachable => "Destination Unreachable", + IcmpTypeV4::SourceQuench => "Source Quench", + IcmpTypeV4::Redirect => "Redirect", + IcmpTypeV4::AlternateHostAddress => "Alternate Host Address", + IcmpTypeV4::Echo => "Echo", + IcmpTypeV4::RouterAdvertisement => "Router Advertisement", + IcmpTypeV4::RouterSolicitation => "Router Solicitation", + IcmpTypeV4::TimeExceeded => "Time Exceeded", + IcmpTypeV4::ParameterProblem => "Parameter Problem", + IcmpTypeV4::Timestamp => "Timestamp", + IcmpTypeV4::TimestampReply => "Timestamp Reply", + IcmpTypeV4::InformationRequest => "Information Request", + IcmpTypeV4::InformationReply => "Information Reply", + IcmpTypeV4::AddressMaskRequest => "Address Mask Request", + IcmpTypeV4::AddressMaskReply => "Address Mask Reply", + IcmpTypeV4::Traceroute => "Traceroute", + IcmpTypeV4::DatagramConversionError => "Datagram Conversion Error", + IcmpTypeV4::MobileHostRedirect => "Mobile Host Redirect", + IcmpTypeV4::IPv6WhereAreYou => "IPv6 Where-Are-You", + IcmpTypeV4::IPv6IAmHere => "IPv6 I-Am-Here", + IcmpTypeV4::MobileRegistrationRequest => "Mobile Registration Request", + IcmpTypeV4::MobileRegistrationReply => "Mobile Registration Reply", + IcmpTypeV4::DomainNameRequest => "Domain Name Request", + IcmpTypeV4::DomainNameReply => "Domain Name Reply", + IcmpTypeV4::Skip => "SKIP", + IcmpTypeV4::Photuris => "Photuris", + IcmpTypeV4::ExtendedEchoRequest => "Extended Echo Request", + IcmpTypeV4::ExtendedEchoReply => "Extended Echo Reply", + IcmpTypeV4::Unknown => "?", + } + ) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] +#[allow(clippy::module_name_repetitions)] +pub enum IcmpTypeV6 { + DestinationUnreachable, + PacketTooBig, + TimeExceeded, + ParameterProblem, + EchoRequest, + EchoReply, + MulticastListenerQuery, + MulticastListenerReport, + MulticastListenerDone, + RouterSolicitation, + RouterAdvertisement, + NeighborSolicitation, + NeighborAdvertisement, + RedirectMessage, + RouterRenumbering, + ICMPNodeInformationQuery, + ICMPNodeInformationResponse, + InverseNeighborDiscoverySolicitationMessage, + InverseNeighborDiscoveryAdvertisementMessage, + Version2MulticastListenerReport, + HomeAgentAddressDiscoveryRequestMessage, + HomeAgentAddressDiscoveryReplyMessage, + MobilePrefixSolicitation, + MobilePrefixAdvertisement, + CertificationPathSolicitationMessage, + CertificationPathAdvertisementMessage, + MulticastRouterAdvertisement, + MulticastRouterSolicitation, + MulticastRouterTermination, + FMIPv6Messages, + RPLControlMessage, + ILNPv6LocatorUpdateMessage, + DuplicateAddressRequest, + DuplicateAddressConfirmation, + MPLControlMessage, + ExtendedEchoRequest, + ExtendedEchoReply, + #[default] + Unknown, +} + +impl IcmpTypeV6 { + pub fn from_etherparse(icmpv6type: &Icmpv6Type) -> IcmpType { + IcmpType::V6(match icmpv6type { + Icmpv6Type::DestinationUnreachable(_) => Self::DestinationUnreachable, + Icmpv6Type::PacketTooBig { .. } => Self::PacketTooBig, + Icmpv6Type::TimeExceeded(_) => Self::TimeExceeded, + Icmpv6Type::ParameterProblem(_) => Self::ParameterProblem, + Icmpv6Type::EchoRequest(_) => Self::EchoRequest, + Icmpv6Type::EchoReply(_) => Self::EchoReply, + Icmpv6Type::Unknown { type_u8: id, .. } => match id { + 130 => Self::MulticastListenerQuery, + 131 => Self::MulticastListenerReport, + 132 => Self::MulticastListenerDone, + 133 => Self::RouterSolicitation, + 134 => Self::RouterAdvertisement, + 135 => Self::NeighborSolicitation, + 136 => Self::NeighborAdvertisement, + 137 => Self::RedirectMessage, + 138 => Self::RouterRenumbering, + 139 => Self::ICMPNodeInformationQuery, + 140 => Self::ICMPNodeInformationResponse, + 141 => Self::InverseNeighborDiscoverySolicitationMessage, + 142 => Self::InverseNeighborDiscoveryAdvertisementMessage, + 143 => Self::Version2MulticastListenerReport, + 144 => Self::HomeAgentAddressDiscoveryRequestMessage, + 145 => Self::HomeAgentAddressDiscoveryReplyMessage, + 146 => Self::MobilePrefixSolicitation, + 147 => Self::MobilePrefixAdvertisement, + 148 => Self::CertificationPathSolicitationMessage, + 149 => Self::CertificationPathAdvertisementMessage, + 151 => Self::MulticastRouterAdvertisement, + 152 => Self::MulticastRouterSolicitation, + 153 => Self::MulticastRouterTermination, + 154 => Self::FMIPv6Messages, + 155 => Self::RPLControlMessage, + 156 => Self::ILNPv6LocatorUpdateMessage, + 157 => Self::DuplicateAddressRequest, + 158 => Self::DuplicateAddressConfirmation, + 159 => Self::MPLControlMessage, + 160 => Self::ExtendedEchoRequest, + 161 => Self::ExtendedEchoReply, + _ => Self::Unknown, + }, + }) + } +} + +impl Display for IcmpTypeV6 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + IcmpTypeV6::DestinationUnreachable => "Destination Unreachable", + IcmpTypeV6::PacketTooBig => "Packet Too Big", + IcmpTypeV6::TimeExceeded => "Time Exceeded", + IcmpTypeV6::ParameterProblem => "Parameter Problem", + IcmpTypeV6::EchoRequest => "Echo Request", + IcmpTypeV6::EchoReply => "Echo Reply", + IcmpTypeV6::MulticastListenerQuery => "Multicast Listener Query", + IcmpTypeV6::MulticastListenerReport => "Multicast Listener Report", + IcmpTypeV6::MulticastListenerDone => "Multicast Listener Done", + IcmpTypeV6::RouterSolicitation => "Router Solicitation", + IcmpTypeV6::RouterAdvertisement => "Router Advertisement", + IcmpTypeV6::NeighborSolicitation => "Neighbor Solicitation", + IcmpTypeV6::NeighborAdvertisement => "Neighbor Advertisement", + IcmpTypeV6::RedirectMessage => "Redirect Message", + IcmpTypeV6::RouterRenumbering => "Router Renumbering", + IcmpTypeV6::ICMPNodeInformationQuery => "ICMP Node Information Query", + IcmpTypeV6::ICMPNodeInformationResponse => "ICMP Node Information Response", + IcmpTypeV6::InverseNeighborDiscoverySolicitationMessage => + "Inverse Neighbor Discovery Solicitation Message", + IcmpTypeV6::InverseNeighborDiscoveryAdvertisementMessage => + "Inverse Neighbor Discovery Advertisement Message", + IcmpTypeV6::Version2MulticastListenerReport => + "Version 2 Multicast Listener Report", + IcmpTypeV6::HomeAgentAddressDiscoveryRequestMessage => + "Home Agent Address Discovery Request Message", + IcmpTypeV6::HomeAgentAddressDiscoveryReplyMessage => + "Home Agent Address Discovery Reply Message", + IcmpTypeV6::MobilePrefixSolicitation => "Mobile Prefix Solicitation", + IcmpTypeV6::MobilePrefixAdvertisement => "Mobile Prefix Advertisement", + IcmpTypeV6::CertificationPathSolicitationMessage => + "Certification Path Solicitation Message", + IcmpTypeV6::CertificationPathAdvertisementMessage => + "Certification Path Advertisement Message", + IcmpTypeV6::MulticastRouterAdvertisement => "Multicast Router Advertisement", + IcmpTypeV6::MulticastRouterSolicitation => "Multicast Router Solicitation", + IcmpTypeV6::MulticastRouterTermination => "Multicast Router Termination", + IcmpTypeV6::FMIPv6Messages => "FMIPv6 Messages", + IcmpTypeV6::RPLControlMessage => "RPL Control Message", + IcmpTypeV6::ILNPv6LocatorUpdateMessage => "ILNPv6 Locator Update Message", + IcmpTypeV6::DuplicateAddressRequest => "Duplicate Address Request", + IcmpTypeV6::DuplicateAddressConfirmation => "Duplicate Address Confirmation", + IcmpTypeV6::MPLControlMessage => "MPL Control Message", + IcmpTypeV6::ExtendedEchoRequest => "Extended Echo Request", + IcmpTypeV6::ExtendedEchoReply => "Extended Echo Reply", + IcmpTypeV6::Unknown => "?", + } + ) + } +} diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index b91f308ea..1ca8db5c3 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -1,10 +1,12 @@ //! Module defining the `InfoAddressPortPair` struct, useful to format the output report file and //! to keep track of statistics about the sniffed traffic. +use std::collections::HashMap; use std::fmt; use chrono::{DateTime, Local}; +use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::traffic_direction::TrafficDirection; use crate::utils::formatted_strings::get_formatted_bytes_string; use crate::AppProtocol; @@ -30,6 +32,8 @@ pub struct InfoAddressPortPair { pub app_protocol: AppProtocol, /// Determines if the connection is incoming or outgoing pub traffic_direction: TrafficDirection, + /// Types of the ICMP messages exchanged, with the relative count (this is empty if not ICMP) + pub icmp_types: HashMap, } impl Default for InfoAddressPortPair { @@ -43,6 +47,7 @@ impl Default for InfoAddressPortPair { final_timestamp: DateTime::default(), app_protocol: AppProtocol::Other, traffic_direction: TrafficDirection::default(), + icmp_types: HashMap::new(), } } } diff --git a/src/networking/types/mod.rs b/src/networking/types/mod.rs index 66a62b825..2134f3284 100644 --- a/src/networking/types/mod.rs +++ b/src/networking/types/mod.rs @@ -6,6 +6,7 @@ pub mod data_info; pub mod data_info_host; pub mod filters; pub mod host; +pub mod icmp_type; pub mod info_address_port_pair; pub mod info_traffic; pub mod ip_collection; diff --git a/src/secondary_threads/parse_packets.rs b/src/secondary_threads/parse_packets.rs index 62ff0230d..33ac999e5 100644 --- a/src/secondary_threads/parse_packets.rs +++ b/src/secondary_threads/parse_packets.rs @@ -14,6 +14,7 @@ use crate::networking::manage_packets::{ }; use crate::networking::types::data_info::DataInfo; use crate::networking::types::filters::Filters; +use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::networking::types::my_device::MyDevice; use crate::networking::types::packet_filters_fields::PacketFiltersFields; @@ -51,12 +52,14 @@ pub fn parse_packets( Ok(headers) => { let mut exchanged_bytes = 0; let mut mac_addresses = (String::new(), String::new()); + let mut icmp_type = IcmpType::default(); let mut packet_filters_fields = PacketFiltersFields::default(); let key_option = analyze_headers( headers, &mut mac_addresses, &mut exchanged_bytes, + &mut icmp_type, &mut packet_filters_fields, ); if key_option.is_none() { @@ -74,6 +77,7 @@ pub fn parse_packets( &key, device, mac_addresses, + icmp_type, exchanged_bytes, application_protocol, ); diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs index 0c6e901c4..77d6041e4 100644 --- a/src/translations/translations_3.rs +++ b/src/translations/translations_3.rs @@ -73,3 +73,11 @@ pub fn invalid_filters_translation(language: Language) -> &'static str { _ => "Invalid filters", } } + +pub fn messages_translation(language: Language) -> &'static str { + match language { + Language::EN => "Messages", + Language::IT => "Messaggi", + _ => "Messages", + } +} From 92bd22a2f9ae053eaf5d0edeecdd82785e44bf5c Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Wed, 20 Dec 2023 16:51:24 +0100 Subject: [PATCH 4/7] fixed tests --- src/gui/types/sniffer.rs | 9 ++++- src/networking/types/app_protocol.rs | 2 +- src/networking/types/port_collection.rs | 44 ++++++++++++------------- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/gui/types/sniffer.rs b/src/gui/types/sniffer.rs index c1a9ea1a9..0b874a5f4 100644 --- a/src/gui/types/sniffer.rs +++ b/src/gui/types/sniffer.rs @@ -593,9 +593,16 @@ mod tests { sniffer.update(Message::ProtocolSelection(Protocol::UDP, true)); assert_eq!(sniffer.filters.protocols, HashSet::from(Protocol::ALL)); sniffer.update(Message::ProtocolSelection(Protocol::UDP, false)); - assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::TCP])); + assert_eq!( + sniffer.filters.protocols, + HashSet::from([Protocol::TCP, Protocol::ICMP]) + ); sniffer.update(Message::ProtocolSelection(Protocol::TCP, false)); + assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ICMP])); + sniffer.update(Message::ProtocolSelection(Protocol::ICMP, false)); assert_eq!(sniffer.filters.protocols, HashSet::new()); + sniffer.update(Message::ProtocolSelection(Protocol::UDP, true)); + assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::UDP])); } #[test] diff --git a/src/networking/types/app_protocol.rs b/src/networking/types/app_protocol.rs index f15d57b9a..9235a6f98 100644 --- a/src/networking/types/app_protocol.rs +++ b/src/networking/types/app_protocol.rs @@ -181,6 +181,6 @@ mod tests { #[test] fn app_protocol_display_other() { let test_str = AppProtocol::Other.to_string(); - assert_eq!(test_str, "-"); + assert_eq!(test_str, "?"); } } diff --git a/src/networking/types/port_collection.rs b/src/networking/types/port_collection.rs index 9a186e818..f3383f138 100644 --- a/src/networking/types/port_collection.rs +++ b/src/networking/types/port_collection.rs @@ -87,13 +87,13 @@ mod tests { #[test] fn test_default_collection_contains_everything() { let collection = PortCollection::default(); - assert!(collection.contains(0)); - assert!(collection.contains(1)); - assert!(collection.contains(2)); - assert!(collection.contains(80)); - assert!(collection.contains(8080)); - assert!(collection.contains(55333)); - assert!(collection.contains(65535)); + assert!(collection.contains(Some(0))); + assert!(collection.contains(Some(1))); + assert!(collection.contains(Some(2))); + assert!(collection.contains(Some(80))); + assert!(collection.contains(Some(8080))); + assert!(collection.contains(Some(55333))); + assert!(collection.contains(Some(65535))); } #[test] @@ -163,20 +163,20 @@ mod tests { #[test] fn test_port_collection_contains() { let collection = PortCollection::new("1,2,25-30,55,101-117").unwrap(); - assert!(collection.contains(1)); - assert!(collection.contains(2)); - assert!(collection.contains(25)); - assert!(collection.contains(27)); - assert!(collection.contains(30)); - assert!(collection.contains(55)); - assert!(collection.contains(101)); - assert!(collection.contains(109)); - assert!(collection.contains(117)); - assert!(!collection.contains(4)); - assert!(!collection.contains(24)); - assert!(!collection.contains(31)); - assert!(!collection.contains(100)); - assert!(!collection.contains(118)); - assert!(!collection.contains(8080)); + assert!(collection.contains(Some(1))); + assert!(collection.contains(Some(2))); + assert!(collection.contains(Some(25))); + assert!(collection.contains(Some(27))); + assert!(collection.contains(Some(30))); + assert!(collection.contains(Some(55))); + assert!(collection.contains(Some(101))); + assert!(collection.contains(Some(109))); + assert!(collection.contains(Some(117))); + assert!(!collection.contains(Some(4))); + assert!(!collection.contains(Some(24))); + assert!(!collection.contains(Some(31))); + assert!(!collection.contains(Some(100))); + assert!(!collection.contains(Some(118))); + assert!(!collection.contains(Some(8080))); } } From 32d1ba5b748d722de269189e8ee312a0267235a0 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Wed, 20 Dec 2023 17:16:45 +0100 Subject: [PATCH 5/7] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f76f8f45f..7b1a5e4c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] +- Added support for ICMP connections and messages ([#417](https://github.com/GyulyVGC/sniffnet/pull/417) — fixes [#288](https://github.com/GyulyVGC/sniffnet/issues/288)) - Introduced new filtering capabilities to allow users specify custom values of ports and IP addresses ([#414](https://github.com/GyulyVGC/sniffnet/pull/414)) - The size of text and widgets can now be customised by setting a proper zoom value (fixes [#202](https://github.com/GyulyVGC/sniffnet/issues/202) and [#344](https://github.com/GyulyVGC/sniffnet/issues/344)) - Added possibility to totally customize the app's theme via styles defined in TOML files ([#286](https://github.com/GyulyVGC/sniffnet/pull/286)) From 99d398c92731de65c89a62c23a90bfeb088ab471 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Wed, 20 Dec 2023 17:37:07 +0100 Subject: [PATCH 6/7] minor fix --- src/gui/pages/connection_details_page.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 1c6ec5b51..3b9231818 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -37,7 +37,7 @@ use crate::translations::translations_2::{ use crate::translations::translations_3::{copy_translation, messages_translation}; use crate::utils::formatted_strings::{get_formatted_bytes_string_with_b, get_socket_address}; use crate::utils::types::icon::Icon; -use crate::{Language, Sniffer, StyleType}; +use crate::{Language, Protocol, Sniffer, StyleType}; pub fn connection_details_page( sniffer: &Sniffer, @@ -190,7 +190,7 @@ fn col_info( font: Font, language: Language, ) -> Column<'static, Message, Renderer> { - let is_icmp = key.port1.is_none() || key.port2.is_none(); + let is_icmp = key.protocol.eq(&Protocol::ICMP); let protocol_caption = if is_icmp { protocol_translation(language) } else { From aa35c9dfe9c19d5190763c58dee388b4da498f8a Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Wed, 20 Dec 2023 22:31:05 +0100 Subject: [PATCH 7/7] added AppProtocol::NotApplicable variant in case of ICMP --- src/gui/pages/overview_page.rs | 4 +- src/networking/manage_packets.rs | 12 +-- src/networking/types/app_protocol.rs | 102 +++++++++++------- .../types/info_address_port_pair.rs | 18 +--- src/report/get_report_entries.rs | 11 +- 5 files changed, 76 insertions(+), 71 deletions(-) diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs index de7ae3bec..59c6c37b9 100644 --- a/src/gui/pages/overview_page.rs +++ b/src/gui/pages/overview_page.rs @@ -363,8 +363,8 @@ fn col_app(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer width * 0.88 { + // check if Unknown is longer than the first entry + if app.eq(&AppProtocol::Unknown) && incoming_bar_len + outgoing_bar_len > width * 0.88 { let incoming_proportion = incoming_bar_len / (incoming_bar_len + outgoing_bar_len); incoming_bar_len = width * 0.88 * incoming_proportion; outgoing_bar_len = width * 0.88 * (1.0 - incoming_proportion); diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 739762b91..6d0c300b0 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -155,15 +155,11 @@ fn analyze_transport_header( } pub fn get_app_protocol(src_port: Option, dst_port: Option) -> AppProtocol { - if src_port.is_some() && dst_port.is_some() { - let mut application_protocol = from_port_to_application_protocol(src_port.unwrap()); - if (application_protocol).eq(&AppProtocol::Other) { - application_protocol = from_port_to_application_protocol(dst_port.unwrap()); - } - application_protocol - } else { - AppProtocol::Other + let mut application_protocol = from_port_to_application_protocol(src_port); + if (application_protocol).eq(&AppProtocol::Unknown) { + application_protocol = from_port_to_application_protocol(dst_port); } + application_protocol } /// Function to insert the source and destination of a packet into the shared map containing the analyzed traffic. diff --git a/src/networking/types/app_protocol.rs b/src/networking/types/app_protocol.rs index 9235a6f98..0b01e6da0 100644 --- a/src/networking/types/app_protocol.rs +++ b/src/networking/types/app_protocol.rs @@ -1,7 +1,7 @@ use std::fmt; /// Enum representing the possible observed values of application layer protocol. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] #[allow(clippy::upper_case_acronyms)] pub enum AppProtocol { /// File Transfer Protocol @@ -53,8 +53,11 @@ pub enum AppProtocol { SSDP, /// Extensible Messaging and Presence Protocol | XMPP, - /// not identified - Other, + /// Not identified + #[default] + Unknown, + /// Not applicable + NotApplicable, } /// Given an integer in the range `0..=65535`, this function returns an `Option` containing @@ -78,40 +81,47 @@ pub enum AppProtocol { /// //Unknown port-to-protocol mapping /// assert_eq!(y, Option::None); /// ``` -pub fn from_port_to_application_protocol(port: u16) -> AppProtocol { - match port { - 20..=21 => AppProtocol::FTP, - 22 => AppProtocol::SSH, - 23 => AppProtocol::Telnet, - 25 => AppProtocol::SMTP, - 49 => AppProtocol::TACACS, - 53 => AppProtocol::DNS, - 67..=68 => AppProtocol::DHCP, - 69 => AppProtocol::TFTP, - 80 | 8080 => AppProtocol::HTTP, - 109..=110 => AppProtocol::POP, - 123 => AppProtocol::NTP, - 137..=139 => AppProtocol::NetBIOS, - 143 | 220 => AppProtocol::IMAP, - 161..=162 | 199 => AppProtocol::SNMP, - 179 => AppProtocol::BGP, - 389 => AppProtocol::LDAP, - 443 => AppProtocol::HTTPS, - 636 => AppProtocol::LDAPS, - 989..=990 => AppProtocol::FTPS, - 993 => AppProtocol::IMAPS, - 995 => AppProtocol::POP3S, - 1900 => AppProtocol::SSDP, - 5222 => AppProtocol::XMPP, - 5353 => AppProtocol::mDNS, - _ => AppProtocol::Other, +pub fn from_port_to_application_protocol(port: Option) -> AppProtocol { + if let Some(res) = port { + match res { + 20..=21 => AppProtocol::FTP, + 22 => AppProtocol::SSH, + 23 => AppProtocol::Telnet, + 25 => AppProtocol::SMTP, + 49 => AppProtocol::TACACS, + 53 => AppProtocol::DNS, + 67..=68 => AppProtocol::DHCP, + 69 => AppProtocol::TFTP, + 80 | 8080 => AppProtocol::HTTP, + 109..=110 => AppProtocol::POP, + 123 => AppProtocol::NTP, + 137..=139 => AppProtocol::NetBIOS, + 143 | 220 => AppProtocol::IMAP, + 161..=162 | 199 => AppProtocol::SNMP, + 179 => AppProtocol::BGP, + 389 => AppProtocol::LDAP, + 443 => AppProtocol::HTTPS, + 636 => AppProtocol::LDAPS, + 989..=990 => AppProtocol::FTPS, + 993 => AppProtocol::IMAPS, + 995 => AppProtocol::POP3S, + 1900 => AppProtocol::SSDP, + 5222 => AppProtocol::XMPP, + 5353 => AppProtocol::mDNS, + _ => AppProtocol::Unknown, + } + } else { + // ICMP + AppProtocol::NotApplicable } } impl fmt::Display for AppProtocol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.eq(&AppProtocol::Other) { + if self.eq(&AppProtocol::Unknown) { write!(f, "?") + } else if self.eq(&AppProtocol::NotApplicable) { + write!(f, "-") } else { write!(f, "{self:?}") } @@ -120,7 +130,7 @@ impl fmt::Display for AppProtocol { // impl AppProtocol { // pub(crate) const ALL: [AppProtocol; 25] = [ -// AppProtocol::Other, +// AppProtocol::Unknown, // AppProtocol::BGP, // AppProtocol::DHCP, // AppProtocol::DNS, @@ -154,22 +164,28 @@ mod tests { #[test] fn from_port_to_application_protocol_ftp() { - let result1 = from_port_to_application_protocol(20); + let result1 = from_port_to_application_protocol(Some(20)); assert_eq!(AppProtocol::FTP, result1); - let result2 = from_port_to_application_protocol(21); + let result2 = from_port_to_application_protocol(Some(21)); assert_eq!(AppProtocol::FTP, result2); } #[test] fn from_port_to_application_protocol_ssh() { - let result = from_port_to_application_protocol(22); + let result = from_port_to_application_protocol(Some(22)); assert_eq!(AppProtocol::SSH, result); } #[test] - fn from_port_to_application_protocol_other() { - let result = from_port_to_application_protocol(500); - assert_eq!(AppProtocol::Other, result); + fn from_port_to_application_protocol_unknown() { + let result = from_port_to_application_protocol(Some(500)); + assert_eq!(AppProtocol::Unknown, result); + } + + #[test] + fn from_port_to_application_protocol_not_applicable() { + let result = from_port_to_application_protocol(None); + assert_eq!(AppProtocol::NotApplicable, result); } #[test] @@ -179,8 +195,14 @@ mod tests { } #[test] - fn app_protocol_display_other() { - let test_str = AppProtocol::Other.to_string(); + fn app_protocol_display_unknown() { + let test_str = AppProtocol::Unknown.to_string(); assert_eq!(test_str, "?"); } + + #[test] + fn app_protocol_display_not_applicable() { + let test_str = AppProtocol::NotApplicable.to_string(); + assert_eq!(test_str, "-"); + } } diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index 1ca8db5c3..2ede47a8c 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -14,7 +14,7 @@ use crate::AppProtocol; /// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic. /// /// Each `InfoAddressPortPair` struct is associated to a single address:port pair. -#[derive(Clone)] +#[derive(Clone, Default)] pub struct InfoAddressPortPair { /// Source MAC address pub mac_address1: String, @@ -36,22 +36,6 @@ pub struct InfoAddressPortPair { pub icmp_types: HashMap, } -impl Default for InfoAddressPortPair { - fn default() -> Self { - Self { - mac_address1: String::new(), - mac_address2: String::new(), - transmitted_bytes: 0, - transmitted_packets: 0, - initial_timestamp: DateTime::default(), - final_timestamp: DateTime::default(), - app_protocol: AppProtocol::Other, - traffic_direction: TrafficDirection::default(), - icmp_types: HashMap::new(), - } - } -} - impl fmt::Display for InfoAddressPortPair { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bytes_string = get_formatted_bytes_string(self.transmitted_bytes); diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs index 4c8be9e6c..fbe0e6a50 100644 --- a/src/report/get_report_entries.rs +++ b/src/report/get_report_entries.rs @@ -160,13 +160,16 @@ pub fn get_app_entries( chart_type: ChartType, ) -> Vec<(AppProtocol, DataInfo)> { let info_traffic_lock = info_traffic.lock().unwrap(); - let mut sorted_vec: Vec<(&AppProtocol, &DataInfo)> = - info_traffic_lock.app_protocols.iter().collect(); + let mut sorted_vec: Vec<(&AppProtocol, &DataInfo)> = info_traffic_lock + .app_protocols + .iter() + .filter(|(app_protocol, _)| app_protocol.ne(&&AppProtocol::NotApplicable)) + .collect(); sorted_vec.sort_by(|&(p1, a), &(p2, b)| { - if p1.eq(&AppProtocol::Other) { + if p1.eq(&AppProtocol::Unknown) { Ordering::Greater - } else if p2.eq(&AppProtocol::Other) { + } else if p2.eq(&AppProtocol::Unknown) { Ordering::Less } else { match chart_type {