Skip to content

Commit

Permalink
Merge pull request #409 from GyulyVGC/copy-ip
Browse files Browse the repository at this point in the history
Copy IP addresses to clipboard and add IP address in search parameters inside Inspect page
  • Loading branch information
GyulyVGC authored Dec 14, 2023
2 parents e9143e0 + c5dc14d commit 3c03dc2
Show file tree
Hide file tree
Showing 13 changed files with 147 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All Sniffnet releases with the relative changes are documented in this file.
## [UNRELEASED]
- 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))
- IP addresses can now be copied to clipboard from the popup related to a given entry of the connections table, and a new search parameter has been introduced in Inspect page to allow users filter their connections based on IP address values ([#409](https://github.com/GyulyVGC/sniffnet/pull/409))
- Added Japanese translation 🇯🇵 ([#343](https://github.com/GyulyVGC/sniffnet/pull/343))
- Added Uzbek translation 🇺🇿 ([#385](https://github.com/GyulyVGC/sniffnet/pull/385))
- Window size and position are now remembered, so that Sniffnet can reopen with the same window properties
Expand Down
Binary file modified resources/fonts/subset/icons.ttf
Binary file not shown.
57 changes: 51 additions & 6 deletions src/gui/pages/connection_details_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::gui::styles::style_constants::{get_font, get_font_headers, FONT_SIZE_
use crate::gui::styles::text::TextType;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
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;
Expand All @@ -28,6 +29,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::utils::formatted_strings::{get_formatted_bytes_string_with_b, get_socket_address};
use crate::utils::types::icon::Icon;
use crate::{Language, Sniffer, StyleType};
Expand All @@ -37,7 +39,11 @@ pub fn connection_details_page(
key: AddressPortPair,
) -> Container<Message, Renderer<StyleType>> {
Container::new(lazy(
sniffer.runtime_data.tot_sent_packets + sniffer.runtime_data.tot_received_packets,
(
sniffer.runtime_data.tot_sent_packets + sniffer.runtime_data.tot_received_packets,
sniffer.timing_events.was_just_copy_ip(&key.address1),
sniffer.timing_events.was_just_copy_ip(&key.address2),
),
move |_| page_content(sniffer, &key),
))
}
Expand Down Expand Up @@ -116,6 +122,7 @@ fn page_content(
&val.mac_address1,
font,
language,
&sniffer.timing_events,
);
let mut dest_col = get_src_or_dest_col(
dest_caption,
Expand All @@ -124,6 +131,7 @@ fn page_content(
&val.mac_address2,
font,
language,
&sniffer.timing_events,
);

if address_to_lookup.eq(&key.address1) {
Expand Down Expand Up @@ -308,6 +316,7 @@ fn get_src_or_dest_col(
mac: &str,
font: Font,
language: Language,
timing_events: &TimingEvents,
) -> Column<'static, Message, Renderer<StyleType>> {
Column::new()
.spacing(4)
Expand All @@ -317,11 +326,17 @@ fn get_src_or_dest_col(
.align_x(Horizontal::Center),
)
.push(Rule::horizontal(10.0))
.push(TextType::highlighted_subtitle_with_desc(
socket_address_translation(language),
&get_socket_address(ip, port),
font,
))
.push(
Row::new()
.spacing(10)
.align_items(Alignment::End)
.push(TextType::highlighted_subtitle_with_desc(
socket_address_translation(language),
&get_socket_address(ip, port),
font,
))
.push(get_button_copy(language, font, ip, timing_events)),
)
.push(TextType::highlighted_subtitle_with_desc(
mac_address_translation(language),
mac,
Expand Down Expand Up @@ -359,3 +374,33 @@ fn assemble_widgets(
.push(vertical_space(Length::FillPortion(1))),
)
}

fn get_button_copy(
language: Language,
font: Font,
string: &String,
timing_events: &TimingEvents,
) -> Tooltip<'static, Message, Renderer<StyleType>> {
let icon = if timing_events.was_just_copy_ip(string) {
Text::new("✔").font(font).size(14)
} else {
Icon::Copy.to_text().size(12)
};

let content = button(
icon.horizontal_alignment(Horizontal::Center)
.vertical_alignment(Vertical::Center),
)
.padding(0)
.height(Length::Fixed(25.0))
.width(Length::Fixed(25.0))
.on_press(Message::CopyIp(string.clone()));

Tooltip::new(
content,
format!("{} (IP)", copy_translation(language)),
Position::Right,
)
.font(font)
.style(ContainerType::Tooltip)
}
18 changes: 17 additions & 1 deletion src/gui/pages/inspect_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::gui::types::message::Message;
use crate::networking::types::search_parameters::{FilterInputType, SearchParameters};
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::report::get_report_entries::get_searched_entries;
use crate::translations::translations::application_protocol_translation;
use crate::translations::translations::{address_translation, application_protocol_translation};
use crate::translations::translations_2::{
administrative_entity_translation, country_translation, domain_name_translation,
no_search_results_translation, only_show_favorites_translation, search_filters_translation,
Expand Down Expand Up @@ -244,6 +244,14 @@ fn filters_col(
Row::new()
.align_items(Alignment::Center)
.spacing(10)
.push(filter_input(
FilterInputType::Address,
&search_params.address,
address_translation(language),
120.0,
search_params.clone(),
font,
))
.push(filter_input(
FilterInputType::App,
&search_params.app,
Expand Down Expand Up @@ -312,6 +320,10 @@ fn filter_input(
as_name: String::new(),
..search_params.clone()
},
FilterInputType::Address => SearchParameters {
address: String::new(),
..search_params.clone()
},
},
font,
);
Expand All @@ -335,6 +347,10 @@ fn filter_input(
as_name: new_value.trim().to_string(),
..search_params.clone()
},
FilterInputType::Address => SearchParameters {
address: new_value.trim().to_string(),
..search_params.clone()
},
})
})
.padding([0, 5])
Expand Down
2 changes: 2 additions & 0 deletions src/gui/types/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,6 @@ pub enum Message {
// CustomReport(String),
/// Save the configurations of the app and quit
CloseRequested,
/// Copies the given string to clipboard
CopyIp(String),
}
1 change: 1 addition & 0 deletions src/gui/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod message;
pub mod runtime_data;
pub mod sniffer;
pub mod timing_events;
20 changes: 12 additions & 8 deletions src/gui/types/sniffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
use std::collections::{HashSet, VecDeque};
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use iced::{window, Command};
use pcap::Device;
Expand All @@ -16,6 +15,7 @@ use crate::gui::pages::types::running_page::RunningPage;
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::custom_palette::{CustomPalette, ExtraStyles};
use crate::gui::types::message::Message;
use crate::gui::types::timing_events::TimingEvents;
use crate::mmdb::asn::ASN_MMDB;
use crate::mmdb::country::COUNTRY_MMDB;
use crate::mmdb::types::mmdb_reader::MmdbReader;
Expand Down Expand Up @@ -75,8 +75,6 @@ pub struct Sniffer {
pub search: SearchParameters,
/// Current page number of inspect search results
pub page_number: usize,
/// Record the timestamp of last window focus
pub last_focus_time: std::time::Instant,
/// Application settings
pub settings: ConfigSettings,
/// Position and size of the app window
Expand All @@ -85,6 +83,8 @@ pub struct Sniffer {
pub country_mmdb_reader: Arc<MmdbReader>,
/// MMDB reader for ASN
pub asn_mmdb_reader: Arc<MmdbReader>,
/// Time-related events
pub timing_events: TimingEvents,
}

impl Sniffer {
Expand All @@ -108,14 +108,14 @@ impl Sniffer {
unread_notifications: 0,
search: SearchParameters::default(),
page_number: 1,
last_focus_time: std::time::Instant::now(),
settings: configs.settings.clone(),
window: configs.window,
country_mmdb_reader: Arc::new(MmdbReader::from(
&configs.settings.mmdb_country,
COUNTRY_MMDB,
)),
asn_mmdb_reader: Arc::new(MmdbReader::from(&configs.settings.mmdb_asn, ASN_MMDB)),
timing_events: TimingEvents::default(),
}
}

Expand Down Expand Up @@ -196,7 +196,7 @@ impl Sniffer {
Message::SwitchPage(next) => {
// To prevent SwitchPage be triggered when using `Alt` + `Tab` to switch back,
// first check if user switch back just now, and ignore the request for a short time.
if self.last_focus_time.elapsed() > Duration::from_millis(200) {
if !self.timing_events.was_just_focus() {
self.switch_page(next);
}
}
Expand Down Expand Up @@ -232,7 +232,7 @@ impl Sniffer {
}
}
}
Message::WindowFocused => self.last_focus_time = std::time::Instant::now(),
Message::WindowFocused => self.timing_events.focus_now(),
Message::GradientsSelection(gradient_type) => {
self.settings.color_gradient = gradient_type;
}
Expand Down Expand Up @@ -263,6 +263,10 @@ impl Sniffer {
self.get_configs().store();
return iced::window::close();
}
Message::CopyIp(string) => {
self.timing_events.copy_ip_now(string.clone());
return iced::clipboard::write(string);
}
_ => {}
}
Command::none()
Expand Down Expand Up @@ -1119,9 +1123,9 @@ mod tests {
}

#[test]
fn test_correctly_switch_running_and_notification_pages() {
fn test_correctly_switch_running_and_settings_pages() {
let mut sniffer = Sniffer::new(&Configs::default(), Arc::new(Mutex::new(None)));
sniffer.last_focus_time = std::time::Instant::now().sub(Duration::from_millis(400));
sniffer.timing_events.focus = std::time::Instant::now().sub(Duration::from_millis(400));

// initial status
assert_eq!(sniffer.settings_page, None);
Expand Down
39 changes: 39 additions & 0 deletions src/gui/types/timing_events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use std::time::Duration;

pub struct TimingEvents {
/// Timestamp of last window focus
pub focus: std::time::Instant,
/// Timestamp of the last press on Copy IP button, with the related IP address
pub copy_ip: (std::time::Instant, String),
}

impl TimingEvents {
const TIMEOUT_FOCUS: u64 = 200;
const TIMEOUT_COPY_IP: u64 = 1500;

pub fn focus_now(&mut self) {
self.focus = std::time::Instant::now();
}

pub fn was_just_focus(&self) -> bool {
self.focus.elapsed() < Duration::from_millis(TimingEvents::TIMEOUT_FOCUS)
}

pub fn copy_ip_now(&mut self, ip: String) {
self.copy_ip = (std::time::Instant::now(), ip);
}

pub fn was_just_copy_ip(&self, ip: &String) -> bool {
self.copy_ip.0.elapsed() < Duration::from_millis(TimingEvents::TIMEOUT_COPY_IP)
&& self.copy_ip.1.eq(ip)
}
}

impl Default for TimingEvents {
fn default() -> Self {
Self {
focus: std::time::Instant::now(),
copy_ip: (std::time::Instant::now(), String::new()),
}
}
}
4 changes: 4 additions & 0 deletions src/networking/types/search_parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
pub struct SearchParameters {
/// Application protocol
pub app: String,
/// IP address
pub address: String,
/// Domain
pub domain: String,
/// Country
Expand All @@ -20,6 +22,7 @@ impl SearchParameters {
|| !self.domain.is_empty()
|| !self.country.is_empty()
|| !self.as_name.is_empty()
|| !self.address.is_empty()
}
}

Expand All @@ -28,4 +31,5 @@ pub enum FilterInputType {
Domain,
Country,
AS,
Address,
}
9 changes: 9 additions & 0 deletions src/report/get_report_entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec<ReportEntry>, usize) {
let searched_domain = &*sniffer.search.domain.to_lowercase();
let searched_country = &*sniffer.search.country.to_lowercase();
let searched_as_name = &*sniffer.search.as_name.to_lowercase();
let searched_address = &*sniffer.search.address.to_lowercase();
let searched_only_fav = sniffer.search.only_favorites;
// if a host-related filter is active and this address has not been resolved yet => false
if r_dns_host.is_none()
Expand Down Expand Up @@ -67,6 +68,14 @@ pub fn get_searched_entries(sniffer: &Sniffer) -> (Vec<ReportEntry>, usize) {
return false;
}
}
// check IP address filter
if !searched_address.is_empty() {
let source = key.address1.to_lowercase();
let dest = key.address2.to_lowercase();
if !source.contains(searched_address) && !dest.contains(searched_address) {
return false;
}
}
// check favorites filter
if searched_only_fav
&& !info_traffic_lock
Expand Down
2 changes: 1 addition & 1 deletion src/secondary_threads/check_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn is_newer_release_available(max_retries: u8, seconds_between_retries: u8) -> O
}
}

#[cfg(test)]
#[cfg(all(test, not(target_os = "macos")))]
mod tests {
use super::*;

Expand Down
8 changes: 8 additions & 0 deletions src/translations/translations_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ pub fn custom_style_translation(language: Language) -> &'static str {
_ => "Custom style",
}
}

pub fn copy_translation(language: Language) -> &'static str {
match language {
Language::EN => "Copy",
Language::IT => "Copia",
_ => "Copy",
}
}
2 changes: 2 additions & 0 deletions src/utils/types/icon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub enum Icon {
Bin,
BytesThreshold,
Clock,
Copy,
Dots,
Error,
// File,
Expand Down Expand Up @@ -74,6 +75,7 @@ impl Icon {
Icon::Star => "g",
Icon::Warning => "T",
Icon::Waves => "y",
Icon::Copy => "u",
})
.font(ICONS)
}
Expand Down

0 comments on commit 3c03dc2

Please sign in to comment.