From 4cb0bcbec6dbac0eebe08df099ba7a4b561dd6e8 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 22:35:40 +0200 Subject: [PATCH 01/23] docs: add GeorgeLS as a contributor for translation (#1177) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index c55c3853b..853e317fe 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -939,6 +939,15 @@ "contributions": [ "translation" ] + }, + { + "login": "GeorgeLS", + "name": "George Liontos", + "avatar_url": "https://avatars.githubusercontent.com/u/24968445?v=4", + "profile": "https://github.com/GeorgeLS", + "contributions": [ + "translation" + ] } ], "contributorsPerLine": 7, diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 83dcb5b30..f0da21577 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -51,87 +51,88 @@ A big shout-out to all the contributors of Sniffnet ([emoji key](https://allcont GNUser
GNUser

📖 📦 + George Liontos
George Liontos

🌍 George Shuklin
George Shuklin

🌍 Giusy Digital
Giusy Digital

🐛 Glenn Johansson
Glenn Johansson

💵 Hiroki Tagato
Hiroki Tagato

📦 Hubert
Hubert

🌍 Hüseyin Fahri Uzun
Hüseyin Fahri Uzun

🌍 - IPinfo
IPinfo

💵 + IPinfo
IPinfo

💵 Ilmi2
Ilmi2

💵 Jan Walter
Jan Walter

💵 Jauder Ho
Jauder Ho

🚇 Joshua Megnauth
Joshua Megnauth

💻 🎨 Julian Schmid
Julian Schmid

💻 🤔 LiChenG-P
LiChenG-P

💻 - Liam OBrien
Liam OBrien

🤔 + Liam OBrien
Liam OBrien

🤔 Limdongju
Limdongju

🌍 Lion Rayonnant
Lion Rayonnant

🌍 Ludwig Stecher
Ludwig Stecher

🤔 💻 Marc Gavilán
Marc Gavilán

🌍 Marco Cadetg
Marco Cadetg

📦 Matt Seng
Matt Seng

💵 - Matthias Braun
Matthias Braun

📖 + Matthias Braun
Matthias Braun

📖 MelcuGoa
MelcuGoa

🌍 Michal Zboril
Michal Zboril

🌍 Michel Hansma
Michel Hansma

🎨 ️️️️♿️ Morgan Hill
Morgan Hill

🛡️ Muhammadali Hakimov
Muhammadali Hakimov

🌍 Nubi
Nubi

🌍 - Oleksii Filonenko
Oleksii Filonenko

🌍 + Oleksii Filonenko
Oleksii Filonenko

🌍 Orhun Parmaksız
Orhun Parmaksız

📖 📦 💵 Paco
Paco

🌍 Peter Dave Hello
Peter Dave Hello

🌍 Phil Clifford
Phil Clifford

📦 Quetzal-coalt
Quetzal-coalt

🌍 Raghav Potluri
Raghav Potluri

📦 - Recall.ai
Recall.ai

💵 + Recall.ai
Recall.ai

💵 Ron
Ron

🤔 Safaraliev Maxim
Safaraliev Maxim

🌍 Shawn Yeager
Shawn Yeager

💵 SignPath GmbH
SignPath GmbH

📦 The Artifex
The Artifex

🌍 📦 Tony
Tony

💵 - Trịnh Duy Hưng
Trịnh Duy Hưng

🌍 + Trịnh Duy Hưng
Trịnh Duy Hưng

🌍 TyseEX
TyseEX

🐛 Victor Nilsson
Victor Nilsson

🌍 💻 Wang Zishi
Wang Zishi

🌍 Yevhen
Yevhen

🌍 Ylva
Ylva

🌍 ZEROF
ZEROF

💵 - ZeroDot1
ZeroDot1

🎨 ️️️️♿️ + ZeroDot1
ZeroDot1

🎨 ️️️️♿️ clr
clr

📖 🌍 edwloef
edwloef

🐛 ervinpopescu
ervinpopescu

🌍 glitsj16
glitsj16

📦 guilherme-demarchi
guilherme-demarchi

🌍 hirotake111
hirotake111

🌍 - islameehassan
islameehassan

💻 + islameehassan
islameehassan

💻 louis-ym4
louis-ym4

🎨 luca3s
luca3s

🌍 mbe57
mbe57

🤔 pia
pia

🌍 pin
pin

📦 shu-kitamura
shu-kitamura

💻 🌍 - starccy
starccy

💻 + starccy
starccy

💻 tiansheng li
tiansheng li

💵 vtiinanen
vtiinanen

🌍 yossarian
yossarian

🌍 From b1e6c664c4a38f73cc3af3e463a404ef03d5e35c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 17:21:14 +0200 Subject: [PATCH 02/23] docs: add SimplyClever96 as a contributor for financial (#1180) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 17 +++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 853e317fe..e58873eec 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -948,6 +948,15 @@ "contributions": [ "translation" ] + }, + { + "login": "SimplyClever96", + "name": "Kevin", + "avatar_url": "https://avatars.githubusercontent.com/u/265333205?v=4", + "profile": "https://technik-kiste.de/", + "contributions": [ + "financial" + ] } ], "contributorsPerLine": 7, diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index f0da21577..fb6e96316 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -66,72 +66,73 @@ A big shout-out to all the contributors of Sniffnet ([emoji key](https://allcont Jauder Ho
Jauder Ho

🚇 Joshua Megnauth
Joshua Megnauth

💻 🎨 Julian Schmid
Julian Schmid

💻 🤔 - LiChenG-P
LiChenG-P

💻 + Kevin
Kevin

💵 + LiChenG-P
LiChenG-P

💻 Liam OBrien
Liam OBrien

🤔 Limdongju
Limdongju

🌍 Lion Rayonnant
Lion Rayonnant

🌍 Ludwig Stecher
Ludwig Stecher

🤔 💻 Marc Gavilán
Marc Gavilán

🌍 Marco Cadetg
Marco Cadetg

📦 - Matt Seng
Matt Seng

💵 + Matt Seng
Matt Seng

💵 Matthias Braun
Matthias Braun

📖 MelcuGoa
MelcuGoa

🌍 Michal Zboril
Michal Zboril

🌍 Michel Hansma
Michel Hansma

🎨 ️️️️♿️ Morgan Hill
Morgan Hill

🛡️ Muhammadali Hakimov
Muhammadali Hakimov

🌍 - Nubi
Nubi

🌍 + Nubi
Nubi

🌍 Oleksii Filonenko
Oleksii Filonenko

🌍 Orhun Parmaksız
Orhun Parmaksız

📖 📦 💵 Paco
Paco

🌍 Peter Dave Hello
Peter Dave Hello

🌍 Phil Clifford
Phil Clifford

📦 Quetzal-coalt
Quetzal-coalt

🌍 - Raghav Potluri
Raghav Potluri

📦 + Raghav Potluri
Raghav Potluri

📦 Recall.ai
Recall.ai

💵 Ron
Ron

🤔 Safaraliev Maxim
Safaraliev Maxim

🌍 Shawn Yeager
Shawn Yeager

💵 SignPath GmbH
SignPath GmbH

📦 The Artifex
The Artifex

🌍 📦 - Tony
Tony

💵 + Tony
Tony

💵 Trịnh Duy Hưng
Trịnh Duy Hưng

🌍 TyseEX
TyseEX

🐛 Victor Nilsson
Victor Nilsson

🌍 💻 Wang Zishi
Wang Zishi

🌍 Yevhen
Yevhen

🌍 Ylva
Ylva

🌍 - ZEROF
ZEROF

💵 + ZEROF
ZEROF

💵 ZeroDot1
ZeroDot1

🎨 ️️️️♿️ clr
clr

📖 🌍 edwloef
edwloef

🐛 ervinpopescu
ervinpopescu

🌍 glitsj16
glitsj16

📦 guilherme-demarchi
guilherme-demarchi

🌍 - hirotake111
hirotake111

🌍 + hirotake111
hirotake111

🌍 islameehassan
islameehassan

💻 louis-ym4
louis-ym4

🎨 luca3s
luca3s

🌍 mbe57
mbe57

🤔 pia
pia

🌍 pin
pin

📦 - shu-kitamura
shu-kitamura

💻 🌍 + shu-kitamura
shu-kitamura

💻 🌍 starccy
starccy

💻 tiansheng li
tiansheng li

💵 vtiinanen
vtiinanen

🌍 From 8dad7213819c6095cb8c7f1d08b15ea787c8f7b2 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Wed, 6 May 2026 22:42:25 +0200 Subject: [PATCH 03/23] fix various minor bugs --- src/gui/pages/inspect_page.rs | 21 ++++++++------------- src/networking/manage_packets.rs | 2 +- src/notifications/notify_and_log.rs | 2 +- src/report/types/search_parameters.rs | 8 ++------ src/utils/formatted_strings.rs | 2 +- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs index f8a06cd5e..99037ca8e 100644 --- a/src/gui/pages/inspect_page.rs +++ b/src/gui/pages/inspect_page.rs @@ -35,6 +35,7 @@ use crate::translations::translations_2::{ only_show_favorites_translation, showing_results_translation, }; use crate::translations::translations_5::{only_show_blacklisted_translation, program_translation}; +use crate::utils::formatted_strings::clip_text; use crate::utils::types::icon::Icon; use crate::{Language, RunningPage, Sniffer, StyleType}; @@ -261,16 +262,9 @@ fn row_report_entry<'a>( let max_chars = report_col.get_max_chars(None); let col_value = report_col.get_value(key, val, data_repr); ret_val = ret_val.push( - Container::new( - Text::new(if col_value.len() <= max_chars { - col_value - } else { - [&col_value[..max_chars - 2], "…"].concat() - }) - .class(text_type), - ) - .align_x(Alignment::Center) - .width(report_col.get_width()), + Container::new(Text::new(clip_text(&col_value, max_chars)).class(text_type)) + .align_x(Alignment::Center) + .width(report_col.get_width()), ); } ret_val @@ -412,7 +406,7 @@ fn filter_input<'a>( let mut input = TextInput::new("", filter_value) .on_input(move |new_value| { - Message::Search(filter_input_type.new_search(&search_params, new_value)) + Message::Search(filter_input_type.new_search(&search_params, &new_value)) }) .padding([2, 5]) .size(FONT_SIZE_FOOTER) @@ -462,8 +456,9 @@ fn filter_combobox( let button_clear = button_clear_filter(filter_input_type.clear_search(&search_params)); - let update_fn = - move |new_value| Message::Search(filter_input_type.new_search(&search_params, new_value)); + let update_fn = move |new_value: String| { + Message::Search(filter_input_type.new_search(&search_params, &new_value)) + }; let mut combobox = ComboBox::new(combo_box_state, "", Some(&filter_value), update_fn.clone()) .on_input(update_fn) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 00013d2dc..bceba1885 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -124,7 +124,7 @@ fn analyze_network_header( Some(NetHeaders::Ipv6(ipv6header, _)) => { *address1 = IpAddr::from(ipv6header.source); *address2 = IpAddr::from(ipv6header.destination); - *exchanged_bytes += u128::from(40 + ipv6header.payload_length); + *exchanged_bytes += u128::from(ipv6header.payload_length) + 40; true } Some(NetHeaders::Arp(arp_packet)) => { diff --git a/src/notifications/notify_and_log.rs b/src/notifications/notify_and_log.rs index 09062c22e..64430d3f8 100644 --- a/src/notifications/notify_and_log.rs +++ b/src/notifications/notify_and_log.rs @@ -43,7 +43,7 @@ pub fn notify_and_log( let notification = LoggedNotification::DataThresholdExceeded(DataThresholdExceeded { id: logged_notifications.tot(), data_repr, - threshold: notifications.data_notification.previous_threshold, + threshold, data_info, timestamp: get_formatted_timestamp(timestamp), is_expanded: false, diff --git a/src/report/types/search_parameters.rs b/src/report/types/search_parameters.rs index f2db0efdc..ec2ce6fa5 100644 --- a/src/report/types/search_parameters.rs +++ b/src/report/types/search_parameters.rs @@ -236,11 +236,7 @@ impl FilterInputType { result } - pub fn new_search( - self, - search_params: &SearchParameters, - new_value: String, - ) -> SearchParameters { + pub fn new_search(self, search_params: &SearchParameters, new_value: &str) -> SearchParameters { let mut result = search_params.clone(); let trimmed = new_value.trim().to_string(); match self { @@ -252,7 +248,7 @@ impl FilterInputType { FilterInputType::Service => result.service = trimmed, FilterInputType::Domain => result.domain = trimmed, FilterInputType::Country => result.country = trimmed, - FilterInputType::AsName => result.as_name = new_value, + FilterInputType::AsName => result.as_name = trimmed, FilterInputType::Program => result.program = trimmed, } result diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index 24510ed04..9c0591c26 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -145,7 +145,7 @@ pub fn clip_text(text: &str, max_chars: usize) -> String { let suspensions = if tot_len > max_chars { "…" } else { "" }; let slice = if tot_len > max_chars { - &chars[..slice_len - 2] + &chars[..slice_len.saturating_sub(2)] } else { &chars[..slice_len] } From 9b9dcfc010249c6a1466ef1555f7a83aaa58734e Mon Sep 17 00:00:00 2001 From: HangryNugget Date: Wed, 6 May 2026 23:04:38 +0200 Subject: [PATCH 04/23] feat: support CIDR ranges in imported IP blacklists --- CHANGELOG.md | 1 + Cargo.lock | 1 + Cargo.toml | 1 + README.md | 3 +- .../test/ip_blacklist_real_cidr_ranges.txt | 6 + .../test/ip_blacklist_valid_cidr_only.txt | 1 + .../test/ip_blacklist_valid_with_cidr.txt | 9 ++ src/networking/manage_packets.rs | 57 ++++++++- src/networking/types/ip_blacklist.rs | 114 +++++++++++++++++- 9 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 resources/test/ip_blacklist_real_cidr_ranges.txt create mode 100644 resources/test/ip_blacklist_valid_cidr_only.txt create mode 100644 resources/test/ip_blacklist_valid_with_cidr.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 032cdf20b..1bb318dc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All Sniffnet releases with the relative changes are documented in this file. - Greek ([#1175](https://github.com/GyulyVGC/sniffnet/pull/1175)) - Fix unseen hosts and services not appearing dimmed in thumbnail mode (fixes [#1142](https://github.com/GyulyVGC/sniffnet/issues/1142)) - Fix `X-AppImage-Version` field in Linux AppImage metadata to correctly reflect the app version ([#1174](https://github.com/GyulyVGC/sniffnet/pull/1174) — fixes [#1003](https://github.com/GyulyVGC/sniffnet/issues/1003)) +- Support CIDR ranges in imported IP blacklists. ## [1.5.0] - 2026-04-14 - Show which apps and programs are generating network traffic ([#1056](https://github.com/GyulyVGC/sniffnet/pull/1056) — fixes [#170](https://github.com/GyulyVGC/sniffnet/issues/170)) diff --git a/Cargo.lock b/Cargo.lock index 93d011619..d54ab5250 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5007,6 +5007,7 @@ dependencies = [ "etherparse", "gag", "iced", + "ipnet", "listeners", "maxminddb", "pcap", diff --git a/Cargo.toml b/Cargo.toml index 92211d3fb..2d9d7c41d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ ctrlc = { version = "3.5.2", features = ["termination"] } dns-lookup = "3.0.1" etherparse = "0.20.1" iced = { version = "0.14.0", features = ["tokio", "svg", "advanced", "lazy", "image"] } +ipnet = "2.12.0" listeners = "0.5.1" maxminddb = "0.28.1" pcap = "2.4.0" diff --git a/README.md b/README.md index 74e368bcc..4847e1a3e 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Not what you're looking for? Check out [alternative installation methods](https: - 📌 keep an eye on your network even when the application is **minimized** - ️🔎 search and **inspect** each of your network connections in real time - 🔉 set custom **notifications** to inform you when defined network events occur -- 🚫 import custom **IP blacklists** to highlight potentially dangerous connections +- 🚫 import custom **IP blacklists** with exact IPs or CIDR ranges to highlight potentially dangerous connections - 🎨 choose the **style** that fits you the most, including custom themes support - ...and more! @@ -195,4 +195,3 @@ Follow the news and Sniffnet soci - diff --git a/resources/test/ip_blacklist_real_cidr_ranges.txt b/resources/test/ip_blacklist_real_cidr_ranges.txt new file mode 100644 index 000000000..a290dc79e --- /dev/null +++ b/resources/test/ip_blacklist_real_cidr_ranges.txt @@ -0,0 +1,6 @@ +209.186.20.0/22 +209.186.24.0/21 +209.186.224.0/21 +209.186.232.0/22 +209.186.236.0/24 +209.233.156.0/22 diff --git a/resources/test/ip_blacklist_valid_cidr_only.txt b/resources/test/ip_blacklist_valid_cidr_only.txt new file mode 100644 index 000000000..fbac3bf34 --- /dev/null +++ b/resources/test/ip_blacklist_valid_cidr_only.txt @@ -0,0 +1 @@ +1.2.3.0/24 diff --git a/resources/test/ip_blacklist_valid_with_cidr.txt b/resources/test/ip_blacklist_valid_with_cidr.txt new file mode 100644 index 000000000..b34702816 --- /dev/null +++ b/resources/test/ip_blacklist_valid_with_cidr.txt @@ -0,0 +1,9 @@ +8.8.8.8 +2001:db8::1 +1.2.3.0/24 +2001:db9::/32 # IPv6 test network +5.6.7.0/24 ; SBL123456 +9.9.9.0/24 # inline hash comment +# full-line hash comment +; full-line semicolon comment +invalid text that should be ignored diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index bceba1885..73fd0db77 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -534,9 +534,14 @@ mod tests { use crate::Service; use crate::networking::manage_packets::{ get_service, get_traffic_direction, get_traffic_type, is_local_connection, - mac_from_dec_to_hex, + mac_from_dec_to_hex, modify_or_insert_in_map, }; use crate::networking::types::address_port_pair::AddressPortPair; + use crate::networking::types::arp_type::ArpType; + use crate::networking::types::capture_context::{CaptureSource, MyPcapImport}; + use crate::networking::types::icmp_type::IcmpType; + use crate::networking::types::info_traffic::InfoTraffic; + use crate::networking::types::ip_blacklist::IpBlacklist; use crate::networking::types::service_query::ServiceQuery; use crate::networking::types::traffic_direction::TrafficDirection; use crate::networking::types::traffic_type::TrafficType; @@ -1534,6 +1539,56 @@ mod tests { } } + #[tokio::test] + async fn test_cidr_blacklist_marks_remote_address_blacklisted() { + let blacklist = + IpBlacklist::from_file("resources/test/ip_blacklist_real_cidr_ranges.txt".to_string()) + .await; + let mut info_traffic = InfoTraffic::default(); + let capture_source = CaptureSource::File(MyPcapImport::new(String::new())); + let key = AddressPortPair::new( + IpAddr::from_str("192.168.1.10").unwrap(), + Some(50000), + IpAddr::from_str("209.186.21.7").unwrap(), + Some(443), + Protocol::TCP, + ); + + modify_or_insert_in_map( + &mut info_traffic, + &key, + &capture_source, + (None, None), + IcmpType::default(), + ArpType::default(), + 100, + &blacklist, + ); + + assert!(info_traffic.map.get(&key).unwrap().is_blacklisted); + + let key = AddressPortPair::new( + IpAddr::from_str("192.168.1.10").unwrap(), + Some(50001), + IpAddr::from_str("209.186.237.1").unwrap(), + Some(443), + Protocol::TCP, + ); + + modify_or_insert_in_map( + &mut info_traffic, + &key, + &capture_source, + (None, None), + IcmpType::default(), + ArpType::default(), + 100, + &blacklist, + ); + + assert!(!info_traffic.map.get(&key).unwrap().is_blacklisted); + } + #[test] fn test_get_service_unknown() { let unknown_port_1 = Some(39332); diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index eed1c165c..d3f4c3672 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -2,9 +2,12 @@ use std::collections::HashSet; use std::net::IpAddr; use std::sync::Arc; +use ipnet::IpNet; + #[derive(Clone, Default, Debug)] pub struct IpBlacklist { ips: Arc>, + networks: Arc>, is_loading: bool, } @@ -13,24 +16,32 @@ impl IpBlacklist { let Ok(buf) = tokio::fs::read_to_string(&path).await else { return IpBlacklist::default(); }; - let mut set = HashSet::new(); + let mut ips = HashSet::new(); + let mut networks = Vec::new(); for line in buf.lines() { - if let Ok(ip) = line.trim().parse::() { - set.insert(ip); + let Some(line) = clean_blacklist_line(line) else { + continue; + }; + + if let Ok(ip) = line.parse::() { + ips.insert(ip); + } else if let Ok(network) = line.parse::() { + networks.push(network); } } IpBlacklist { - ips: Arc::new(set), + ips: Arc::new(ips), + networks: Arc::new(networks), is_loading: false, } } pub fn contains(&self, ip: &IpAddr) -> bool { - self.ips.contains(ip) + self.ips.contains(ip) || self.networks.iter().any(|network| network.contains(ip)) } pub fn is_invalid(&self) -> bool { - self.ips.is_empty() && !self.is_loading + self.ips.is_empty() && self.networks.is_empty() && !self.is_loading } pub fn is_loading(&self) -> bool { @@ -42,6 +53,20 @@ impl IpBlacklist { } } +fn clean_blacklist_line(line: &str) -> Option<&str> { + let line = line.trim(); + + if line.is_empty() || line.starts_with('#') || line.starts_with(';') { + return None; + } + + let line = line.split(';').next().unwrap_or(line); + let line = line.split('#').next().unwrap_or(line); + let line = line.trim(); + + if line.is_empty() { None } else { Some(line) } +} + #[cfg(test)] mod tests { use super::*; @@ -55,6 +80,7 @@ mod tests { assert!(!blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 4); + assert_eq!(blacklist.networks.len(), 0); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255)))); @@ -75,10 +101,86 @@ mod tests { assert!(blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); + assert_eq!(blacklist.networks.len(), 0); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))); assert!(!blacklist.contains(&"::123".parse::().unwrap())); assert!(!blacklist.contains(&"::".parse::().unwrap())); } + + #[tokio::test] + async fn test_ip_blacklist_valid_with_cidr() { + let blacklist = + IpBlacklist::from_file("resources/test/ip_blacklist_valid_with_cidr.txt".to_string()) + .await; + + assert!(!blacklist.is_invalid()); + assert!(!blacklist.is_loading()); + assert_eq!(blacklist.ips.len(), 2); + assert_eq!(blacklist.networks.len(), 4); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); + assert!(blacklist.contains(&"2001:db8::1".parse::().unwrap())); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 9)))); + assert!(!blacklist.contains(&"2001:db8::2".parse::().unwrap())); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 5, 1)))); + + assert!(blacklist.contains(&"2001:db9::1".parse::().unwrap())); + assert!(blacklist.contains(&"2001:db9:ffff::1".parse::().unwrap())); + assert!(!blacklist.contains(&"2001:dba::1".parse::().unwrap())); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(5, 6, 7, 10)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(5, 6, 8, 10)))); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(9, 9, 9, 9)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(9, 9, 10, 9)))); + } + + #[tokio::test] + async fn test_ip_blacklist_valid_with_cidr_only() { + let blacklist = + IpBlacklist::from_file("resources/test/ip_blacklist_valid_cidr_only.txt".to_string()) + .await; + + assert!(!blacklist.is_invalid()); + assert!(!blacklist.is_loading()); + assert_eq!(blacklist.ips.len(), 0); + assert_eq!(blacklist.networks.len(), 1); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 4, 1)))); + } + + #[tokio::test] + async fn test_ip_blacklist_real_cidr_ranges() { + let blacklist = + IpBlacklist::from_file("resources/test/ip_blacklist_real_cidr_ranges.txt".to_string()) + .await; + + assert!(!blacklist.is_invalid()); + assert_eq!(blacklist.ips.len(), 0); + assert_eq!(blacklist.networks.len(), 6); + + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 20, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 23, 255)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 24, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 31, 255)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 224, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 231, 255)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 232, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 235, 255)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 236, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 236, 255)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 156, 0)))); + assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 159, 255)))); + + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 19, 255)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 32, 0)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 237, 0)))); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 160, 0)))); + } } From 495377d01022a1ab3968bc9d8b706a4c8ef49d81 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Thu, 7 May 2026 20:04:58 +0200 Subject: [PATCH 05/23] replace IpCollection with ipnet::IpNet for use with bogons --- CHANGELOG.md | 2 +- README.md | 2 +- src/networking/types/bogon.rs | 67 +++--- src/networking/types/ip_collection.rs | 324 -------------------------- src/networking/types/mod.rs | 1 - 5 files changed, 36 insertions(+), 360 deletions(-) delete mode 100644 src/networking/types/ip_collection.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bb318dc8..588f1893e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] +- Support CIDR ranges in imported IP blacklists ([#1181](https://github.com/GyulyVGC/sniffnet/pull/1181)) - Updated some of the existing translations to v1.5: - Ukrainian ([#1039](https://github.com/GyulyVGC/sniffnet/pull/1039)) - German ([#1122](https://github.com/GyulyVGC/sniffnet/pull/1122)) @@ -13,7 +14,6 @@ All Sniffnet releases with the relative changes are documented in this file. - Greek ([#1175](https://github.com/GyulyVGC/sniffnet/pull/1175)) - Fix unseen hosts and services not appearing dimmed in thumbnail mode (fixes [#1142](https://github.com/GyulyVGC/sniffnet/issues/1142)) - Fix `X-AppImage-Version` field in Linux AppImage metadata to correctly reflect the app version ([#1174](https://github.com/GyulyVGC/sniffnet/pull/1174) — fixes [#1003](https://github.com/GyulyVGC/sniffnet/issues/1003)) -- Support CIDR ranges in imported IP blacklists. ## [1.5.0] - 2026-04-14 - Show which apps and programs are generating network traffic ([#1056](https://github.com/GyulyVGC/sniffnet/pull/1056) — fixes [#170](https://github.com/GyulyVGC/sniffnet/issues/170)) diff --git a/README.md b/README.md index 4847e1a3e..918416ee4 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ Not what you're looking for? Check out [alternative installation methods](https: - 📌 keep an eye on your network even when the application is **minimized** - ️🔎 search and **inspect** each of your network connections in real time - 🔉 set custom **notifications** to inform you when defined network events occur -- 🚫 import custom **IP blacklists** with exact IPs or CIDR ranges to highlight potentially dangerous connections +- 🚫 import custom **IP blacklists** to highlight potentially dangerous connections - 🎨 choose the **style** that fits you the most, including custom themes support - ...and more! diff --git a/src/networking/types/bogon.rs b/src/networking/types/bogon.rs index d21fa2606..fc26a6afc 100644 --- a/src/networking/types/bogon.rs +++ b/src/networking/types/bogon.rs @@ -1,133 +1,134 @@ -use crate::networking::types::ip_collection::IpCollection; +use ipnet::IpNet; use std::net::IpAddr; pub struct Bogon { - pub range: IpCollection, + pub ranges: Vec, pub description: &'static str, } // IPv4 bogons static THIS_NETWORK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("0.0.0.0-0.255.255.255").unwrap(), + ranges: vec!["0.0.0.0/8".parse().unwrap()], description: "\"this\" network", }); static PRIVATE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new( - "10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255", - ) - .unwrap(), + ranges: vec![ + "10.0.0.0/8".parse().unwrap(), + "172.16.0.0/12".parse().unwrap(), + "192.168.0.0/16".parse().unwrap(), + ], description: "private-use", }); static CARRIER_GRADE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("100.64.0.0-100.127.255.255").unwrap(), + ranges: vec!["100.64.0.0/10".parse().unwrap()], description: "carrier-grade NAT", }); static LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("127.0.0.0-127.255.255.255").unwrap(), + ranges: vec!["127.0.0.0/8".parse().unwrap()], description: "loopback", }); static LINK_LOCAL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("169.254.0.0-169.254.255.255").unwrap(), + ranges: vec!["169.254.0.0/16".parse().unwrap()], description: "link local", }); static IETF_PROTOCOL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("192.0.0.0-192.0.0.255").unwrap(), + ranges: vec!["192.0.0.0/24".parse().unwrap()], description: "IETF protocol assignments", }); static TEST_NET_1: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("192.0.2.0-192.0.2.255").unwrap(), + ranges: vec!["192.0.2.0/24".parse().unwrap()], description: "TEST-NET-1", }); static NETWORK_INTERCONNECT: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("198.18.0.0-198.19.255.255").unwrap(), + ranges: vec!["198.18.0.0/15".parse().unwrap()], description: "network interconnect device benchmark testing", }); static TEST_NET_2: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("198.51.100.0-198.51.100.255").unwrap(), + ranges: vec!["198.51.100.0/24".parse().unwrap()], description: "TEST-NET-2", }); static TEST_NET_3: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("203.0.113.0-203.0.113.255").unwrap(), + ranges: vec!["203.0.113.0/24".parse().unwrap()], description: "TEST-NET-3", }); static MULTICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("224.0.0.0-239.255.255.255").unwrap(), + ranges: vec!["224.0.0.0/4".parse().unwrap()], description: "multicast", }); static FUTURE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("240.0.0.0-255.255.255.255").unwrap(), + ranges: vec!["240.0.0.0/4".parse().unwrap()], description: "future use", }); // IPv6 bogons static NODE_SCOPE_UNSPECIFIED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("::").unwrap(), + ranges: vec!["::/128".parse().unwrap()], description: "node-scope unicast unspecified", }); static NODE_SCOPE_LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("::1").unwrap(), + ranges: vec!["::1/128".parse().unwrap()], description: "node-scope unicast loopback", }); static IPV4_MAPPED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(), + ranges: vec!["::ffff:0.0.0.0/96".parse().unwrap()], description: "IPv4-mapped", }); static IPV4_COMPATIBLE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("::-::255.255.255.255").unwrap(), + ranges: vec!["::/96".parse().unwrap()], description: "IPv4-compatible", }); static REMOTELY_TRIGGERED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["100::/64".parse().unwrap()], description: "remotely triggered black hole", }); static ORCHID: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["2001:10::/28".parse().unwrap()], description: "ORCHID", }); -static DOCUMENTATION_PREFIX: std::sync::LazyLock = std::sync::LazyLock::new(|| { - Bogon { - range: IpCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff") - .unwrap(), +static DOCUMENTATION_PREFIX: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { + ranges: vec![ + "2001:db8::/32".parse().unwrap(), + "3fff::/20".parse().unwrap(), + ], description: "documentation prefix", -} }); static ULA: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["fc00::/7".parse().unwrap()], description: "ULA", }); static LINK_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["fe80::/10".parse().unwrap()], description: "link-local unicast", }); static SITE_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["fec0::/10".parse().unwrap()], description: "site-local unicast", }); static MULTICAST_V6: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: IpCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + ranges: vec!["ff00::/8".parse().unwrap()], description: "multicast v6", }); @@ -163,7 +164,7 @@ static BOGONS: std::sync::LazyLock> = std::sync::LazyLock::n pub fn is_bogon(address: &IpAddr) -> Option<&'static str> { for bogon in BOGONS.iter() { - if bogon.range.contains(address) { + if bogon.ranges.iter().any(|net| net.contains(address)) { return Some(bogon.description); } } diff --git a/src/networking/types/ip_collection.rs b/src/networking/types/ip_collection.rs deleted file mode 100644 index 09b0d327e..000000000 --- a/src/networking/types/ip_collection.rs +++ /dev/null @@ -1,324 +0,0 @@ -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use std::ops::RangeInclusive; -use std::str::FromStr; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct IpCollection { - ips: Vec, - ranges: Vec>, -} - -impl IpCollection { - const SEPARATOR: char = ','; - const RANGE_SEPARATOR: char = '-'; - - pub(crate) fn new(str: &str) -> Option { - let str = str.replace(' ', ""); - - if str.is_empty() { - return Some(Self::default()); - } - - let mut ips = Vec::new(); - let mut ranges = Vec::new(); - - let objects: Vec<&str> = str.split(Self::SEPARATOR).collect(); - for object in objects { - if object.contains(Self::RANGE_SEPARATOR) { - // IP range - let mut subparts = object.split(Self::RANGE_SEPARATOR); - if subparts.clone().count() != 2 { - return None; - } - let (lower_str, upper_str) = - (subparts.next().unwrap_or(""), subparts.next().unwrap_or("")); - let lower_ip = IpAddr::from_str(lower_str).ok()?; - let upper_ip = IpAddr::from_str(upper_str).ok()?; - let range = RangeInclusive::new(lower_ip, upper_ip); - if range.is_empty() || lower_ip.is_ipv4() != upper_ip.is_ipv4() { - return None; - } - ranges.push(range); - } else { - // individual IP - let ip = IpAddr::from_str(object).ok()?; - ips.push(ip); - } - } - - Some(Self { ips, ranges }) - } - - pub(crate) fn contains(&self, ip: &IpAddr) -> bool { - for range in &self.ranges { - if range.contains(ip) { - return true; - } - } - self.ips.contains(ip) - } -} - -impl Default for IpCollection { - fn default() -> Self { - IpCollection { - ips: vec![], - ranges: vec![ - RangeInclusive::new( - IpAddr::V4(Ipv4Addr::UNSPECIFIED), - IpAddr::from([255, 255, 255, 255]), - ), - RangeInclusive::new( - IpAddr::V6(Ipv6Addr::UNSPECIFIED), - IpAddr::from([ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, - ]), - ), - ], - } - } -} - -#[cfg(test)] -mod tests { - use std::net::IpAddr; - use std::ops::RangeInclusive; - use std::str::FromStr; - - use crate::networking::types::ip_collection::IpCollection; - - #[test] - fn test_default_collection_contains_everything() { - let collection = IpCollection::default(); - assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap())); - assert!(collection.contains(&IpAddr::from_str("0.0.0.0").unwrap())); - assert!(collection.contains(&IpAddr::from_str("255.255.255.255").unwrap())); - assert!(collection.contains(&IpAddr::from_str("192.168.1.1").unwrap())); - assert!( - collection - .contains(&IpAddr::from_str("0000:0000:0000:0000:0000:0000:0000:0000").unwrap()) - ); - assert!( - collection - .contains(&IpAddr::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - assert!(collection.contains(&IpAddr::from_str("88::02").unwrap())); - assert!( - collection - .contains(&IpAddr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:eeee").unwrap()) - ); - } - - #[test] - fn test_new_collections_1() { - assert_eq!( - IpCollection::new("1.1.1.1,2.2.2.2").unwrap(), - IpCollection { - ips: vec![ - IpAddr::from_str("1.1.1.1").unwrap(), - IpAddr::from_str("2.2.2.2").unwrap() - ], - ranges: vec![] - } - ); - - assert_eq!( - IpCollection::new("1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",) - .unwrap(), - IpCollection { - ips: vec![ - IpAddr::from_str("1.1.1.1").unwrap(), - IpAddr::from_str("2.2.2.2").unwrap(), - IpAddr::from_str("9.9.9.9").unwrap() - ], - ranges: vec![ - RangeInclusive::new( - IpAddr::from_str("3.3.3.3").unwrap(), - IpAddr::from_str("5.5.5.5").unwrap() - ), - RangeInclusive::new( - IpAddr::from_str("10.0.0.1").unwrap(), - IpAddr::from_str("10.0.0.255").unwrap() - ) - ] - } - ); - - assert_eq!( - IpCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(), - IpCollection { - ips: vec![IpAddr::from_str("aaaa::ffff").unwrap(),], - ranges: vec![RangeInclusive::new( - IpAddr::from_str("bbbb::1").unwrap(), - IpAddr::from_str("cccc::2").unwrap() - )] - } - ); - } - - #[test] - fn test_new_collections_2() { - assert_eq!( - IpCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(), - IpCollection { - ips: vec![ - IpAddr::from_str("1.1.1.1").unwrap(), - IpAddr::from_str("2.2.2.2").unwrap(), - IpAddr::from_str("8.8.8.8").unwrap() - ], - ranges: vec![] - } - ); - - assert_eq!( - IpCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(), - IpCollection { - ips: vec![], - ranges: vec![RangeInclusive::new( - IpAddr::from_str("1.1.1.1").unwrap(), - IpAddr::from_str("1.1.1.1").unwrap() - ),] - } - ); - - assert_eq!( - IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",) - .unwrap(), - IpCollection { - ips: vec![ - IpAddr::from_str("1.1.1.1").unwrap(), - IpAddr::from_str("2.2.2.2").unwrap(), - IpAddr::from_str("9.9.9.9").unwrap() - ], - ranges: vec![ - RangeInclusive::new( - IpAddr::from_str("3.3.3.3").unwrap(), - IpAddr::from_str("5.5.5.5").unwrap() - ), - RangeInclusive::new( - IpAddr::from_str("10.0.0.1").unwrap(), - IpAddr::from_str("10.0.0.255").unwrap() - ) - ] - } - ); - - assert_eq!( - IpCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(), - IpCollection { - ips: vec![ - IpAddr::from_str("aaaa::ffff").unwrap(), - IpAddr::from_str("ff::dd").unwrap() - ], - ranges: vec![RangeInclusive::new( - IpAddr::from_str("bbbb::1").unwrap(), - IpAddr::from_str("cccc::2").unwrap() - )] - } - ); - } - - #[test] - fn test_new_collections_invalid() { - assert_eq!( - IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"), - None - ); - - assert_eq!( - IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"), - None - ); - - assert_eq!(IpCollection::new("1.1.1.1-aa::ff"), None); - - assert_eq!(IpCollection::new("aa::ff-1.1.1.1"), None); - - assert_eq!(IpCollection::new("aa::ff-aa::ee"), None); - - assert_eq!(IpCollection::new("1.1.1.1-1.1.0.1"), None); - - assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None); - - assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-"), None); - } - - #[test] - fn test_ip_collection_contains() { - let collection = - IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9") - .unwrap(); - assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap())); - assert!(collection.contains(&IpAddr::from_str("2.2.2.2").unwrap())); - assert!(collection.contains(&IpAddr::from_str("3.3.3.3").unwrap())); - assert!(collection.contains(&IpAddr::from_str("4.0.0.0").unwrap())); - assert!(collection.contains(&IpAddr::from_str("5.5.5.5").unwrap())); - assert!(collection.contains(&IpAddr::from_str("9.9.9.9").unwrap())); - assert!(collection.contains(&IpAddr::from_str("10.0.0.1").unwrap())); - assert!(collection.contains(&IpAddr::from_str("10.0.0.128").unwrap())); - assert!(collection.contains(&IpAddr::from_str("10.0.0.255").unwrap())); - assert!(!collection.contains(&IpAddr::from_str("10.0.0.0").unwrap())); - assert!(!collection.contains(&IpAddr::from_str("2.2.2.1").unwrap())); - assert!(!collection.contains(&IpAddr::from_str("9.9.9.10").unwrap())); - assert!(!collection.contains(&IpAddr::from_str("3.3.3.2").unwrap())); - - let collection_2 = IpCollection::new("1.1.1.0-1.1.9.0").unwrap(); - assert!(!collection_2.contains(&IpAddr::from_str("1.1.100.5").unwrap())); - assert!(collection_2.contains(&IpAddr::from_str("1.1.3.255").unwrap())); - - // check that ipv4 range doesn't contain ipv6 - let collection_3 = IpCollection::new("0.0.0.0-255.255.255.255").unwrap(); - assert!(!collection_3.contains(&IpAddr::from_str("::").unwrap())); - assert!(!collection_3.contains(&IpAddr::from_str("1111::2222").unwrap())); - } - - #[test] - fn test_ip_collection_contains_ipv6() { - let collection = - IpCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap(); - assert!( - collection - .contains(&IpAddr::from_str("2001:db8:1234:0000:0000:0000:0000:0000").unwrap()) - ); - assert!( - collection - .contains(&IpAddr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - assert!( - collection - .contains(&IpAddr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:eeee").unwrap()) - ); - assert!( - collection - .contains(&IpAddr::from_str("2001:db8:1234:aaaa:ffff:ffff:ffff:eeee").unwrap()) - ); - assert!(collection.contains(&IpAddr::from_str("daa::aad").unwrap())); - assert!(collection.contains(&IpAddr::from_str("caa::aac").unwrap())); - assert!( - !collection - .contains(&IpAddr::from_str("2000:db8:1234:0000:0000:0000:0000:0000").unwrap()) - ); - assert!( - !collection - .contains(&IpAddr::from_str("2001:db8:1235:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - assert!( - !collection - .contains(&IpAddr::from_str("2001:eb8:1234:ffff:ffff:ffff:ffff:eeee").unwrap()) - ); - assert!(!collection.contains(&IpAddr::from_str("da::aad").unwrap())); - assert!(!collection.contains(&IpAddr::from_str("caa::aab").unwrap())); - - let collection_2 = IpCollection::new("aa::bb-aa:1::00").unwrap(); - assert!(!collection_2.contains(&IpAddr::from_str("aa:11::0").unwrap())); - assert!(collection_2.contains(&IpAddr::from_str("aa::bc").unwrap())); - assert!(collection_2.contains(&IpAddr::from_str("aa::bbcc").unwrap())); - assert!(collection_2.contains(&IpAddr::from_str("00aa:0001::00").unwrap())); - - // check that ipv6 range doesn't contain ipv4 - let collection_3 = IpCollection::new("0000::0000-ffff::8888").unwrap(); - assert!(!collection_3.contains(&IpAddr::from_str("192.168.1.1").unwrap())); - assert!(!collection_3.contains(&IpAddr::from_str("0.0.0.0").unwrap())); - } -} diff --git a/src/networking/types/mod.rs b/src/networking/types/mod.rs index 0a7b1b425..5c3418867 100644 --- a/src/networking/types/mod.rs +++ b/src/networking/types/mod.rs @@ -13,7 +13,6 @@ pub mod icmp_type; pub mod info_address_port_pair; pub mod info_traffic; pub mod ip_blacklist; -pub mod ip_collection; pub mod my_device; pub mod my_link_type; pub mod program; From 90649c68cf8080e918d5cda8826b1b7406dccd06 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Thu, 7 May 2026 20:27:46 +0200 Subject: [PATCH 06/23] simplify blacklist line parsing to take first whitespace-separated token --- src/networking/types/ip_blacklist.rs | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index d3f4c3672..a502f5871 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -19,13 +19,13 @@ impl IpBlacklist { let mut ips = HashSet::new(); let mut networks = Vec::new(); for line in buf.lines() { - let Some(line) = clean_blacklist_line(line) else { + let Some(first) = line.split_whitespace().next() else { continue; }; - if let Ok(ip) = line.parse::() { + if let Ok(ip) = first.parse::() { ips.insert(ip); - } else if let Ok(network) = line.parse::() { + } else if let Ok(network) = first.parse::() { networks.push(network); } } @@ -53,20 +53,6 @@ impl IpBlacklist { } } -fn clean_blacklist_line(line: &str) -> Option<&str> { - let line = line.trim(); - - if line.is_empty() || line.starts_with('#') || line.starts_with(';') { - return None; - } - - let line = line.split(';').next().unwrap_or(line); - let line = line.split('#').next().unwrap_or(line); - let line = line.trim(); - - if line.is_empty() { None } else { Some(line) } -} - #[cfg(test)] mod tests { use super::*; From 6a0d5a7cff16e799931987f81c56f7e8276338b2 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Thu, 7 May 2026 21:26:06 +0200 Subject: [PATCH 07/23] perf: use prefix-trie for blacklist network lookups --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 1 + src/networking/types/ip_blacklist.rs | 9 +++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d54ab5250..2d7f7f295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3923,6 +3923,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prefix-trie" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f561214012d3fc240a1f9c817cc4d57f5310910d066069c1b093f766bb5966" +dependencies = [ + "either", + "ipnet", + "num-traits", +] + [[package]] name = "presser" version = "0.3.1" @@ -5017,6 +5028,7 @@ dependencies = [ "picon", "plotters", "plotters-iced2", + "prefix-trie", "reqwest", "rfd", "rodio", diff --git a/Cargo.toml b/Cargo.toml index 2d9d7c41d..4abb43987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ phf_shared = "0.13.1" picon = "0.1.0" plotters = { version = "0.3.7", default-features = false, features = ["area_series", "line_series"] } plotters-iced2 = "0.14.0" +prefix-trie = { version = "0.8.3", features = ["ipnet"] } reqwest = { version = "0.13.3", features = ["json"] } rfd = "0.17.2" rodio = { version = "0.22.2", default-features = false, features = ["mp3", "playback"] } diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index a502f5871..42a6d2947 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -3,11 +3,12 @@ use std::net::IpAddr; use std::sync::Arc; use ipnet::IpNet; +use prefix_trie::joint::set::JointPrefixSet; #[derive(Clone, Default, Debug)] pub struct IpBlacklist { ips: Arc>, - networks: Arc>, + networks: Arc>, is_loading: bool, } @@ -17,7 +18,7 @@ impl IpBlacklist { return IpBlacklist::default(); }; let mut ips = HashSet::new(); - let mut networks = Vec::new(); + let mut networks = JointPrefixSet::new(); for line in buf.lines() { let Some(first) = line.split_whitespace().next() else { continue; @@ -26,7 +27,7 @@ impl IpBlacklist { if let Ok(ip) = first.parse::() { ips.insert(ip); } else if let Ok(network) = first.parse::() { - networks.push(network); + networks.insert(network); } } IpBlacklist { @@ -37,7 +38,7 @@ impl IpBlacklist { } pub fn contains(&self, ip: &IpAddr) -> bool { - self.ips.contains(ip) || self.networks.iter().any(|network| network.contains(ip)) + self.ips.contains(ip) || self.networks.get_lpm(&IpNet::from(*ip)).is_some() } pub fn is_invalid(&self) -> bool { From 388c56469cb15b0eb219ed011a22f2205b3dfe95 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Thu, 7 May 2026 22:06:09 +0200 Subject: [PATCH 08/23] remove redundant test in manage_packets.rs --- src/networking/manage_packets.rs | 57 +------------------------------- 1 file changed, 1 insertion(+), 56 deletions(-) diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 73fd0db77..bceba1885 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -534,14 +534,9 @@ mod tests { use crate::Service; use crate::networking::manage_packets::{ get_service, get_traffic_direction, get_traffic_type, is_local_connection, - mac_from_dec_to_hex, modify_or_insert_in_map, + mac_from_dec_to_hex, }; use crate::networking::types::address_port_pair::AddressPortPair; - use crate::networking::types::arp_type::ArpType; - use crate::networking::types::capture_context::{CaptureSource, MyPcapImport}; - use crate::networking::types::icmp_type::IcmpType; - use crate::networking::types::info_traffic::InfoTraffic; - use crate::networking::types::ip_blacklist::IpBlacklist; use crate::networking::types::service_query::ServiceQuery; use crate::networking::types::traffic_direction::TrafficDirection; use crate::networking::types::traffic_type::TrafficType; @@ -1539,56 +1534,6 @@ mod tests { } } - #[tokio::test] - async fn test_cidr_blacklist_marks_remote_address_blacklisted() { - let blacklist = - IpBlacklist::from_file("resources/test/ip_blacklist_real_cidr_ranges.txt".to_string()) - .await; - let mut info_traffic = InfoTraffic::default(); - let capture_source = CaptureSource::File(MyPcapImport::new(String::new())); - let key = AddressPortPair::new( - IpAddr::from_str("192.168.1.10").unwrap(), - Some(50000), - IpAddr::from_str("209.186.21.7").unwrap(), - Some(443), - Protocol::TCP, - ); - - modify_or_insert_in_map( - &mut info_traffic, - &key, - &capture_source, - (None, None), - IcmpType::default(), - ArpType::default(), - 100, - &blacklist, - ); - - assert!(info_traffic.map.get(&key).unwrap().is_blacklisted); - - let key = AddressPortPair::new( - IpAddr::from_str("192.168.1.10").unwrap(), - Some(50001), - IpAddr::from_str("209.186.237.1").unwrap(), - Some(443), - Protocol::TCP, - ); - - modify_or_insert_in_map( - &mut info_traffic, - &key, - &capture_source, - (None, None), - IcmpType::default(), - ArpType::default(), - 100, - &blacklist, - ); - - assert!(!info_traffic.map.get(&key).unwrap().is_blacklisted); - } - #[test] fn test_get_service_unknown() { let unknown_port_1 = Some(39332); From e4534b0e0f1a34edd69d2d98f839ed9a2c040b6c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 7 May 2026 22:10:16 +0200 Subject: [PATCH 09/23] docs: add TheMasterOfDisasters as a contributor for code (#1182) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index e58873eec..d589fb54b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -957,6 +957,15 @@ "contributions": [ "financial" ] + }, + { + "login": "TheMasterOfDisasters", + "name": "TheMasterOfDisasters", + "avatar_url": "https://avatars.githubusercontent.com/u/43640311?v=4", + "profile": "https://github.com/TheMasterOfDisasters", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fb6e96316..13426a582 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -105,33 +105,34 @@ A big shout-out to all the contributors of Sniffnet ([emoji key](https://allcont The Artifex
The Artifex

🌍 📦 + TheMasterOfDisasters
TheMasterOfDisasters

💻 Tony
Tony

💵 Trịnh Duy Hưng
Trịnh Duy Hưng

🌍 TyseEX
TyseEX

🐛 Victor Nilsson
Victor Nilsson

🌍 💻 Wang Zishi
Wang Zishi

🌍 Yevhen
Yevhen

🌍 - Ylva
Ylva

🌍 + Ylva
Ylva

🌍 ZEROF
ZEROF

💵 ZeroDot1
ZeroDot1

🎨 ️️️️♿️ clr
clr

📖 🌍 edwloef
edwloef

🐛 ervinpopescu
ervinpopescu

🌍 glitsj16
glitsj16

📦 - guilherme-demarchi
guilherme-demarchi

🌍 + guilherme-demarchi
guilherme-demarchi

🌍 hirotake111
hirotake111

🌍 islameehassan
islameehassan

💻 louis-ym4
louis-ym4

🎨 luca3s
luca3s

🌍 mbe57
mbe57

🤔 pia
pia

🌍 - pin
pin

📦 + pin
pin

📦 shu-kitamura
shu-kitamura

💻 🌍 starccy
starccy

💻 tiansheng li
tiansheng li

💵 From 51e03967f4c6ef0116d706ae1cc49d58f0852f76 Mon Sep 17 00:00:00 2001 From: HangryNugget Date: Fri, 8 May 2026 02:16:24 +0200 Subject: [PATCH 10/23] feat: improve IP blacklist import feedback --- src/gui/pages/settings_general_page.rs | 48 ++++++- src/networking/types/ip_blacklist.rs | 105 +++++++++++++- src/translations/translations_5.rs | 192 +++++++++++++++++++++++++ 3 files changed, 337 insertions(+), 8 deletions(-) diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs index 5424d43bd..9dc32612e 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -15,14 +15,18 @@ use crate::gui::styles::text::TextType; use crate::gui::types::message::Message; use crate::gui::types::settings::Settings; use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders}; -use crate::networking::types::ip_blacklist::IpBlacklist; +use crate::networking::types::ip_blacklist::{IpBlacklist, IpBlacklistLoadStatus}; use crate::translations::translations::language_translation; use crate::translations::translations_2::country_translation; use crate::translations::translations_3::{ mmdb_files_translation, params_not_editable_translation, zoom_translation, }; use crate::translations::translations_4::share_feedback_translation; -use crate::translations::translations_5::ip_blacklist_translation; +use crate::translations::translations_5::{ + ip_blacklist_file_read_error_translation, ip_blacklist_loaded_translation, + ip_blacklist_loading_translation, ip_blacklist_no_valid_entries_translation, + ip_blacklist_not_selected_translation, ip_blacklist_translation, +}; use crate::utils::formatted_strings::get_path_termination_string; use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; @@ -331,6 +335,8 @@ fn blacklist_selection<'a>( }; let message = Message::LoadIpBlacklist; + let status = blacklist_status_text(custom_path, ip_blacklist, language); + let status_text_type = blacklist_status_text_type(ip_blacklist); Column::new() .width(Length::Fill) @@ -363,6 +369,44 @@ fn blacklist_selection<'a>( button_clear_mmdb(message, is_editable) }), ) + .push(Text::new(status).class(status_text_type).size(12)) +} + +fn blacklist_status_text( + custom_path: &str, + ip_blacklist: &IpBlacklist, + language: Language, +) -> String { + if custom_path.is_empty() { + return ip_blacklist_not_selected_translation(language).to_string(); + } + + match ip_blacklist.status() { + IpBlacklistLoadStatus::NotSelected => { + ip_blacklist_not_selected_translation(language).to_string() + } + IpBlacklistLoadStatus::Loading => ip_blacklist_loading_translation(language).to_string(), + IpBlacklistLoadStatus::FileReadError => { + ip_blacklist_file_read_error_translation(language).to_string() + } + IpBlacklistLoadStatus::NoValidEntries { .. } => { + ip_blacklist_no_valid_entries_translation(language).to_string() + } + IpBlacklistLoadStatus::Loaded { + ip_count, + network_count, + .. + } => ip_blacklist_loaded_translation(language, *ip_count, *network_count), + } +} + +fn blacklist_status_text_type(ip_blacklist: &IpBlacklist) -> TextType { + match ip_blacklist.status() { + IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. } => { + TextType::Danger + } + _ => TextType::Standard, + } } fn button_clear_mmdb<'a>( diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index 42a6d2947..d4c087b65 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -5,20 +5,40 @@ use std::sync::Arc; use ipnet::IpNet; use prefix_trie::joint::set::JointPrefixSet; +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub enum IpBlacklistLoadStatus { + #[default] + NotSelected, + Loading, + Loaded { + ip_count: usize, + network_count: usize, + ignored_lines: usize, + }, + FileReadError, + NoValidEntries { + ignored_lines: usize, + }, +} + #[derive(Clone, Default, Debug)] pub struct IpBlacklist { ips: Arc>, networks: Arc>, - is_loading: bool, + status: IpBlacklistLoadStatus, } impl IpBlacklist { pub async fn from_file(path: String) -> Self { let Ok(buf) = tokio::fs::read_to_string(&path).await else { - return IpBlacklist::default(); + return IpBlacklist { + status: IpBlacklistLoadStatus::FileReadError, + ..IpBlacklist::default() + }; }; let mut ips = HashSet::new(); let mut networks = JointPrefixSet::new(); + let mut ignored_lines = 0; for line in buf.lines() { let Some(first) = line.split_whitespace().next() else { continue; @@ -28,12 +48,26 @@ impl IpBlacklist { ips.insert(ip); } else if let Ok(network) = first.parse::() { networks.insert(network); + } else { + ignored_lines += 1; } } + let ip_count = ips.len(); + let network_count = networks.len(); + let status = if ip_count == 0 && network_count == 0 { + IpBlacklistLoadStatus::NoValidEntries { ignored_lines } + } else { + IpBlacklistLoadStatus::Loaded { + ip_count, + network_count, + ignored_lines, + } + }; + IpBlacklist { ips: Arc::new(ips), networks: Arc::new(networks), - is_loading: false, + status, } } @@ -42,15 +76,22 @@ impl IpBlacklist { } pub fn is_invalid(&self) -> bool { - self.ips.is_empty() && self.networks.is_empty() && !self.is_loading + matches!( + self.status, + IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. } + ) } pub fn is_loading(&self) -> bool { - self.is_loading + matches!(self.status, IpBlacklistLoadStatus::Loading) + } + + pub fn status(&self) -> &IpBlacklistLoadStatus { + &self.status } pub fn start_loading(&mut self) { - self.is_loading = true; + self.status = IpBlacklistLoadStatus::Loading; } } @@ -68,6 +109,14 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 4); assert_eq!(blacklist.networks.len(), 0); + assert_eq!( + blacklist.status(), + &IpBlacklistLoadStatus::Loaded { + ip_count: 4, + network_count: 0, + ignored_lines: 4, + } + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255)))); @@ -89,6 +138,10 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); assert_eq!(blacklist.networks.len(), 0); + assert_eq!( + blacklist.status(), + &IpBlacklistLoadStatus::NoValidEntries { ignored_lines: 6 } + ); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))); @@ -106,6 +159,14 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 2); assert_eq!(blacklist.networks.len(), 4); + assert_eq!( + blacklist.status(), + &IpBlacklistLoadStatus::Loaded { + ip_count: 2, + network_count: 4, + ignored_lines: 3, + } + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&"2001:db8::1".parse::().unwrap())); @@ -137,6 +198,14 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); assert_eq!(blacklist.networks.len(), 1); + assert_eq!( + blacklist.status(), + &IpBlacklistLoadStatus::Loaded { + ip_count: 0, + network_count: 1, + ignored_lines: 0, + } + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 4, 1)))); @@ -170,4 +239,28 @@ mod tests { assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 237, 0)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 160, 0)))); } + + #[tokio::test] + async fn test_ip_blacklist_read_error() { + let blacklist = + IpBlacklist::from_file("resources/test/does_not_exist_blacklist.txt".to_string()).await; + + assert!(blacklist.is_invalid()); + assert!(!blacklist.is_loading()); + assert_eq!(blacklist.ips.len(), 0); + assert_eq!(blacklist.networks.len(), 0); + assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::FileReadError); + assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); + } + + #[test] + fn test_ip_blacklist_loading_status() { + let mut blacklist = IpBlacklist::default(); + + blacklist.start_loading(); + + assert!(blacklist.is_loading()); + assert!(!blacklist.is_invalid()); + assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::Loading); + } } diff --git a/src/translations/translations_5.rs b/src/translations/translations_5.rs index 7e67f5e2a..60a85ba47 100644 --- a/src/translations/translations_5.rs +++ b/src/translations/translations_5.rs @@ -86,6 +86,198 @@ pub fn ip_blacklist_translation(language: Language) -> &'static str { } } +pub fn ip_blacklist_not_selected_translation(language: Language) -> &'static str { + match language { + Language::EN => "No blacklist file selected.", + Language::CS => "Není vybrán žádný soubor blacklistu.", + Language::DE => "Keine Blacklist-Datei ausgewählt.", + Language::EL => "Δεν έχει επιλεγεί αρχείο λίστας αποκλεισμού.", + Language::ES => "No se ha seleccionado ningún archivo de blacklist.", + Language::FI => "Blacklist-tiedostoa ei ole valittu.", + Language::FR => "Aucun fichier de blacklist sélectionné.", + Language::ID => "Belum ada berkas daftar hitam yang dipilih.", + Language::IT => "Nessun file blacklist selezionato.", + Language::JA => "ブラックリストファイルが選択されていません。", + Language::KO => "선택된 블랙리스트 파일이 없습니다.", + Language::NL => "Geen blacklistbestand geselecteerd.", + Language::PL => "Nie wybrano pliku blacklisty.", + Language::PT => "Nenhum arquivo de blacklist selecionado.", + Language::RO => "Nu a fost selectat niciun fișier blacklist.", + Language::RU => "Файл черного списка не выбран.", + Language::SV => "Ingen svartlistfil har valts.", + Language::TR => "Kara liste dosyası seçilmedi.", + Language::UK => "Файл чорного списку не вибрано.", + Language::UZ => "Qora ro'yxat fayli tanlanmagan.", + Language::VI => "Chưa chọn tệp danh sách đen.", + Language::ZH => "未选择黑名单文件。", + Language::ZH_TW => "未選擇黑名單檔案。", + } +} + +pub fn ip_blacklist_loading_translation(language: Language) -> &'static str { + match language { + Language::EN => "Loading blacklist...", + Language::CS => "Načítá se blacklist...", + Language::DE => "Blacklist wird geladen...", + Language::EL => "Φόρτωση λίστας αποκλεισμού...", + Language::ES => "Cargando blacklist...", + Language::FI => "Ladataan blacklistia...", + Language::FR => "Chargement de la blacklist...", + Language::ID => "Memuat daftar hitam...", + Language::IT => "Caricamento blacklist...", + Language::JA => "ブラックリストを読み込み中...", + Language::KO => "블랙리스트를 불러오는 중...", + Language::NL => "Blacklist laden...", + Language::PL => "Wczytywanie blacklisty...", + Language::PT => "Carregando blacklist...", + Language::RO => "Se încarcă blacklistul...", + Language::RU => "Загрузка черного списка...", + Language::SV => "Läser in svartlistan...", + Language::TR => "Kara liste yükleniyor...", + Language::UK => "Завантаження чорного списку...", + Language::UZ => "Qora ro'yxat yuklanmoqda...", + Language::VI => "Đang tải danh sách đen...", + Language::ZH => "正在加载黑名单...", + Language::ZH_TW => "正在載入黑名單...", + } +} + +pub fn ip_blacklist_file_read_error_translation(language: Language) -> &'static str { + match language { + Language::EN => "Could not read blacklist file.", + Language::CS => "Soubor blacklistu nelze přečíst.", + Language::DE => "Blacklist-Datei konnte nicht gelesen werden.", + Language::EL => "Δεν ήταν δυνατή η ανάγνωση του αρχείου λίστας αποκλεισμού.", + Language::ES => "No se pudo leer el archivo de blacklist.", + Language::FI => "Blacklist-tiedostoa ei voitu lukea.", + Language::FR => "Impossible de lire le fichier de blacklist.", + Language::ID => "Berkas daftar hitam tidak dapat dibaca.", + Language::IT => "Impossibile leggere il file blacklist.", + Language::JA => "ブラックリストファイルを読み取れませんでした。", + Language::KO => "블랙리스트 파일을 읽을 수 없습니다.", + Language::NL => "Kan het blacklistbestand niet lezen.", + Language::PL => "Nie można odczytać pliku blacklisty.", + Language::PT => "Não foi possível ler o arquivo de blacklist.", + Language::RO => "Nu s-a putut citi fișierul blacklist.", + Language::RU => "Не удалось прочитать файл черного списка.", + Language::SV => "Kunde inte läsa svartlistfilen.", + Language::TR => "Kara liste dosyası okunamadı.", + Language::UK => "Не вдалося прочитати файл чорного списку.", + Language::UZ => "Qora ro'yxat faylini o'qib bo'lmadi.", + Language::VI => "Không thể đọc tệp danh sách đen.", + Language::ZH => "无法读取黑名单文件。", + Language::ZH_TW => "無法讀取黑名單檔案。", + } +} + +pub fn ip_blacklist_no_valid_entries_translation(language: Language) -> &'static str { + match language { + Language::EN => "No valid IP addresses or CIDR ranges found.", + Language::CS => "Nebyly nalezeny žádné platné IP adresy ani rozsahy CIDR.", + Language::DE => "Keine gültigen IP-Adressen oder CIDR-Bereiche gefunden.", + Language::EL => "Δεν βρέθηκαν έγκυρες διευθύνσεις IP ή εύρη CIDR.", + Language::ES => "No se encontraron direcciones IP ni rangos CIDR válidos.", + Language::FI => "Kelvollisia IP-osoitteita tai CIDR-alueita ei löytynyt.", + Language::FR => "Aucune adresse IP ni plage CIDR valide trouvée.", + Language::ID => "Tidak ada alamat IP atau rentang CIDR yang valid ditemukan.", + Language::IT => "Nessun indirizzo IP o intervallo CIDR valido trovato.", + Language::JA => "有効な IP アドレスまたは CIDR 範囲が見つかりませんでした。", + Language::KO => "유효한 IP 주소 또는 CIDR 범위를 찾을 수 없습니다.", + Language::NL => "Geen geldige IP-adressen of CIDR-bereiken gevonden.", + Language::PL => "Nie znaleziono prawidłowych adresów IP ani zakresów CIDR.", + Language::PT => "Nenhum endereço IP ou intervalo CIDR válido encontrado.", + Language::RO => "Nu au fost găsite adrese IP sau intervale CIDR valide.", + Language::RU => "Действительные IP-адреса или диапазоны CIDR не найдены.", + Language::SV => "Inga giltiga IP-adresser eller CIDR-intervall hittades.", + Language::TR => "Geçerli IP adresi veya CIDR aralığı bulunamadı.", + Language::UK => "Дійсні IP-адреси або діапазони CIDR не знайдено.", + Language::UZ => "Yaroqli IP manzillar yoki CIDR oraliqlari topilmadi.", + Language::VI => "Không tìm thấy địa chỉ IP hoặc dải CIDR hợp lệ.", + Language::ZH => "未找到有效的 IP 地址或 CIDR 范围。", + Language::ZH_TW => "找不到有效的 IP 位址或 CIDR 範圍。", + } +} + +pub fn ip_blacklist_loaded_translation( + language: Language, + ip_count: usize, + network_count: usize, +) -> String { + match language { + Language::EN => { + format!("Loaded {ip_count} IP addresses and {network_count} CIDR ranges.") + } + Language::CS => { + format!("Načteno {ip_count} IP adres a {network_count} rozsahů CIDR.") + } + Language::DE => { + format!("{ip_count} IP-Adressen und {network_count} CIDR-Bereiche geladen.") + } + Language::EL => { + format!("Φορτώθηκαν {ip_count} διευθύνσεις IP και {network_count} εύρη CIDR.") + } + Language::ES => { + format!("Cargadas {ip_count} direcciones IP y {network_count} rangos CIDR.") + } + Language::FI => { + format!("Ladattu {ip_count} IP-osoitetta ja {network_count} CIDR-aluetta.") + } + Language::FR => { + format!("{ip_count} adresses IP et {network_count} plages CIDR chargées.") + } + Language::ID => { + format!("Dimuat {ip_count} alamat IP dan {network_count} rentang CIDR.") + } + Language::IT => { + format!("Caricati {ip_count} indirizzi IP e {network_count} intervalli CIDR.") + } + Language::JA => { + format!( + "{ip_count} 件の IP アドレスと {network_count} 件の CIDR 範囲を読み込みました。" + ) + } + Language::KO => { + format!("IP 주소 {ip_count}개와 CIDR 범위 {network_count}개를 불러왔습니다.") + } + Language::NL => { + format!("{ip_count} IP-adressen en {network_count} CIDR-bereiken geladen.") + } + Language::PL => { + format!("Wczytano adresów IP: {ip_count}, zakresów CIDR: {network_count}.") + } + Language::PT => { + format!("Carregados {ip_count} endereços IP e {network_count} intervalos CIDR.") + } + Language::RO => { + format!("Încărcate {ip_count} adrese IP și {network_count} intervale CIDR.") + } + Language::RU => { + format!("Загружено IP-адресов: {ip_count}, диапазонов CIDR: {network_count}.") + } + Language::SV => { + format!("{ip_count} IP-adresser och {network_count} CIDR-intervall har lästs in.") + } + Language::TR => { + format!("{ip_count} IP adresi ve {network_count} CIDR aralığı yüklendi.") + } + Language::UK => { + format!("Завантажено IP-адрес: {ip_count}, діапазонів CIDR: {network_count}.") + } + Language::UZ => { + format!("{ip_count} ta IP manzil va {network_count} ta CIDR oralig'i yuklandi.") + } + Language::VI => { + format!("Đã tải {ip_count} địa chỉ IP và {network_count} dải CIDR.") + } + Language::ZH => { + format!("已加载 {ip_count} 个 IP 地址和 {network_count} 个 CIDR 范围。") + } + Language::ZH_TW => { + format!("已載入 {ip_count} 個 IP 位址和 {network_count} 個 CIDR 範圍。") + } + } +} + pub fn blacklisted_transmitted_translation(language: Language) -> &'static str { match language { Language::EN => "New data exchanged from a blacklisted IP", From 2eafa113bff834b8f0b14d7626f92f60e7a563a1 Mon Sep 17 00:00:00 2001 From: HangryNugget Date: Fri, 8 May 2026 16:11:30 +0200 Subject: [PATCH 11/23] review: limit blacklist feedback to counts --- src/gui/pages/settings_general_page.rs | 60 +++----- src/networking/types/ip_blacklist.rs | 95 +++--------- src/translations/translations_5.rs | 192 ------------------------- 3 files changed, 38 insertions(+), 309 deletions(-) diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs index 9dc32612e..348459bb9 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -15,18 +15,14 @@ use crate::gui::styles::text::TextType; use crate::gui::types::message::Message; use crate::gui::types::settings::Settings; use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders}; -use crate::networking::types::ip_blacklist::{IpBlacklist, IpBlacklistLoadStatus}; +use crate::networking::types::ip_blacklist::IpBlacklist; use crate::translations::translations::language_translation; use crate::translations::translations_2::country_translation; use crate::translations::translations_3::{ mmdb_files_translation, params_not_editable_translation, zoom_translation, }; use crate::translations::translations_4::share_feedback_translation; -use crate::translations::translations_5::{ - ip_blacklist_file_read_error_translation, ip_blacklist_loaded_translation, - ip_blacklist_loading_translation, ip_blacklist_no_valid_entries_translation, - ip_blacklist_not_selected_translation, ip_blacklist_translation, -}; +use crate::translations::translations_5::ip_blacklist_translation; use crate::utils::formatted_strings::get_path_termination_string; use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; @@ -335,10 +331,9 @@ fn blacklist_selection<'a>( }; let message = Message::LoadIpBlacklist; - let status = blacklist_status_text(custom_path, ip_blacklist, language); - let status_text_type = blacklist_status_text_type(ip_blacklist); + let loaded_counts = blacklist_loaded_counts(custom_path, ip_blacklist); - Column::new() + let mut column = Column::new() .width(Length::Fill) .spacing(5) .align_x(Alignment::Center) @@ -368,44 +363,25 @@ fn blacklist_selection<'a>( } else { button_clear_mmdb(message, is_editable) }), - ) - .push(Text::new(status).class(status_text_type).size(12)) -} + ); -fn blacklist_status_text( - custom_path: &str, - ip_blacklist: &IpBlacklist, - language: Language, -) -> String { - if custom_path.is_empty() { - return ip_blacklist_not_selected_translation(language).to_string(); + if let Some(loaded_counts) = loaded_counts { + column = column.push(Text::new(loaded_counts).class(TextType::Standard).size(12)); } - match ip_blacklist.status() { - IpBlacklistLoadStatus::NotSelected => { - ip_blacklist_not_selected_translation(language).to_string() - } - IpBlacklistLoadStatus::Loading => ip_blacklist_loading_translation(language).to_string(), - IpBlacklistLoadStatus::FileReadError => { - ip_blacklist_file_read_error_translation(language).to_string() - } - IpBlacklistLoadStatus::NoValidEntries { .. } => { - ip_blacklist_no_valid_entries_translation(language).to_string() - } - IpBlacklistLoadStatus::Loaded { - ip_count, - network_count, - .. - } => ip_blacklist_loaded_translation(language, *ip_count, *network_count), - } + column } -fn blacklist_status_text_type(ip_blacklist: &IpBlacklist) -> TextType { - match ip_blacklist.status() { - IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. } => { - TextType::Danger - } - _ => TextType::Standard, +fn blacklist_loaded_counts(custom_path: &str, ip_blacklist: &IpBlacklist) -> Option { + if custom_path.is_empty() || ip_blacklist.is_loading() || ip_blacklist.is_invalid() { + return None; + } + + match (ip_blacklist.ip_count(), ip_blacklist.network_count()) { + (0, 0) => None, + (ip_count, 0) => Some(format!("(IPs: {ip_count})")), + (0, network_count) => Some(format!("(CIDRs: {network_count})")), + (ip_count, network_count) => Some(format!("(IPs: {ip_count}, CIDRs: {network_count})")), } } diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index d4c087b65..9a3fc11c0 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -5,40 +5,20 @@ use std::sync::Arc; use ipnet::IpNet; use prefix_trie::joint::set::JointPrefixSet; -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub enum IpBlacklistLoadStatus { - #[default] - NotSelected, - Loading, - Loaded { - ip_count: usize, - network_count: usize, - ignored_lines: usize, - }, - FileReadError, - NoValidEntries { - ignored_lines: usize, - }, -} - #[derive(Clone, Default, Debug)] pub struct IpBlacklist { ips: Arc>, networks: Arc>, - status: IpBlacklistLoadStatus, + is_loading: bool, } impl IpBlacklist { pub async fn from_file(path: String) -> Self { let Ok(buf) = tokio::fs::read_to_string(&path).await else { - return IpBlacklist { - status: IpBlacklistLoadStatus::FileReadError, - ..IpBlacklist::default() - }; + return IpBlacklist::default(); }; let mut ips = HashSet::new(); let mut networks = JointPrefixSet::new(); - let mut ignored_lines = 0; for line in buf.lines() { let Some(first) = line.split_whitespace().next() else { continue; @@ -48,26 +28,12 @@ impl IpBlacklist { ips.insert(ip); } else if let Ok(network) = first.parse::() { networks.insert(network); - } else { - ignored_lines += 1; } } - let ip_count = ips.len(); - let network_count = networks.len(); - let status = if ip_count == 0 && network_count == 0 { - IpBlacklistLoadStatus::NoValidEntries { ignored_lines } - } else { - IpBlacklistLoadStatus::Loaded { - ip_count, - network_count, - ignored_lines, - } - }; - IpBlacklist { ips: Arc::new(ips), networks: Arc::new(networks), - status, + is_loading: false, } } @@ -76,22 +42,23 @@ impl IpBlacklist { } pub fn is_invalid(&self) -> bool { - matches!( - self.status, - IpBlacklistLoadStatus::FileReadError | IpBlacklistLoadStatus::NoValidEntries { .. } - ) + self.ips.is_empty() && self.networks.is_empty() && !self.is_loading } pub fn is_loading(&self) -> bool { - matches!(self.status, IpBlacklistLoadStatus::Loading) + self.is_loading } - pub fn status(&self) -> &IpBlacklistLoadStatus { - &self.status + pub fn ip_count(&self) -> usize { + self.ips.len() + } + + pub fn network_count(&self) -> usize { + self.networks.len() } pub fn start_loading(&mut self) { - self.status = IpBlacklistLoadStatus::Loading; + self.is_loading = true; } } @@ -109,14 +76,8 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 4); assert_eq!(blacklist.networks.len(), 0); - assert_eq!( - blacklist.status(), - &IpBlacklistLoadStatus::Loaded { - ip_count: 4, - network_count: 0, - ignored_lines: 4, - } - ); + assert_eq!(blacklist.ip_count(), 4); + assert_eq!(blacklist.network_count(), 0); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255)))); @@ -138,10 +99,8 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); assert_eq!(blacklist.networks.len(), 0); - assert_eq!( - blacklist.status(), - &IpBlacklistLoadStatus::NoValidEntries { ignored_lines: 6 } - ); + assert_eq!(blacklist.ip_count(), 0); + assert_eq!(blacklist.network_count(), 0); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))); @@ -159,14 +118,8 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 2); assert_eq!(blacklist.networks.len(), 4); - assert_eq!( - blacklist.status(), - &IpBlacklistLoadStatus::Loaded { - ip_count: 2, - network_count: 4, - ignored_lines: 3, - } - ); + assert_eq!(blacklist.ip_count(), 2); + assert_eq!(blacklist.network_count(), 4); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&"2001:db8::1".parse::().unwrap())); @@ -198,14 +151,8 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); assert_eq!(blacklist.networks.len(), 1); - assert_eq!( - blacklist.status(), - &IpBlacklistLoadStatus::Loaded { - ip_count: 0, - network_count: 1, - ignored_lines: 0, - } - ); + assert_eq!(blacklist.ip_count(), 0); + assert_eq!(blacklist.network_count(), 1); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 4, 1)))); @@ -249,7 +196,6 @@ mod tests { assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); assert_eq!(blacklist.networks.len(), 0); - assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::FileReadError); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); } @@ -261,6 +207,5 @@ mod tests { assert!(blacklist.is_loading()); assert!(!blacklist.is_invalid()); - assert_eq!(blacklist.status(), &IpBlacklistLoadStatus::Loading); } } diff --git a/src/translations/translations_5.rs b/src/translations/translations_5.rs index 60a85ba47..7e67f5e2a 100644 --- a/src/translations/translations_5.rs +++ b/src/translations/translations_5.rs @@ -86,198 +86,6 @@ pub fn ip_blacklist_translation(language: Language) -> &'static str { } } -pub fn ip_blacklist_not_selected_translation(language: Language) -> &'static str { - match language { - Language::EN => "No blacklist file selected.", - Language::CS => "Není vybrán žádný soubor blacklistu.", - Language::DE => "Keine Blacklist-Datei ausgewählt.", - Language::EL => "Δεν έχει επιλεγεί αρχείο λίστας αποκλεισμού.", - Language::ES => "No se ha seleccionado ningún archivo de blacklist.", - Language::FI => "Blacklist-tiedostoa ei ole valittu.", - Language::FR => "Aucun fichier de blacklist sélectionné.", - Language::ID => "Belum ada berkas daftar hitam yang dipilih.", - Language::IT => "Nessun file blacklist selezionato.", - Language::JA => "ブラックリストファイルが選択されていません。", - Language::KO => "선택된 블랙리스트 파일이 없습니다.", - Language::NL => "Geen blacklistbestand geselecteerd.", - Language::PL => "Nie wybrano pliku blacklisty.", - Language::PT => "Nenhum arquivo de blacklist selecionado.", - Language::RO => "Nu a fost selectat niciun fișier blacklist.", - Language::RU => "Файл черного списка не выбран.", - Language::SV => "Ingen svartlistfil har valts.", - Language::TR => "Kara liste dosyası seçilmedi.", - Language::UK => "Файл чорного списку не вибрано.", - Language::UZ => "Qora ro'yxat fayli tanlanmagan.", - Language::VI => "Chưa chọn tệp danh sách đen.", - Language::ZH => "未选择黑名单文件。", - Language::ZH_TW => "未選擇黑名單檔案。", - } -} - -pub fn ip_blacklist_loading_translation(language: Language) -> &'static str { - match language { - Language::EN => "Loading blacklist...", - Language::CS => "Načítá se blacklist...", - Language::DE => "Blacklist wird geladen...", - Language::EL => "Φόρτωση λίστας αποκλεισμού...", - Language::ES => "Cargando blacklist...", - Language::FI => "Ladataan blacklistia...", - Language::FR => "Chargement de la blacklist...", - Language::ID => "Memuat daftar hitam...", - Language::IT => "Caricamento blacklist...", - Language::JA => "ブラックリストを読み込み中...", - Language::KO => "블랙리스트를 불러오는 중...", - Language::NL => "Blacklist laden...", - Language::PL => "Wczytywanie blacklisty...", - Language::PT => "Carregando blacklist...", - Language::RO => "Se încarcă blacklistul...", - Language::RU => "Загрузка черного списка...", - Language::SV => "Läser in svartlistan...", - Language::TR => "Kara liste yükleniyor...", - Language::UK => "Завантаження чорного списку...", - Language::UZ => "Qora ro'yxat yuklanmoqda...", - Language::VI => "Đang tải danh sách đen...", - Language::ZH => "正在加载黑名单...", - Language::ZH_TW => "正在載入黑名單...", - } -} - -pub fn ip_blacklist_file_read_error_translation(language: Language) -> &'static str { - match language { - Language::EN => "Could not read blacklist file.", - Language::CS => "Soubor blacklistu nelze přečíst.", - Language::DE => "Blacklist-Datei konnte nicht gelesen werden.", - Language::EL => "Δεν ήταν δυνατή η ανάγνωση του αρχείου λίστας αποκλεισμού.", - Language::ES => "No se pudo leer el archivo de blacklist.", - Language::FI => "Blacklist-tiedostoa ei voitu lukea.", - Language::FR => "Impossible de lire le fichier de blacklist.", - Language::ID => "Berkas daftar hitam tidak dapat dibaca.", - Language::IT => "Impossibile leggere il file blacklist.", - Language::JA => "ブラックリストファイルを読み取れませんでした。", - Language::KO => "블랙리스트 파일을 읽을 수 없습니다.", - Language::NL => "Kan het blacklistbestand niet lezen.", - Language::PL => "Nie można odczytać pliku blacklisty.", - Language::PT => "Não foi possível ler o arquivo de blacklist.", - Language::RO => "Nu s-a putut citi fișierul blacklist.", - Language::RU => "Не удалось прочитать файл черного списка.", - Language::SV => "Kunde inte läsa svartlistfilen.", - Language::TR => "Kara liste dosyası okunamadı.", - Language::UK => "Не вдалося прочитати файл чорного списку.", - Language::UZ => "Qora ro'yxat faylini o'qib bo'lmadi.", - Language::VI => "Không thể đọc tệp danh sách đen.", - Language::ZH => "无法读取黑名单文件。", - Language::ZH_TW => "無法讀取黑名單檔案。", - } -} - -pub fn ip_blacklist_no_valid_entries_translation(language: Language) -> &'static str { - match language { - Language::EN => "No valid IP addresses or CIDR ranges found.", - Language::CS => "Nebyly nalezeny žádné platné IP adresy ani rozsahy CIDR.", - Language::DE => "Keine gültigen IP-Adressen oder CIDR-Bereiche gefunden.", - Language::EL => "Δεν βρέθηκαν έγκυρες διευθύνσεις IP ή εύρη CIDR.", - Language::ES => "No se encontraron direcciones IP ni rangos CIDR válidos.", - Language::FI => "Kelvollisia IP-osoitteita tai CIDR-alueita ei löytynyt.", - Language::FR => "Aucune adresse IP ni plage CIDR valide trouvée.", - Language::ID => "Tidak ada alamat IP atau rentang CIDR yang valid ditemukan.", - Language::IT => "Nessun indirizzo IP o intervallo CIDR valido trovato.", - Language::JA => "有効な IP アドレスまたは CIDR 範囲が見つかりませんでした。", - Language::KO => "유효한 IP 주소 또는 CIDR 범위를 찾을 수 없습니다.", - Language::NL => "Geen geldige IP-adressen of CIDR-bereiken gevonden.", - Language::PL => "Nie znaleziono prawidłowych adresów IP ani zakresów CIDR.", - Language::PT => "Nenhum endereço IP ou intervalo CIDR válido encontrado.", - Language::RO => "Nu au fost găsite adrese IP sau intervale CIDR valide.", - Language::RU => "Действительные IP-адреса или диапазоны CIDR не найдены.", - Language::SV => "Inga giltiga IP-adresser eller CIDR-intervall hittades.", - Language::TR => "Geçerli IP adresi veya CIDR aralığı bulunamadı.", - Language::UK => "Дійсні IP-адреси або діапазони CIDR не знайдено.", - Language::UZ => "Yaroqli IP manzillar yoki CIDR oraliqlari topilmadi.", - Language::VI => "Không tìm thấy địa chỉ IP hoặc dải CIDR hợp lệ.", - Language::ZH => "未找到有效的 IP 地址或 CIDR 范围。", - Language::ZH_TW => "找不到有效的 IP 位址或 CIDR 範圍。", - } -} - -pub fn ip_blacklist_loaded_translation( - language: Language, - ip_count: usize, - network_count: usize, -) -> String { - match language { - Language::EN => { - format!("Loaded {ip_count} IP addresses and {network_count} CIDR ranges.") - } - Language::CS => { - format!("Načteno {ip_count} IP adres a {network_count} rozsahů CIDR.") - } - Language::DE => { - format!("{ip_count} IP-Adressen und {network_count} CIDR-Bereiche geladen.") - } - Language::EL => { - format!("Φορτώθηκαν {ip_count} διευθύνσεις IP και {network_count} εύρη CIDR.") - } - Language::ES => { - format!("Cargadas {ip_count} direcciones IP y {network_count} rangos CIDR.") - } - Language::FI => { - format!("Ladattu {ip_count} IP-osoitetta ja {network_count} CIDR-aluetta.") - } - Language::FR => { - format!("{ip_count} adresses IP et {network_count} plages CIDR chargées.") - } - Language::ID => { - format!("Dimuat {ip_count} alamat IP dan {network_count} rentang CIDR.") - } - Language::IT => { - format!("Caricati {ip_count} indirizzi IP e {network_count} intervalli CIDR.") - } - Language::JA => { - format!( - "{ip_count} 件の IP アドレスと {network_count} 件の CIDR 範囲を読み込みました。" - ) - } - Language::KO => { - format!("IP 주소 {ip_count}개와 CIDR 범위 {network_count}개를 불러왔습니다.") - } - Language::NL => { - format!("{ip_count} IP-adressen en {network_count} CIDR-bereiken geladen.") - } - Language::PL => { - format!("Wczytano adresów IP: {ip_count}, zakresów CIDR: {network_count}.") - } - Language::PT => { - format!("Carregados {ip_count} endereços IP e {network_count} intervalos CIDR.") - } - Language::RO => { - format!("Încărcate {ip_count} adrese IP și {network_count} intervale CIDR.") - } - Language::RU => { - format!("Загружено IP-адресов: {ip_count}, диапазонов CIDR: {network_count}.") - } - Language::SV => { - format!("{ip_count} IP-adresser och {network_count} CIDR-intervall har lästs in.") - } - Language::TR => { - format!("{ip_count} IP adresi ve {network_count} CIDR aralığı yüklendi.") - } - Language::UK => { - format!("Завантажено IP-адрес: {ip_count}, діапазонів CIDR: {network_count}.") - } - Language::UZ => { - format!("{ip_count} ta IP manzil va {network_count} ta CIDR oralig'i yuklandi.") - } - Language::VI => { - format!("Đã tải {ip_count} địa chỉ IP và {network_count} dải CIDR.") - } - Language::ZH => { - format!("已加载 {ip_count} 个 IP 地址和 {network_count} 个 CIDR 范围。") - } - Language::ZH_TW => { - format!("已載入 {ip_count} 個 IP 位址和 {network_count} 個 CIDR 範圍。") - } - } -} - pub fn blacklisted_transmitted_translation(language: Language) -> &'static str { match language { Language::EN => "New data exchanged from a blacklisted IP", From a1dbc46179729540018d3500615404a6c804f977 Mon Sep 17 00:00:00 2001 From: HangryNugget Date: Fri, 8 May 2026 16:25:14 +0200 Subject: [PATCH 12/23] refactor: remove redundant tests and simplify blacklist loading checks --- src/gui/pages/settings_general_page.rs | 2 +- src/networking/types/ip_blacklist.rs | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs index 348459bb9..1243c4fbf 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -373,7 +373,7 @@ fn blacklist_selection<'a>( } fn blacklist_loaded_counts(custom_path: &str, ip_blacklist: &IpBlacklist) -> Option { - if custom_path.is_empty() || ip_blacklist.is_loading() || ip_blacklist.is_invalid() { + if custom_path.is_empty() || ip_blacklist.is_loading() { return None; } diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index 9a3fc11c0..dd630af0d 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -186,26 +186,4 @@ mod tests { assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 237, 0)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 233, 160, 0)))); } - - #[tokio::test] - async fn test_ip_blacklist_read_error() { - let blacklist = - IpBlacklist::from_file("resources/test/does_not_exist_blacklist.txt".to_string()).await; - - assert!(blacklist.is_invalid()); - assert!(!blacklist.is_loading()); - assert_eq!(blacklist.ips.len(), 0); - assert_eq!(blacklist.networks.len(), 0); - assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); - } - - #[test] - fn test_ip_blacklist_loading_status() { - let mut blacklist = IpBlacklist::default(); - - blacklist.start_loading(); - - assert!(blacklist.is_loading()); - assert!(!blacklist.is_invalid()); - } } From 1612c8e21e8c3d16c1a2e40fa724a5a5bef5ebe8 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sat, 9 May 2026 23:00:00 +0200 Subject: [PATCH 13/23] cleanup implementation --- src/gui/pages/settings_general_page.rs | 32 ++++--------- src/networking/types/ip_blacklist.rs | 62 +++++++++++++++----------- 2 files changed, 44 insertions(+), 50 deletions(-) diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs index 1243c4fbf..d58ca16ec 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -10,7 +10,7 @@ use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::button::ButtonType; use crate::gui::styles::container::ContainerType; use crate::gui::styles::rule::RuleType; -use crate::gui::styles::style_constants::{FONT_SIZE_SUBTITLE, TOOLTIP_DELAY}; +use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_SUBTITLE, TOOLTIP_DELAY}; use crate::gui::styles::text::TextType; use crate::gui::types::message::Message; use crate::gui::types::settings::Settings; @@ -331,9 +331,7 @@ fn blacklist_selection<'a>( }; let message = Message::LoadIpBlacklist; - let loaded_counts = blacklist_loaded_counts(custom_path, ip_blacklist); - - let mut column = Column::new() + Column::new() .width(Length::Fill) .spacing(5) .align_x(Alignment::Center) @@ -363,26 +361,12 @@ fn blacklist_selection<'a>( } else { button_clear_mmdb(message, is_editable) }), - ); - - if let Some(loaded_counts) = loaded_counts { - column = column.push(Text::new(loaded_counts).class(TextType::Standard).size(12)); - } - - column -} - -fn blacklist_loaded_counts(custom_path: &str, ip_blacklist: &IpBlacklist) -> Option { - if custom_path.is_empty() || ip_blacklist.is_loading() { - return None; - } - - match (ip_blacklist.ip_count(), ip_blacklist.network_count()) { - (0, 0) => None, - (ip_count, 0) => Some(format!("(IPs: {ip_count})")), - (0, network_count) => Some(format!("(CIDRs: {network_count})")), - (ip_count, network_count) => Some(format!("(IPs: {ip_count}, CIDRs: {network_count})")), - } + ) + .push( + ip_blacklist + .imported_items_info() + .map(|info| Text::new(info).size(FONT_SIZE_FOOTER)), + ) } fn button_clear_mmdb<'a>( diff --git a/src/networking/types/ip_blacklist.rs b/src/networking/types/ip_blacklist.rs index dd630af0d..66110de5b 100644 --- a/src/networking/types/ip_blacklist.rs +++ b/src/networking/types/ip_blacklist.rs @@ -8,7 +8,7 @@ use prefix_trie::joint::set::JointPrefixSet; #[derive(Clone, Default, Debug)] pub struct IpBlacklist { ips: Arc>, - networks: Arc>, + cidrs: Arc>, is_loading: bool, } @@ -18,7 +18,7 @@ impl IpBlacklist { return IpBlacklist::default(); }; let mut ips = HashSet::new(); - let mut networks = JointPrefixSet::new(); + let mut cidrs = JointPrefixSet::new(); for line in buf.lines() { let Some(first) = line.split_whitespace().next() else { continue; @@ -26,35 +26,36 @@ impl IpBlacklist { if let Ok(ip) = first.parse::() { ips.insert(ip); - } else if let Ok(network) = first.parse::() { - networks.insert(network); + } else if let Ok(cidr) = first.parse::() { + cidrs.insert(cidr); } } IpBlacklist { ips: Arc::new(ips), - networks: Arc::new(networks), + cidrs: Arc::new(cidrs), is_loading: false, } } pub fn contains(&self, ip: &IpAddr) -> bool { - self.ips.contains(ip) || self.networks.get_lpm(&IpNet::from(*ip)).is_some() + self.ips.contains(ip) || self.cidrs.get_lpm(&IpNet::from(*ip)).is_some() } pub fn is_invalid(&self) -> bool { - self.ips.is_empty() && self.networks.is_empty() && !self.is_loading + self.ips.is_empty() && self.cidrs.is_empty() && !self.is_loading } pub fn is_loading(&self) -> bool { self.is_loading } - pub fn ip_count(&self) -> usize { - self.ips.len() - } - - pub fn network_count(&self) -> usize { - self.networks.len() + pub fn imported_items_info(&self) -> Option { + match (self.ips.len(), self.cidrs.len()) { + (0, 0) => None, + (ips, 0) => Some(format!("(IPs: {ips})")), + (0, cidrs) => Some(format!("(CIDRs: {cidrs})")), + (ips, cidrs) => Some(format!("(IPs: {ips}, CIDRs: {cidrs})")), + } } pub fn start_loading(&mut self) { @@ -75,9 +76,11 @@ mod tests { assert!(!blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 4); - assert_eq!(blacklist.networks.len(), 0); - assert_eq!(blacklist.ip_count(), 4); - assert_eq!(blacklist.network_count(), 0); + assert_eq!(blacklist.cidrs.len(), 0); + assert_eq!( + blacklist.imported_items_info(), + Some("(IPs: 4)".to_string()) + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 255)))); @@ -98,9 +101,8 @@ mod tests { assert!(blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); - assert_eq!(blacklist.networks.len(), 0); - assert_eq!(blacklist.ip_count(), 0); - assert_eq!(blacklist.network_count(), 0); + assert_eq!(blacklist.cidrs.len(), 0); + assert_eq!(blacklist.imported_items_info(), None); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)))); @@ -117,9 +119,11 @@ mod tests { assert!(!blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 2); - assert_eq!(blacklist.networks.len(), 4); - assert_eq!(blacklist.ip_count(), 2); - assert_eq!(blacklist.network_count(), 4); + assert_eq!(blacklist.cidrs.len(), 4); + assert_eq!( + blacklist.imported_items_info(), + Some("(IPs: 2, CIDRs: 4)".to_string()) + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); assert!(blacklist.contains(&"2001:db8::1".parse::().unwrap())); @@ -150,9 +154,11 @@ mod tests { assert!(!blacklist.is_invalid()); assert!(!blacklist.is_loading()); assert_eq!(blacklist.ips.len(), 0); - assert_eq!(blacklist.networks.len(), 1); - assert_eq!(blacklist.ip_count(), 0); - assert_eq!(blacklist.network_count(), 1); + assert_eq!(blacklist.cidrs.len(), 1); + assert_eq!( + blacklist.imported_items_info(), + Some("(CIDRs: 1)".to_string()) + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 3, 1)))); assert!(!blacklist.contains(&IpAddr::V4(Ipv4Addr::new(1, 2, 4, 1)))); @@ -166,7 +172,11 @@ mod tests { assert!(!blacklist.is_invalid()); assert_eq!(blacklist.ips.len(), 0); - assert_eq!(blacklist.networks.len(), 6); + assert_eq!(blacklist.cidrs.len(), 6); + assert_eq!( + blacklist.imported_items_info(), + Some("(CIDRs: 6)".to_string()) + ); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 20, 0)))); assert!(blacklist.contains(&IpAddr::V4(Ipv4Addr::new(209, 186, 23, 255)))); From 6b8313a783c161b8109df401e74592e02357b109 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Sat, 9 May 2026 23:02:38 +0200 Subject: [PATCH 14/23] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 588f1893e..1655b423a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] - Support CIDR ranges in imported IP blacklists ([#1181](https://github.com/GyulyVGC/sniffnet/pull/1181)) +- Show number of IP addresses and CIDR ranges in imported IP blacklists ([#1183](https://github.com/GyulyVGC/sniffnet/pull/1183)) - Updated some of the existing translations to v1.5: - Ukrainian ([#1039](https://github.com/GyulyVGC/sniffnet/pull/1039)) - German ([#1122](https://github.com/GyulyVGC/sniffnet/pull/1122)) From ab4340abe8efff0fc16e96dda1f9916e3adb623b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 10:42:16 +0200 Subject: [PATCH 15/23] docs: add ahassany as a contributor for ideas (#1186) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 31 +++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index d589fb54b..9a2f26dc8 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -966,6 +966,15 @@ "contributions": [ "code" ] + }, + { + "login": "ahassany", + "name": "Ahmed Elhassany", + "avatar_url": "https://avatars.githubusercontent.com/u/691424?v=4", + "profile": "http://hassany.ps", + "contributions": [ + "ideas" + ] } ], "contributorsPerLine": 7, diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 13426a582..1b7221362 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -12,132 +12,135 @@ A big shout-out to all the contributors of Sniffnet ([emoji key](https://allcont Absolutex
Absolutex

🐛 Adriano
Adriano

🤔 Aguacero 🌧️
Aguacero 🌧️

🌍 💻 - Ahmet Kaan GÜMÜŞ
Ahmet Kaan GÜMÜŞ

🌍 + Ahmed Elhassany
Ahmed Elhassany

🤔 + Ahmet Kaan GÜMÜŞ
Ahmet Kaan GÜMÜŞ

🌍 Alexandr Shashkin
Alexandr Shashkin

🐛 AlleM43
AlleM43

📦 AmadeusGraves
AmadeusGraves

🌍 Angelos Bousis
Angelos Bousis

🌍 Antoine Colombier
Antoine Colombier

⚠️ 🌍 Anton Liashkevich
Anton Liashkevich

💻 - Aris Konstantoulas
Aris Konstantoulas

🌍 + Aris Konstantoulas
Aris Konstantoulas

🌍 BugsQuanti
BugsQuanti

🌍 Charpy
Charpy

🌍 Christian
Christian

🌍 Christoph Wanja
Christoph Wanja

💵 Colin Delahunty
Colin Delahunty

⚠️ Cornelius Roemer
Cornelius Roemer

🤔 - CosminPerRam
CosminPerRam

💻 + CosminPerRam
CosminPerRam

💻 Cristiano
Cristiano

💻 🤔 Cthulu201
Cthulu201

💵 Dinar Shagaliev
Dinar Shagaliev

🌍 Dmytro Gaiduk
Dmytro Gaiduk

🌍 Dominic Kim
Dominic Kim

🌍 Duc Tin Ngo
Duc Tin Ngo

🌍 - Echo
Echo

💵 + Echo
Echo

💵 Elyrial
Elyrial

🌍 Embers-of-the-Fire
Embers-of-the-Fire

🌍 Enes Çaydere
Enes Çaydere

🌍 Farsheed Atef
Farsheed Atef

🤔 Francisco Salgueiro
Francisco Salgueiro

🌍 Franco Bonfiglio Vazquez
Franco Bonfiglio Vazquez

🌍 - GNUser
GNUser

📖 📦 + GNUser
GNUser

📖 📦 George Liontos
George Liontos

🌍 George Shuklin
George Shuklin

🌍 Giusy Digital
Giusy Digital

🐛 Glenn Johansson
Glenn Johansson

💵 Hiroki Tagato
Hiroki Tagato

📦 Hubert
Hubert

🌍 - Hüseyin Fahri Uzun
Hüseyin Fahri Uzun

🌍 + Hüseyin Fahri Uzun
Hüseyin Fahri Uzun

🌍 IPinfo
IPinfo

💵 Ilmi2
Ilmi2

💵 Jan Walter
Jan Walter

💵 Jauder Ho
Jauder Ho

🚇 Joshua Megnauth
Joshua Megnauth

💻 🎨 Julian Schmid
Julian Schmid

💻 🤔 - Kevin
Kevin

💵 + Kevin
Kevin

💵 LiChenG-P
LiChenG-P

💻 Liam OBrien
Liam OBrien

🤔 Limdongju
Limdongju

🌍 Lion Rayonnant
Lion Rayonnant

🌍 Ludwig Stecher
Ludwig Stecher

🤔 💻 Marc Gavilán
Marc Gavilán

🌍 - Marco Cadetg
Marco Cadetg

📦 + Marco Cadetg
Marco Cadetg

📦 Matt Seng
Matt Seng

💵 Matthias Braun
Matthias Braun

📖 MelcuGoa
MelcuGoa

🌍 Michal Zboril
Michal Zboril

🌍 Michel Hansma
Michel Hansma

🎨 ️️️️♿️ Morgan Hill
Morgan Hill

🛡️ - Muhammadali Hakimov
Muhammadali Hakimov

🌍 + Muhammadali Hakimov
Muhammadali Hakimov

🌍 Nubi
Nubi

🌍 Oleksii Filonenko
Oleksii Filonenko

🌍 Orhun Parmaksız
Orhun Parmaksız

📖 📦 💵 Paco
Paco

🌍 Peter Dave Hello
Peter Dave Hello

🌍 Phil Clifford
Phil Clifford

📦 - Quetzal-coalt
Quetzal-coalt

🌍 + Quetzal-coalt
Quetzal-coalt

🌍 Raghav Potluri
Raghav Potluri

📦 Recall.ai
Recall.ai

💵 Ron
Ron

🤔 Safaraliev Maxim
Safaraliev Maxim

🌍 Shawn Yeager
Shawn Yeager

💵 SignPath GmbH
SignPath GmbH

📦 - The Artifex
The Artifex

🌍 📦 + The Artifex
The Artifex

🌍 📦 TheMasterOfDisasters
TheMasterOfDisasters

💻 Tony
Tony

💵 Trịnh Duy Hưng
Trịnh Duy Hưng

🌍 TyseEX
TyseEX

🐛 Victor Nilsson
Victor Nilsson

🌍 💻 Wang Zishi
Wang Zishi

🌍 - Yevhen
Yevhen

🌍 + Yevhen
Yevhen

🌍 Ylva
Ylva

🌍 ZEROF
ZEROF

💵 ZeroDot1
ZeroDot1

🎨 ️️️️♿️ clr
clr

📖 🌍 edwloef
edwloef

🐛 ervinpopescu
ervinpopescu

🌍 - glitsj16
glitsj16

📦 + glitsj16
glitsj16

📦 guilherme-demarchi
guilherme-demarchi

🌍 hirotake111
hirotake111

🌍 islameehassan
islameehassan

💻 louis-ym4
louis-ym4

🎨 luca3s
luca3s

🌍 mbe57
mbe57

🤔 - pia
pia

🌍 + pia
pia

🌍 pin
pin

📦 shu-kitamura
shu-kitamura

💻 🌍 starccy
starccy

💻 tiansheng li
tiansheng li

💵 vtiinanen
vtiinanen

🌍 yossarian
yossarian

🌍 + + 陈寒彤
陈寒彤

🌍 From 4269a289f511b25b7da18dd9543063c636f9ec6d Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Wed, 27 May 2026 13:29:13 +0200 Subject: [PATCH 16/23] update domain to sniffnet.app --- .design/README.md | 2 +- .well-known/funding-manifest-urls | 2 +- Cargo.toml | 2 +- INCIDENT_RESPONSE.md | 2 +- README.md | 8 ++++---- SECURITY.md | 2 +- giscus.json | 8 ++++++-- src/utils/formatted_strings.rs | 6 +++--- src/utils/types/web_page.rs | 8 ++++---- 9 files changed, 22 insertions(+), 18 deletions(-) diff --git a/.design/README.md b/.design/README.md index b7a89ff56..7ea559367 100644 --- a/.design/README.md +++ b/.design/README.md @@ -41,7 +41,7 @@ for this reason implementing an optimal UI for the application is crucially impo ## 🚀 The Main Product -Sniffnet can be downloaded from [here](https://sniffnet.net/download/). +Sniffnet can be downloaded from [here](https://sniffnet.app/download/). ### Target audience diff --git a/.well-known/funding-manifest-urls b/.well-known/funding-manifest-urls index 9fa7bcf5b..c2723b47f 100644 --- a/.well-known/funding-manifest-urls +++ b/.well-known/funding-manifest-urls @@ -1 +1 @@ -https://sniffnet.net/funding.json \ No newline at end of file +https://sniffnet.app/funding.json \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 4abb43987..83b8b2efe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Giuliano Bellini "] edition = "2024" description = "Application to comfortably monitor your network traffic" readme = "README.md" -homepage = "https://sniffnet.net" +homepage = "https://sniffnet.app" repository = "https://github.com/GyulyVGC/sniffnet" documentation = "https://github.com/GyulyVGC/sniffnet/wiki" license = "MIT OR Apache-2.0" diff --git a/INCIDENT_RESPONSE.md b/INCIDENT_RESPONSE.md index 2fa7864f1..ba2b689e0 100644 --- a/INCIDENT_RESPONSE.md +++ b/INCIDENT_RESPONSE.md @@ -13,7 +13,7 @@ or safety of Sniffnet or its users: - a vulnerability in Sniffnet or one of its dependencies - a tampered release artifact or compromised signing key - unauthorized commits, tags, or releases -- a compromised maintainer account or the [sniffnet.net](https://sniffnet.net) website +- a compromised maintainer account or the [sniffnet.app](https://sniffnet.app) website ## Roles diff --git a/README.md b/README.md index 918416ee4..29bc10688 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - + Application to comfortably monitor your Internet traffic.
@@ -61,7 +61,7 @@ A special shout-out to these awesome organizations and folks who are sponsoring ## Download - + @@ -174,12 +174,12 @@ ICED_BACKEND=tiny-skia ## Stay in the loop Wait... there's more!
Sniffnet is rapidly evolving, and new features are added on a regular basis.
-Follow the news and Sniffnet socials to never miss an update. +Follow the news and Sniffnet socials to never miss an update.
- Bluesky + Bluesky LinkedIn diff --git a/SECURITY.md b/SECURITY.md index ac139c929..615f75ebe 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,7 +3,7 @@ We consider the security of Sniffnet a top priority.
The application is developed with a **security-first approach**: it has always been designed to **protect users' data privacy and system integrity**, -as demonstrated by [security audits](https://sniffnet.net/news/security-audit/) assessing the application safety. +as demonstrated by [security audits](https://sniffnet.app/news/security-audit/) assessing the application safety. But no matter how much effort we put into securing the app, there can still be **vulnerabilities** present. diff --git a/giscus.json b/giscus.json index 80f1a31a4..21ae364b4 100644 --- a/giscus.json +++ b/giscus.json @@ -1,5 +1,9 @@ { - "origins": ["https://sniffnet.net"], - "originsRegex": ["http://localhost:[0-9]+"], + "origins": [ + "https://sniffnet.app" + ], + "originsRegex": [ + "http://localhost:[0-9]+" + ], "defaultCommentOrder": "oldest" } \ No newline at end of file diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index 9c0591c26..94eb96bc4 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -28,7 +28,7 @@ pub fn print_cli_welcome_message() { │ │\n\ │ Sniffnet {ver} │\n\ │ │\n\ -│ → Website: https://sniffnet.net │\n\ +│ → Website: https://sniffnet.app │\n\ │ → GitHub: https://github.com/GyulyVGC/sniffnet │\n\ │ │\n\ ╰────────────────────────────────────────────────────────────────────╯\n\n" @@ -245,8 +245,8 @@ mod tests { assert_eq!(clip_text("protocol90 23456", 13), "protocol90…"); assert_eq!( - clip_text(" \n\t sniffnet.net ", 26), - "sniffnet.net" + clip_text(" \n\t sniffnet.app ", 26), + "sniffnet.app" ); assert_eq!( clip_text(" protocol90 23456 \n ", 12), diff --git a/src/utils/types/web_page.rs b/src/utils/types/web_page.rs index 3795bc017..7e60357e9 100644 --- a/src/utils/types/web_page.rs +++ b/src/utils/types/web_page.rs @@ -27,10 +27,10 @@ impl WebPage { pub fn get_url(&self) -> &str { match self { WebPage::Repo => "https://github.com/GyulyVGC/sniffnet", - // WebPage::Website => "https://www.sniffnet.net", - WebPage::WebsiteSponsor => "https://www.sniffnet.net/sponsor", - WebPage::WebsiteDownload => "https://www.sniffnet.net/download", - WebPage::WebsiteNews => "https://www.sniffnet.net/news", + // WebPage::Website => "https://www.sniffnet.app", + WebPage::WebsiteSponsor => "https://www.sniffnet.app/sponsor", + WebPage::WebsiteDownload => "https://www.sniffnet.app/download", + WebPage::WebsiteNews => "https://www.sniffnet.app/news", WebPage::Roadmap => "https://whimsical.com/sniffnet-roadmap-Damodrdfx22V9jGnpHSCGo", WebPage::Issues => "https://github.com/GyulyVGC/sniffnet/issues", WebPage::IssueLanguages => "https://github.com/GyulyVGC/sniffnet/issues/60", From 3f7a00d4399089b3349008fdf6c7f881f8500721 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Thu, 28 May 2026 11:49:03 +0200 Subject: [PATCH 17/23] update sniffnet website repo name ref --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29bc10688..9904ea0da 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ A special shout-out to these awesome organizations and folks who are sponsoring ## Download - + From 739ab44aad085fdc91d2cab802623ad6440869a6 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Tue, 2 Jun 2026 11:44:59 +0200 Subject: [PATCH 18/23] update docs to include official sniffnet emails --- CODE_OF_CONDUCT.md | 2 +- INCIDENT_RESPONSE.md | 2 + README.md | 4 ++ SECURITY.md | 4 +- THREAT_MODEL.md | 2 + resources/logos/raw/rect.svg | 108 +++++++++++++++++++++++++++++++++++ 6 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 resources/logos/raw/rect.svg diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 227004e5d..8442e3261 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -60,7 +60,7 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -gyulyvgc99@gmail.com. +`info@sniffnet.app`. All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the diff --git a/INCIDENT_RESPONSE.md b/INCIDENT_RESPONSE.md index ba2b689e0..3a1a6a136 100644 --- a/INCIDENT_RESPONSE.md +++ b/INCIDENT_RESPONSE.md @@ -5,6 +5,8 @@ This document describes how Sniffnet handles security incidents. For how to report a vulnerability, see [`SECURITY.md`](https://github.com/GyulyVGC/sniffnet/blob/main/SECURITY.md).
For the project's threat model, see [`THREAT_MODEL.md`](https://github.com/GyulyVGC/sniffnet/blob/main/THREAT_MODEL.md). +For all security-related communications, please write to `security@sniffnet.app`. + ## Scope An incident is anything that compromises, or could compromise, the integrity diff --git a/README.md b/README.md index 9904ea0da..109373687 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Translated in:
If you appreciate Sniffnet, [sponsor the project](https://github.com/sponsors/GyulyVGC) to support its [growth](https://github.com/GyulyVGC/sniffnet/blob/main/ROADMAP.md). +For sponsorship inquiries, feel free to reach out at `sponsor@sniffnet.app`. + A special shout-out to these awesome organizations and folks who are sponsoring Sniffnet:
@@ -195,3 +197,5 @@ Follow the news and Sniffnet soci
+ +You can also reach us by email at `info@sniffnet.app`. diff --git a/SECURITY.md b/SECURITY.md index 615f75ebe..db15573d1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,13 +10,15 @@ But no matter how much effort we put into securing the app, there can still be * For how incidents are handled, see [`INCIDENT_RESPONSE.md`](https://github.com/GyulyVGC/sniffnet/blob/main/INCIDENT_RESPONSE.md).
For the project's threat model, see [`THREAT_MODEL.md`](https://github.com/GyulyVGC/sniffnet/blob/main/THREAT_MODEL.md). +For all security-related communications, please write to `security@sniffnet.app`. + *** If you discover a vulnerability, we would like to know about it so we can take steps to address it as quickly as possible. Please do the following: -- E-mail your findings to gyulyvgc99@gmail.com +- E-mail your findings to `security@sniffnet.app` - Do not take advantage of the vulnerability or problem you have discovered - Do not reveal the problem to others until it has been resolved - Do provide sufficient information to reproduce the problem, so we will be able to resolve it as quickly as possible diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md index 26223db28..eacc7c2be 100644 --- a/THREAT_MODEL.md +++ b/THREAT_MODEL.md @@ -15,6 +15,8 @@ We'll use STRIDE, a widely adopted framework to help identify common types of th For how to report a vulnerability, see [`SECURITY.md`](https://github.com/GyulyVGC/sniffnet/blob/main/SECURITY.md).
For how incidents are handled, see [`INCIDENT_RESPONSE.md`](https://github.com/GyulyVGC/sniffnet/blob/main/INCIDENT_RESPONSE.md). +For all security-related communications, please write to `security@sniffnet.app`. + ## 1. Project overview and scope of this document - **Project name:** Sniffnet diff --git a/resources/logos/raw/rect.svg b/resources/logos/raw/rect.svg new file mode 100644 index 000000000..12de879fc --- /dev/null +++ b/resources/logos/raw/rect.svg @@ -0,0 +1,108 @@ + + From ee5379ad81fdae6081c4f3d233156493492799e1 Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Tue, 2 Jun 2026 20:43:29 +0200 Subject: [PATCH 19/23] add note to services.txt stating the file is auto-generated --- build.rs | 4 ++++ resources/scripts/services.sh | 4 +++- services.txt | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index e310910ec..85a9149e7 100644 --- a/build.rs +++ b/build.rs @@ -41,6 +41,10 @@ fn build_services_phf() { for line_res in input.lines() { // we want to panic if one of the lines is err... let line = line_res.unwrap(); + // skip comment or blank lines + if line.trim().is_empty() || line.trim().starts_with('#') { + continue; + } let mut parts = line.split('\t'); // we want to panic if one of the service names is invalid let val = Cow::Owned(get_valid_service_fmt_const(parts.next().unwrap())); diff --git a/resources/scripts/services.sh b/resources/scripts/services.sh index 017313730..973e97ff7 100755 --- a/resources/scripts/services.sh +++ b/resources/scripts/services.sh @@ -6,7 +6,9 @@ OUT=./services.txt +echo -e "# This file is automatically generated from the port-to-service mappings provided by nmap\n# Available at: https://raw.githubusercontent.com/nmap/nmap/master/nmap-services\n# Don't edit this file manually\n" > $OUT + curl https://raw.githubusercontent.com/nmap/nmap/master/nmap-services \ | grep -E '/tcp|/udp' \ | grep -E -v '^#|^unknown\t' \ - | cut -d$'\t' -f 1,2 > $OUT + | cut -d$'\t' -f 1,2 >> $OUT diff --git a/services.txt b/services.txt index 4c26ac4d9..e0227dfc9 100644 --- a/services.txt +++ b/services.txt @@ -1,3 +1,7 @@ +# This file is automatically generated from the port-to-service mappings provided by nmap +# Available at: https://raw.githubusercontent.com/nmap/nmap/master/nmap-services +# Don't edit this file manually + tcpmux 1/tcp tcpmux 1/udp compressnet 2/tcp From 953060098aa56f7cbbf8033a70ee715f36534026 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini <100347457+GyulyVGC@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:49:55 +0200 Subject: [PATCH 20/23] don't rely on shields.io for README badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 109373687..971806542 100644 --- a/README.md +++ b/README.md @@ -63,8 +63,8 @@ A special shout-out to these awesome organizations and folks who are sponsoring ## Download - - + + From 4c9ccbb095735af9d9f493836e67d9bf566489af Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 8 Jun 2026 12:10:22 +0200 Subject: [PATCH 21/23] docs: add golanbenoni as a contributor for financial (#1204) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9a2f26dc8..9d2b4bc7f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -975,6 +975,15 @@ "contributions": [ "ideas" ] + }, + { + "login": "golanbenoni", + "name": "golanbenoni", + "avatar_url": "https://avatars.githubusercontent.com/u/108551998?v=4", + "profile": "https://github.com/golanbenoni", + "contributions": [ + "financial" + ] } ], "contributorsPerLine": 7, diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1b7221362..7e3b53251 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -124,23 +124,24 @@ A big shout-out to all the contributors of Sniffnet ([emoji key](https://allcont + - + - + From e93efd8eea8e686ef4f13e8ec5312546e3c4d96a Mon Sep 17 00:00:00 2001 From: GyulyVGC Date: Mon, 8 Jun 2026 12:16:46 +0200 Subject: [PATCH 22/23] add Golan Ben-Oni as sponsor in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 971806542..722551598 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ A special shout-out to these awesome organizations and folks who are sponsoring Cthulu201   ZEROF   Jan Walter +Golan Ben-Oni

## Download From 236c1b367d278957404520f31da286a8e77d2e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Poleszny=C3=A1k=20M=C3=A1rk?= Date: Thu, 28 May 2026 17:54:38 +0200 Subject: [PATCH 23/23] feat: add Hungarian translations --- src/gui/pages/inspect_page.rs | 6 +- src/translations/translations.rs | 92 +++++++++++++++++++++++++++++- src/translations/translations_2.rs | 25 ++++++++ src/translations/translations_3.rs | 25 +++++++- src/translations/translations_4.rs | 14 +++++ src/translations/translations_5.rs | 11 +++- src/translations/types/language.rs | 16 ++++-- 7 files changed, 175 insertions(+), 14 deletions(-) diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs index 99037ca8e..cbee772b3 100644 --- a/src/gui/pages/inspect_page.rs +++ b/src/gui/pages/inspect_page.rs @@ -4,9 +4,9 @@ use iced::widget::scrollable::Direction; use iced::widget::text::LineHeight; use iced::widget::text_input::Side; use iced::widget::tooltip::Position; +use iced::widget::{button, combo_box, text_input, ComboBox, Space, Toggler, Tooltip}; use iced::widget::{Button, Column, Container, Row, Scrollable, Text, TextInput}; -use iced::widget::{ComboBox, Space, Toggler, Tooltip, button, combo_box, text_input}; -use iced::{Alignment, Element, Length, Padding, Pixels, alignment}; +use iced::{alignment, Alignment, Element, Length, Padding, Pixels}; use crate::gui::components::tab::get_pages_tabs; use crate::gui::components::types::my_modal::MyModal; @@ -624,7 +624,7 @@ mod tests { #[test] fn test_table_titles_display_and_tooltip_values_for_each_language() { // check glyph len when adding new language... - assert_eq!(Language::ALL.len(), 23); + assert_eq!(Language::ALL.len(), 24); for report_col in ReportCol::ALL { for data_repr in DataRepr::ALL { for language in Language::ALL { diff --git a/src/translations/translations.rs b/src/translations/translations.rs index 076976acf..f6b286514 100644 --- a/src/translations/translations.rs +++ b/src/translations/translations.rs @@ -2,8 +2,8 @@ use iced::widget::Text; -use crate::StyleType; use crate::translations::types::language::Language; +use crate::StyleType; // pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> { // Text::new(match language { @@ -31,6 +31,7 @@ use crate::translations::types::language::Language; // Language::VI => "Hãy chọn network adapter để quan sát", // Language::ID => "Pilih Adapter Jaringan yang ingin dicek", // Language::NL => "Selecteer netwerkadapter om te inspecteren", +// Language::HU => "Válaszd ki a vizsgálni kívánt adaptert", // }) // } @@ -59,6 +60,7 @@ use crate::translations::types::language::Language; // Language::UZ => "Ilova protokoli", // Language::ID => "Protokol Aplikasi", // Language::NL => "Toepassingsprotocol", +// Language::HU => "Applikáció protokoll" // } // } @@ -88,6 +90,7 @@ use crate::translations::types::language::Language; // Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng", // Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan", // Language::NL => "Selecteer filters om toe te passen op netwerkverkeer", +// Language::HU => "Válaszd ki a forgalomra alkalmazni kívánt szűrőket", // }) // } @@ -113,6 +116,7 @@ pub fn start_translation(language: Language) -> &'static str { Language::UZ => "Boshlash!", Language::VI => "Bắt đầu!", Language::ID => "Mulai!", + Language::HU => "Kezdés!", } } @@ -139,6 +143,7 @@ pub fn address_translation(language: Language) -> &'static str { Language::UZ => "Manzil", Language::VI => "Địa chỉ", Language::ID => "Alamat", + Language::HU => "Cím", } } @@ -166,6 +171,7 @@ pub fn address_translation(language: Language) -> &'static str { // Language::UZ => "Manzillar", // Language::VI => "Danh sách địa chỉ", // Language::ID => "Alamat", +// Language::HU => "Címek", // } // } @@ -195,6 +201,7 @@ pub fn address_translation(language: Language) -> &'static str { // Language::VI => "Phiên bản IP", // Language::ID => "Versi IP", // Language::NL => "IP versie", +// Language::HU => "IP verzió", // } // } @@ -222,6 +229,7 @@ pub fn address_translation(language: Language) -> &'static str { // Language::UZ => "Transport protokoli", // Language::ID => "Protokol berjalan", // Language::NL => "Transportprotocol", +// Language::HU => "Szállítási protokoll", // } // } @@ -232,7 +240,7 @@ pub fn protocol_translation(language: Language) -> &'static str { Language::FR => "Protocole", Language::ES | Language::PT => "Protocolo", Language::PL => "Protokół", - Language::DE | Language::SV => "Protokoll", + Language::DE | Language::SV | Language::HU => "Protokoll", Language::UK | Language::RU => "Протокол", Language::ZH => "协议", Language::ZH_TW => "通訊協定", @@ -274,6 +282,7 @@ pub fn traffic_rate_translation<'a>(language: Language) -> Text<'a, StyleType> { Language::VI => "Lưu lượng truy cập", Language::ID => "Tingkat lalulintas", Language::NL => "Verkeerssnelheid", + Language::HU => "Adatráta", }) } @@ -300,6 +309,7 @@ pub fn traffic_rate_translation<'a>(language: Language) -> Text<'a, StyleType> { // Language::UZ => "Tegishli ulanishlar:", // Language::ID => "Koneksi yang berkaitan", // Language::NL => "Relevante verbindingen:", +// Language::HU => "Releváns kapcsolatok:", // }) // } @@ -328,6 +338,7 @@ pub fn settings_translation(language: Language) -> &'static str { Language::VI => "Cài đặt", Language::ID => "Pengaturan", Language::NL => "Instellingen", + Language::HU => "Beállítások", } } @@ -354,6 +365,7 @@ pub fn yes_translation<'a>(language: Language) -> Text<'a, StyleType> { Language::UZ => "Ha", Language::VI => "Chấp nhận", Language::ID => "Ya", + Language::HU => "Igen", }) } @@ -383,6 +395,7 @@ pub fn ask_quit_translation<'a>(language: Language) -> Text<'a, StyleType> { Language::VI => "Bạn có chắc là muốn thoát phiên phân tích này?", Language::ID => "Apa kamu yakin untuk berhenti analisa?", Language::NL => "Weet je zeker dat je deze analyse wilt afsluiten?", + Language::HU => "Biztosan le akarod állítani ezt az elemzést?", }) } @@ -412,6 +425,7 @@ pub fn quit_analysis_translation(language: Language) -> &'static str { Language::VI => "Thoát phiên phân tích", Language::ID => "Berhenti analisa", Language::NL => "Analyse afsluiten", + Language::HU => "Elemzés leállítása", } } @@ -441,6 +455,7 @@ pub fn ask_clear_all_translation<'a>(language: Language) -> Text<'a, StyleType> Language::VI => "Bạn có chắc là muốn xóa các thông báo?", Language::ID => "Apa kamu yakin untuk membersihkan notifikasi?", Language::NL => "Weet je zeker dat je alle meldingen wilt wissen?", + Language::HU => "Biztosan törölni akarod az értesítéseket?", }) } @@ -469,6 +484,7 @@ pub fn clear_all_translation(language: Language) -> &'static str { Language::VI => "Xóa tất cả", Language::ID => "Bersihkan semua", Language::NL => "Alles wissen", + Language::HU => "Összes törlése", } } @@ -497,6 +513,7 @@ pub fn hide_translation(language: Language) -> &'static str { Language::VI => "Ẩn", Language::ID => "Sembunyikan", Language::NL | Language::DE => "Verbergen", + Language::HU => "Elrejtés", } } @@ -525,6 +542,7 @@ pub fn network_adapter_translation(language: Language) -> &'static str { Language::UZ => "Tarmoq adapteri", Language::ID => "Adapter jaringan", Language::NL => "Netwerkadapter", + Language::HU => "Hálózati adapter", } } @@ -626,6 +644,10 @@ pub fn no_addresses_translation(language: Language) -> &'static str { "Er kan geen verkeer worden waargenomen omdat de geselecteerde adapter geen actieve adressen heeft...\n\n\ Als je zeker weet dat je verbonden bent met het internet, probeer dan een andere adapter te kiezen." } + Language::HU => { + "Nem megfigyelhető a forgalom mivel a kiválasztott adapternek nincs aktív címe...\n\n\ + Ha biztos vagy benne, hogy csatlakozva vagy az internethet, próbálj meg egy másik adaptert használni." + } } } @@ -727,6 +749,10 @@ pub fn waiting_translation(language: Language) -> &'static str { "Er is nog geen verkeer waargenomen. Wachten op netwerkpakketten...\n\n\ Weet je zeker dat je verbonden bent met het internet en de juiste adapter hebt geselecteerd?" } + Language::HU => { + "Nem észleltünk még forgalmat. Várakozás hálózati csomagokra...\n\n\ + Biztosan csatlakozva vagy az internethez és helyes adaptert választottál ki?" + } } } @@ -851,6 +877,11 @@ pub fn waiting_translation(language: Language) -> &'static str { // Gefilterde pakketten: 0\n\n\ // Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..." // ), +// Language::HU => format!( +// "Elfogott csomagok: {observed}\n\n\ +// Kiszűrt csomagok: 0\n\n\ +// Elfogtunk valamennyi csomagot, de egy se lett kiválasztva a megadott szűrők alapján..." +// ), // }) // } @@ -880,6 +911,7 @@ pub fn waiting_translation(language: Language) -> &'static str { // Language::VI => "Các gói tin đã được lọc", // Language::ID => "Paket data tersaring", // Language::NL => "Gefilterde pakketten", +// Language::HU => "Kiszűrt csomagok", // } // } @@ -908,6 +940,7 @@ pub fn waiting_translation(language: Language) -> &'static str { // Language::VI => "Các bytes đã được lọc", // Language::ID => "Bytes tersaring", // Language::NL => "Gefilterde bytes", +// Language::HU => "Kiszűrt bájtok", // } // } @@ -953,6 +986,7 @@ pub fn waiting_translation(language: Language) -> &'static str { // Language::VI => format!("({percentage} trên tổng cộng)"), // Language::ID => format!("({percentage} dari total)"), // Language::NL => format!("({percentage} van het totaal)"), +// Language::HU => format!("(Az egész {percentage}-a)"), // } // } @@ -979,6 +1013,7 @@ pub fn waiting_translation(language: Language) -> &'static str { // Language::UZ => "Har bir dastur protokoli uchun filtrlangan paketlar:", // Language::ID => "Paket data tersaring dari setiap protokol aplikasi:", // Language::NL => "Gefilterde pakketten per applicatieprotocol:", +// Language::HU => "Kiszűrt csomagok applikáció protokollonként:", // }) // } @@ -1023,8 +1058,10 @@ pub fn waiting_translation(language: Language) -> &'static str { // Ulanishni sevimlilar ro'yhatiga qo'shish uchun ulanish yaqinidagi yulduzcha belgisini bosing." // Language::ID => "Tidak ada yang ditampilkan untuk saatini.\n\ // untuk menambah koneksi ke favorit, klik dilogo bintang dekat dengan koneksi.", -// Language::NL => "Niets om te laten zien op dit moment.\n\ +// Language::NL => "Niets om te laten zien op dit moment.\n\ // Om een verbinding toe te voegen aan je favorieten, klik op het ster symbool naast de verbinding.", +// Language::HU => "Jelenleg nincs mutatni való.\n\ +// A kedvencekhez való hozzáadáshoz nyomj rá a csillag jelre egy adott kapcsolat mellett.", // }) // } @@ -1057,6 +1094,7 @@ pub fn error_translation(language: Language) -> &'static str { Language::VI => "Đã có lỗi xảy ra!", Language::ID => "Terjadi kesalahan!", Language::NL => "Er is een fout opgetreden!", + Language::HU => "Hiba történt!", } } @@ -1084,6 +1122,7 @@ pub fn error_translation(language: Language) -> &'static str { // Language::UZ => "ikkalasi ham", // Language::ID => "keduanya", // Language::NL => "beide", +// Language::HU => "mindkettő", // } // } @@ -1104,6 +1143,7 @@ pub fn error_translation(language: Language) -> &'static str { // Language::UK => "Усі протоколи", // Language::ID => "Semua protokol", // Language::NL => "Alle protocollen", +// Language::HU => "Minden protokoll", // } // } @@ -1131,6 +1171,7 @@ pub fn error_translation(language: Language) -> &'static str { // Language::UZ => "Barchasi", // Language::ID => "Semua", // Language::NL => "Alle", +// Language::HU => "Összes", // } // } @@ -1158,6 +1199,7 @@ pub fn packets_translation(language: Language) -> &'static str { Language::UZ => "paketlar", Language::VI => "các gói tin", Language::NL => "pakketten", + Language::HU => "csomagok", } } @@ -1187,6 +1229,7 @@ pub fn packets_translation(language: Language) -> &'static str { // Language::VI => "gói tin trên giây", // Language::ID => "paket per detik", // Language::NL => "pakketten per seconde", +// Language::HU => "Másodpercenkénti csomagok", // } // } @@ -1215,6 +1258,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::FA => "بایت ها", Language::FI => "tavua", Language::JA => "バイト", + Language::HU => "bájtok", } } @@ -1243,6 +1287,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::VI => "byte trên giây", // Language::ID => "bytes per detik", // Language::NL => "bytes per seconde", +// Language::HU => "Másodpercenkénti bájtok", // } // } @@ -1271,6 +1316,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::UZ => "eng so'nggi", // Language::ID => "paling terakhir", // Language::NL => "meest recent", +// Language::HU => "legutóbbi", // } // } @@ -1299,6 +1345,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::UZ => "eng ko'p paketlar", // Language::ID => "paket terbanyak", // Language::NL => "meeste pakketten", +// Language::HU => "legtöbb csomag", // } // } @@ -1326,6 +1373,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::JA => "バイト量の多い順", // Language::UZ => "eng ko'p bayt", // Language::NL => "meeste bytes", +// Language::HU => "legtöbb bájt", // } // } @@ -1351,6 +1399,7 @@ pub fn bytes_translation(language: Language) -> &'static str { // Language::UZ => "sevimlilar", // Language::ID => "favorit", // Language::NL => "favorieten", +// Language::HU => "kedvencek", // } // } @@ -1380,6 +1429,7 @@ pub fn notifications_title_translation<'a>(language: Language) -> Text<'a, Style Language::VI => "Tùy chỉnh thông báo của bạn", Language::ID => "Sesuaikan notifikasi Anda", Language::NL => "Pas je meldingen aan", + Language::HU => "Értesítések személyre szabása", }) } @@ -1409,6 +1459,7 @@ pub fn appearance_title_translation<'a>(language: Language) -> Text<'a, StyleTyp Language::VI => "Chọn chủ đề bạn muốn", Language::ID => "Pilih tema favorit Kamu", Language::NL => "Kies je favoriete thema", + Language::HU => "Válaszd ki a kedvenc témád", }) } @@ -1438,6 +1489,7 @@ pub fn active_filters_translation(language: Language) -> &'static str { Language::VI => "Bộ lọc đang hoạt động", Language::ID => "Filter aktif", Language::NL => "Actieve filters", + Language::HU => "Aktív szűrők", } } @@ -1467,6 +1519,7 @@ pub fn none_translation(language: Language) -> &'static str { Language::VI => "không có", Language::ID => "Tidak ada", Language::NL => "geen", + Language::HU => "semmi", } } @@ -1496,6 +1549,7 @@ pub fn none_translation(language: Language) -> &'static str { // Language::VI => "Chủ đề tối của Sniffnet", // Language::ID => "Tema gelap bawaan Sniffnet", // Language::NL => "Sniffnet's originele donkere thema", +// Language::HU => "A Sniffnet eredeti sötét témája", // } // } @@ -1524,6 +1578,7 @@ pub fn none_translation(language: Language) -> &'static str { // Language::VI => "Chủ đề sáng của Sniffnet", // Language::ID => "Tema terang bawaan Sniffnet", // Language::NL => "Sniffnet's originele lichte thema", +// Language::HU => "A Sniffnet eredeti világos témája", // } // } @@ -1553,6 +1608,7 @@ pub fn none_translation(language: Language) -> &'static str { // Language::VI => "Đắm chìm vào lưu lượng mạng", // Language::ID => "Untuk mendalami lalu lintas jaringan", // Language::NL => "Om in het netwerkverkeer te duiken", +// Language::HU => "Belemélyedés a hálózati forgalomba", // } // } @@ -1582,6 +1638,7 @@ pub fn none_translation(language: Language) -> &'static str { // Language::VI => "Chủ đề mộng mơ cho những kẻ mơ mộng", // Language::ID => "Tema yang indah dibuat untuk para pemimpi", // Language::NL => "Liefelijk thema gemaakt voor dromers", +// Language::HU => "Bájos téma az álmodozóknak", // } // } @@ -1610,6 +1667,7 @@ pub fn incoming_translation(language: Language) -> &'static str { Language::VI => "Đang tới", Language::ID => "Masuk", Language::NL => "Inkomend", + Language::HU => "Beérkező", } } @@ -1638,6 +1696,7 @@ pub fn outgoing_translation(language: Language) -> &'static str { Language::VI => "Đang hướng ra ngoài", Language::ID => "Keluar", Language::NL => "Uitgaand", + Language::HU => "Kimenő", } } @@ -1664,6 +1723,7 @@ pub fn notifications_translation(language: Language) -> &'static str { Language::VI => "Thông báo", Language::ID => "Pemberitahuan", Language::NL => "Notificaties", + Language::HU => "Értesítések", } } @@ -1687,6 +1747,7 @@ pub fn style_translation(language: Language) -> &'static str { Language::VI => "Chủ đề", Language::ID => "Gaya", Language::NL => "Stijl", + Language::HU => "Kinézet", } } @@ -1716,6 +1777,7 @@ pub fn language_translation(language: Language) -> &'static str { Language::VI => "Ngôn ngữ", Language::ID => "Bahasa", Language::NL => "Taal", + Language::HU => "Nyelv", } } @@ -1745,6 +1807,7 @@ pub fn overview_translation(language: Language) -> &'static str { Language::VI => "Tổng quan", Language::ID => "Ringkasan", Language::NL => "Overzicht", + Language::HU => "Áttekintés", } } @@ -1774,6 +1837,7 @@ pub fn overview_translation(language: Language) -> &'static str { // Language::VI => "Báo cho tôi biết khi vượt quá ngưỡng gói tin", // Language::ID => "Beritahu saya ketika ambang batas paket terlampaui", // Language::NL => "Geef me een melding wanneer een pakketdrempel is overschreden", +// Language::HU => "Értesíts amikor túllépnek egy csomagküszöböt", // } // } @@ -1803,6 +1867,7 @@ pub fn overview_translation(language: Language) -> &'static str { // Language::VI => "Báo cho tôi biết khi vượt quá ngưỡng bytes", // Language::ID => "Beritahu saya ketika ambang batas bytes terlampaui", // Language::NL => "Geef me een melding wanneer een byte-drempel is overschreden", +// Language::HU => "Értesíts amikor túllépnek egy bájtküszöböt", // } // } @@ -1829,6 +1894,7 @@ pub fn per_second_translation(language: Language) -> &'static str { Language::VI => "(trên giây)", Language::ID => "(per detik)", Language::NL => "(per seconde)", + Language::HU => "(másodpercenként)", } } @@ -1858,6 +1924,7 @@ pub fn per_second_translation(language: Language) -> &'static str { // Language::VI => "bạn cũng có thể chọn 'K', 'M' and 'G'", // Language::ID => "Anda juga dapat menentukan 'K', 'M' dan 'G'", // Language::NL => "je kunt ook 'K', 'M' en 'G' specificeren", +// Language::HU => "'K', 'M' és 'G' között is választhatsz", // } // } @@ -1889,6 +1956,7 @@ pub fn per_second_translation(language: Language) -> &'static str { // Language::VI => "Báo cho tôi biết khi dữ liệu mới được trao đổi từ mục yêu thích của tôi", // Language::ID => "Beritahu saya ketika data baru dipertukarkan dari favorit saya", // Language::NL => "Geef me een melding wanneer nieuwe gegevens worden uitgewisseld van mijn favorieten", +// Language::HU => "Értesíts új adatforgalomról a kedvencekből", // } // } @@ -1918,6 +1986,7 @@ pub fn threshold_translation(language: Language) -> &'static str { Language::VI => "Ngưỡng", Language::ID => "Ambang batas", Language::NL => "Grens", + Language::HU => "Küszöb", } } @@ -1942,6 +2011,7 @@ pub fn volume_translation(language: Language) -> &'static str { Language::UZ => "Ovoz balandligi", Language::VI => "Âm lượng", Language::ID => "Bunyi", + Language::HU => "Hangerő", } } @@ -1969,6 +2039,7 @@ pub fn sound_translation(language: Language) -> &'static str { Language::VI => "Âm thanh", Language::ID => "Suara", Language::NL => "Geluid", + Language::HU => "Hang", } } @@ -1998,6 +2069,7 @@ pub fn bytes_exceeded_translation(language: Language) -> &'static str { Language::VI => "Bytes đã vượt ngưỡng", Language::ID => "Ambang batas byte terlampaui", Language::NL => "Byte-drempel overschreden", + Language::HU => "Bájtküszöb meghaladva", } } @@ -2027,6 +2099,7 @@ pub fn bytes_exceeded_translation(language: Language) -> &'static str { // Language::VI => format!("{value} đã được trao đổi"), // Language::ID => format!("{value} telah dipertukarkan"), // Language::NL => format!("{value} zijn uitgewisseld"), +// Language::HU => format!("{value} cserélve"), // } // } @@ -2056,6 +2129,7 @@ pub fn packets_exceeded_translation(language: Language) -> &'static str { Language::VI => "Gói tin đã vượt ngưỡng", Language::ID => "Ambang batas paket terlampaui", Language::NL => "Pakketdrempel overschreden", + Language::HU => "Csomagküszöb meghaladva", } } @@ -2112,6 +2186,7 @@ pub fn packets_exceeded_translation(language: Language) -> &'static str { // Language::VI => format!("{value} gói tin đã được trao đổi"), // Language::ID => format!("{value} paket telah dipertukarkan"), // Language::NL => format!("{value} pakketten zijn uitgewisseld"), +// Language::HU => format!("{value} csomag került továbbításra"), // } // } @@ -2141,6 +2216,7 @@ pub fn favorite_transmitted_translation(language: Language) -> &'static str { Language::VI => "Mục ưa thích vừa có trao đổi", Language::ID => "Data baru dipertukarkan dari favorit", Language::NL => "Nieuwe gegevens uitgewisseld van favorieten", + Language::HU => "Új adatforgalom a kedvencekből", } } @@ -2265,6 +2341,11 @@ pub fn no_notifications_set_translation<'a>(language: Language) -> Text<'a, Styl Nadat je ze hebt ingeschakeld, zal deze pagina een logboek van je meldingen weergeven\n\n\ Je kunt meldingen inschakelen vanuit de instellingen:" } + Language::HU => { + "Még nem engedélyezted az értesítéseket!\n\n\ + Miután engedélyezed őket, ez az oldal fogja mutatni az összes értesítésed.\n\n\ + A beállításokban tudod őket engedélyezni:" + } }) } @@ -2366,6 +2447,10 @@ pub fn no_notifications_received_translation<'a>(language: Language) -> Text<'a, "Er is momenteel niets te zien...\n\n\ Wanneer je een melding ontvangt, wordt deze hier weergegeven" } + Language::HU => { + "Semmi látnivaló...\n\n\ + Amint érkezik egy értesítés, azt itt fogod látni." + } }) } @@ -2395,5 +2480,6 @@ pub fn only_last_30_translation(language: Language) -> &'static str { Language::VI => "Chỉ có 30 thông báo gần nhất được hiển thị", Language::ID => "Hanya 30 notifikasi terakhir yang ditampilkan", Language::NL => "Alleen de laatste 30 meldingen worden weergegeven", + Language::HU => "Itt csak az utolsó 30 értesítés van megjelenítve", } } diff --git a/src/translations/translations_2.rs b/src/translations/translations_2.rs index 93ab85ee5..b83121a86 100644 --- a/src/translations/translations_2.rs +++ b/src/translations/translations_2.rs @@ -26,6 +26,7 @@ pub fn new_version_available_translation(language: Language) -> &'static str { Language::VI => "Phiên bản mới đã sẵn sàng!", Language::ID => "Versi baru tersedia!", Language::NL => "Een nieuwere versie is beschikbaar!", + Language::HU => "Elérhető egy újabb verzió!", } } @@ -55,6 +56,7 @@ pub fn inspect_translation(language: Language) -> &'static str { Language::ID => "Memeriksa", Language::NL => "Inspecteren", Language::EL => "Επιθεώρηση", + Language::HU => "Megvizsgálás", } } @@ -84,6 +86,7 @@ pub fn connection_details_translation(language: Language) -> &'static str { Language::ID => "Rincian koneksi", Language::NL => "Verbindingsdetails", Language::EL => "Λεπτομέρειες σύνδεσης", + Language::HU => "Kapcsolat részletei", } } @@ -112,6 +115,7 @@ pub fn dropped_translation(language: Language) -> &'static str { Language::VI => "Mất", Language::ID => "Dihapus", Language::EL => "Απορριμμένα", + Language::HU => "Elvesztett", } } @@ -141,6 +145,7 @@ pub fn data_representation_translation(language: Language) -> &'static str { Language::ID => "Penyajian ulang data", Language::NL => "Gegevensweergave", Language::EL => "Αναπαράσταση δεδομένων", + Language::HU => "Adat reprezentació", } } @@ -170,6 +175,7 @@ pub fn host_translation(language: Language) -> &'static str { Language::ID => "Jaringan asal", Language::NL => "Netwerk host", Language::EL => "Κόμβος δικτύου", + Language::HU => "Hálózati gazda", } } @@ -199,6 +205,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str { Language::ID => "Hanya 30 teratas yang ditampilkan disini", Language::NL => "Alleen de bovenste 30 items worden hier weergegeven", Language::EL => "Εμφανίζονται μόνο τα κορυφαία 30 στοιχεία", + Language::HU => "Itt csak az első 30 elem van megjelenítve", } } @@ -225,6 +232,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str { // Language::UZ => "Saralash turi", // Language::ID => "Urut berdasarkan", // Language::NL => "Sorteren op", +// Language::HU => "Rendezés a következő szerint:", // _ => "Sort by", // } // } @@ -255,6 +263,7 @@ pub fn local_translation(language: Language) -> &'static str { Language::ID => "Jaringan lokal", Language::NL => "Lokaal netwerk", Language::EL => "Τοπικό δίκτυο", + Language::HU => "Helyi hálózat", } } @@ -284,6 +293,7 @@ pub fn unknown_translation(language: Language) -> &'static str { Language::ID => "Lokasi tidak diketahui", Language::NL => "Onbekende locatie", Language::EL => "Άγνωστη τοποθεσία", + Language::HU => "Ismeretlen hely", } } @@ -313,6 +323,7 @@ pub fn your_network_adapter_translation(language: Language) -> &'static str { Language::ID => "Adaptor jaringan kamu", Language::NL => "Uw netwerkadapter", Language::EL => "Ο προσαρμογέας δικτύου σας", + Language::HU => "A hálózati adaptered", } } @@ -342,6 +353,7 @@ pub fn socket_address_translation(language: Language) -> &'static str { Language::ID => "Alamat sambungan", Language::NL => "Socket adres", Language::EL => "Διεύθυνση υποδοχής", + Language::HU => "Socket cím", } } @@ -371,6 +383,7 @@ pub fn mac_address_translation(language: Language) -> &'static str { Language::ID => "Alamat MAC", Language::NL => "MAC-adres", Language::EL => "Διεύθυνση MAC", + Language::HU => "MAC-cím", } } @@ -399,6 +412,7 @@ pub fn source_translation(language: Language) -> &'static str { Language::ID => "Asal", Language::NL => "Bron", Language::EL => "Πηγή", + Language::HU => "Forrás", } } @@ -425,6 +439,7 @@ pub fn destination_translation(language: Language) -> &'static str { Language::ID => "Tujuan", Language::NL => "Bestemming", Language::EL => "Προορισμός", + Language::HU => "Cél", } } @@ -452,6 +467,7 @@ pub fn fqdn_translation(language: Language) -> &'static str { Language::ID => "Nama domain yang memenuhi syarat", Language::NL => "Volledig gekwalificeerde domeinnaam", Language::EL => "Πλήρως προσδιορισμένο όνομα τομέα", + Language::HU => "Teljesen minősített tartománynév", } } @@ -481,6 +497,7 @@ pub fn fqdn_translation(language: Language) -> &'static str { // Language::ID => "Nama System Otomatis", // Language::NL => "Naam van het autonome systeem", // Language::EL => "Όνομα αυτόνομου συστήματος", +// Language::HU => "Autonóm rendszer név", // } // } @@ -510,6 +527,7 @@ pub fn transmitted_data_translation(language: Language) -> &'static str { Language::ID => "Data terkirim", Language::NL => "Verzonden gegevens", Language::EL => "Μεταδιδόμενα δεδομένα", + Language::HU => "Átvitt adat", } } @@ -536,6 +554,7 @@ pub fn country_translation(language: Language) -> &'static str { Language::VI => "Quốc gia", Language::ID => "Negara", Language::EL => "Χώρα", + Language::HU => "Ország", } } @@ -563,6 +582,7 @@ pub fn domain_translation(language: Language) -> &'static str { Language::VI => "Miền", Language::NL => "Domeinnaam", Language::EL => "Τομέας", + Language::HU => "Tartománynév", } } @@ -592,6 +612,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str { Language::ID => "Hanya tunjukkan favorit", Language::NL => "Toon alleen favorieten", Language::EL => "Εμφάνιση μόνο αγαπημένων", + Language::HU => "Csak kedvencek mutatása", } } @@ -619,6 +640,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str { // Language::PT => "Filtros de busca", // Language::ID => "Filter Pencarian", // Language::NL => "Zoekfilters", +// Language::HU => "Keresési szűrők", // _ => "Search filters", // } // } @@ -651,6 +673,7 @@ pub fn no_search_results_translation(language: Language) -> &'static str { Language::EL => { "Δεν υπάρχουν διαθέσιμα αποτελέσματα σύμφωνα με τα καθορισμένα φίλτρα αναζήτησης" } + Language::HU => "Nincs a megadott keresési szűrőknek megfelelő találat", } } @@ -687,6 +710,7 @@ pub fn showing_results_translation( format!("{start}-{end} van de {total} totale resultaten worden weergegeven") } Language::EL => format!("Εμφάνιση {start}-{end} από {total} συνολικά αποτελέσματα"), + Language::HU => format!("{start}-{end}. találatok megjelenítve, összesen {total}"), } } @@ -716,5 +740,6 @@ pub fn color_gradients_translation(language: Language) -> &'static str { Language::ID => "Aplikasikan gradasi warna", Language::NL => "Kleurverlopen toepassen", Language::EL => "Εφαρμογή χρωματικών διαβαθμίσεων", + Language::HU => "Színátmenet alkalmazása", } } diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs index 08a0dc7af..06909d04c 100644 --- a/src/translations/translations_3.rs +++ b/src/translations/translations_3.rs @@ -27,6 +27,7 @@ pub fn general_translation(language: Language) -> &'static str { Language::ID => "Umum", Language::NL => "Algemeen", Language::EL => "Γενικά", + Language::HU => "Általános", _ => "General", } } @@ -56,6 +57,7 @@ pub fn zoom_translation(language: Language) -> &'static str { Language::UK => "Масштабування", Language::ID => "Perbesar", Language::EL => "Εστίαση", + Language::HU => "Közelítés", _ => "Zoom", } } @@ -85,6 +87,7 @@ pub fn mmdb_files_translation(language: Language) -> &'static str { Language::ID => "Berkas database", Language::NL => "Database bestanden", Language::EL => "Αρχεία βάσης δεδομένων", + Language::HU => "Adatbázis fájlok", _ => "Database files", } } @@ -116,6 +119,7 @@ pub fn params_not_editable_translation(language: Language) -> &'static str { Language::EL => { "Οι ακόλουθες παράμετροι δεν μπορούν να τροποποιηθούν κατά τη διάρκεια της ανάλυσης" } + Language::HU => "A következő paramétereken nem lehet elemzés közben változtatni", _ => "The following parameters can't be modified during the analysis", } } @@ -144,6 +148,7 @@ pub fn custom_style_translation(language: Language) -> &'static str { Language::ID => "Ubah Model", Language::NL => "Aangepaste stijl", Language::EL => "Προσαρμοσμένο στυλ", + Language::HU => "Egyedi stílus", _ => "Custom style", } } @@ -171,6 +176,7 @@ pub fn copy_translation(language: Language) -> &'static str { Language::ID => "Salin", Language::NL => "Kopiëren", Language::EL => "Αντιγραφή", + Language::HU => "Másolás", _ => "Copy", } } @@ -185,7 +191,9 @@ pub fn port_translation(language: Language) -> &'static str { | Language::RO | Language::UZ | Language::SV - | Language::TR => "Port", + | Language::TR + | Language::ID + | Language::HU => "Port", // Language::FA => "درگاه", Language::ES => "Puerto", Language::IT | Language::PT => "Porta", @@ -196,7 +204,6 @@ pub fn port_translation(language: Language) -> &'static str { Language::ZH_TW => "連接埠", Language::KO => "포트", Language::UK => "Порт", - Language::ID => "Port", Language::NL => "Poort", Language::EL => "Θύρα", _ => "Port", @@ -227,6 +234,7 @@ pub fn port_translation(language: Language) -> &'static str { // Language::ID => "Filter salah", // Language::NL => "Ongeldige filters", // Language::EL => "Μη έγκυρα φίλτρα", +// Language::HU => "Érvénytelen szűrők", // _ => "Invalid filters", // } // } @@ -255,6 +263,7 @@ pub fn messages_translation(language: Language) -> &'static str { Language::ID => "Pesan", Language::NL => "Berichten", Language::EL => "Μηνύματα", + Language::HU => "Üzenetek", _ => "Messages", } } @@ -283,6 +292,7 @@ pub fn link_type_translation(language: Language) -> &'static str { Language::UK => "Різновид зʼєднання", Language::ID => "Tipe koneksi", Language::EL => "Τύπος σύνδεσης", + Language::HU => "Link típus", _ => "Link type", } } @@ -346,6 +356,7 @@ pub fn unsupported_link_type_translation(language: Language) -> &'static str { Language::EL => { "Ο τύπος σύνδεσης που σχετίζεται με αυτόν τον προσαρμογέα δεν υποστηρίζεται ακόμη από το Sniffnet..." } + Language::HU => "Az ezzel az adapterrel asszociált link típust még nem támogatja a Sniffnet...", _ => "The link type associated with this adapter is not supported by Sniffnet yet...", } } @@ -375,6 +386,7 @@ pub fn unsupported_link_type_translation(language: Language) -> &'static str { // Language::ID => "Pilih model / gaya dari berkas", // Language::NL => "Selecteer stijl vanuit een bestand", // Language::EL => "Επιλογή στυλ από αρχείο", +// Language::HU => "Stílus kiválasztása fájlból", // _ => "Select style from a file", // } // } @@ -404,6 +416,7 @@ pub fn unsupported_link_type_translation(language: Language) -> &'static str { // Language::ID => "Pilih berkas database", // Language::NL => "Selecteer database bestand", // Language::EL => "Επιλογή αρχείου βάσης δεδομένων", +// Language::HU => "Adatbázis fájl kiválasztása", // _ => "Select database file", // } // } @@ -433,6 +446,7 @@ pub fn unsupported_link_type_translation(language: Language) -> &'static str { // Language::ID => "Filter berdasarkan jaringan asal", // Language::NL => "Filteren op netwerk host", // Language::EL => "Φίλτρο ανά διακομιστή δικτύου", +// Language::HU => "Szűrés hálózati gazda szerint", // _ => "Filter by network host", // } // } @@ -459,6 +473,7 @@ pub fn service_translation(language: Language) -> &'static str { Language::ID => "Layanan", Language::NL => "Dienst", Language::EL => "Υπηρεσία", + Language::HU => "Szolgáltatás", _ => "Service", } } @@ -488,6 +503,7 @@ pub fn export_capture_translation(language: Language) -> &'static str { Language::ES => "Exportar archivo de captura", Language::NL => "Exporteer capture bestand", Language::EL => "Εξαγωγή αρχείου καταγραφής", + Language::HU => "Gyűjtött forgalom exportálása fájlba", _ => "Export capture file", } } @@ -516,6 +532,7 @@ pub fn directory_translation(language: Language) -> &'static str { Language::ES => "Directorio", Language::NL => "Map", Language::EL => "Κατάλογος", + Language::HU => "Mappa", _ => "Directory", } } @@ -545,6 +562,7 @@ pub fn select_dest_directory_translation(language: Language) -> &'static str { Language::ES => "Selecciona el directorio de destino", Language::NL => "Selecteer doelmap", Language::EL => "Επιλογή καταλόγου προορισμού", + Language::HU => "Cél mappa kiválasztása", _ => "Select destination directory", } } @@ -574,6 +592,7 @@ pub fn file_name_translation(language: Language) -> &'static str { Language::ES => "Nombre del archivo", Language::NL => "Bestandsnaam", Language::EL => "Όνομα αρχείου", + Language::HU => "Fájlnév", _ => "File name", } } @@ -602,6 +621,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str { Language::ID => "Mode gambar kecil", Language::NL => "Miniatuur modus", Language::EL => "Λειτουργία μικρογραφιών", + Language::HU => "Miniatűr nézet", _ => "Thumbnail mode", } } @@ -630,6 +650,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str { // Language::ID => "Apakah kamu mau belajar lebih lanjut?", // Language::ES => "¿Quieres aprender más?", // Language::NL => "Wil je meer leren?", +// Language::HU => "Szeretnél többet megtudni?" // _ => "Do you want to learn more?", // } // } diff --git a/src/translations/translations_4.rs b/src/translations/translations_4.rs index aad99e91e..9cd044924 100644 --- a/src/translations/translations_4.rs +++ b/src/translations/translations_4.rs @@ -23,6 +23,7 @@ pub fn reserved_address_translation(language: Language, info: &str) -> String { Language::VI => format!("Địa chỉ dự trữ ({info})"), Language::TR => format!("Rezerve edilmiş adres ({info})"), Language::SV => format!("Reserverad adress ({info})"), + Language::HU => format!("Foglalt cím ({info})"), _ => format!("Reserved address ({info})"), } } @@ -47,6 +48,7 @@ pub fn share_feedback_translation(language: Language) -> &'static str { Language::TR => "Geri bildirimlerinizi paylaşın", Language::UK => "Надіслати відгук", Language::SV => "Dela din feedback", + Language::HU => "Visszajelzés megosztása", _ => "Share your feedback", } } @@ -70,6 +72,7 @@ pub fn share_feedback_translation(language: Language) -> &'static str { // Language::ES => "Excluidos", // Language::VI => "Loại trừ", // Language::TR => "Hariç tutulan", +// Language::HU => "Kizárva", // _ => "Excluded", // } // } @@ -94,6 +97,7 @@ pub fn capture_file_translation(language: Language) -> &'static str { Language::TR => "Yakalama dosyası", Language::UK => "Захопити файл", Language::SV => "Inspelningsfil", + Language::HU => "Adatforgalmi fájl", _ => "Capture file", } } @@ -118,6 +122,7 @@ pub fn select_file_translation(language: Language) -> &'static str { Language::TR => "Dosya seç", Language::UK => "Обрати файл", Language::SV => "Välj fil", + Language::HU => "Fájl kiválasztása", _ => "Select file", } } @@ -196,6 +201,10 @@ pub fn reading_from_pcap_translation(language: Language) -> &'static str { "Läser paket från fil...\n\n\ Är du säker på att filen du valde inte är tom?" } + Language::HU => { + "Csomagok olvasása fájlból...\n\n\ + Biztos, hogy a kiválasztott fájl nem üres?" + } _ => { "Reading packets from file...\n\n\ Are you sure the file you selected isn't empty?" @@ -222,6 +231,7 @@ pub fn data_exceeded_translation(language: Language) -> &'static str { Language::TR => "Veri limiti aşıldı", Language::UK => "Перевищено ліміт даних", Language::SV => "Datagräns överskriden", + Language::HU => "Adatküszöb túllépve", _ => "Data threshold exceeded", } } @@ -246,6 +256,7 @@ pub fn bits_exceeded_translation(language: Language) -> &'static str { Language::TR => "Bit limiti aşıldı", Language::UK => "Перевищено ліміт бітів", Language::SV => "Bitgräns överskriden", + Language::HU => "Bitküszöb túllépve", _ => "Bits threshold exceeded", } } @@ -269,6 +280,7 @@ pub fn bits_translation(language: Language) -> &'static str { Language::ZH_TW => "位元", Language::VI | Language::TR => "bit", Language::UK => "біти", + Language::HU => "bitek", _ => "bits", } } @@ -290,6 +302,7 @@ pub fn pause_translation(language: Language) -> &'static str { Language::VI => "Tạm dừng", Language::TR => "Duraklat", Language::UK => "Пауза", + Language::HU => "Szüneteltetés", _ => "Pause", } } @@ -315,6 +328,7 @@ pub fn resume_translation(language: Language) -> &'static str { Language::TR => "Devam et", Language::UK => "Продовжити", Language::SV => "Återuppta", + Language::HU => "Folytatás", _ => "Resume", } } diff --git a/src/translations/translations_5.rs b/src/translations/translations_5.rs index 7e67f5e2a..f7598c316 100644 --- a/src/translations/translations_5.rs +++ b/src/translations/translations_5.rs @@ -19,6 +19,7 @@ pub fn filter_traffic_translation(language: Language) -> String { Language::UK => "Фільтр трафіку", Language::SV => "Filtrera trafik", Language::EL => "Φιλτράρισμα ροής", + Language::HU => "Forgalom szűrése", _ => "Filter traffic", } .to_string() @@ -42,6 +43,7 @@ pub fn traffic_source_translation(language: Language) -> &'static str { Language::UK => "Джерело трафіку", Language::SV => "Trafikkälla", Language::EL => "Πηγή ροής", + Language::HU => "Forgalom forrása", _ => "Traffic source", } } @@ -62,6 +64,7 @@ pub fn remote_notifications_translation(language: Language) -> &'static str { Language::UK => "Віддалені сповіщення", Language::SV => "Fjärrnotiser", Language::EL => "Απομακρυσμένες ειδοποιήσεις", + Language::HU => "Távoli értesítések", _ => "Remote notifications", } } @@ -82,6 +85,7 @@ pub fn ip_blacklist_translation(language: Language) -> &'static str { Language::UK => "Чорний список IP-адрес", Language::SV => "IP-svartlista", Language::EL => "Λίστα μπλοκαρισμένων διευθύνσεων", + Language::HU => "IP feketelista", _ => "IP blacklist", } } @@ -102,6 +106,7 @@ pub fn blacklisted_transmitted_translation(language: Language) -> &'static str { Language::UK => "Отримано нові дані з IP-адреси з чорного списку", Language::SV => "Ny data utbytt från en svartlistad IP-adress", Language::EL => "Νέα δεδομένα ανταλλαχθηκαν από μια μπλοκαρισμένη διεύθυνση", + Language::HU => "Új adatforgalom egy feketelistán lévő IP-vel", _ => "New data exchanged from a blacklisted IP", } } @@ -122,13 +127,16 @@ pub fn only_show_blacklisted_translation(language: Language) -> &'static str { Language::UK => "Показувати лише заблоковані", Language::SV => "Visa endast svartlistade", Language::EL => "Εμφάνιση μόνο μπλοκαρισμένων διευθύνσεων", + Language::HU => "Csak feketelistán lévők mutatása", _ => "Only show blacklisted", } } pub fn program_translation(language: Language) -> &'static str { match language { - Language::EN | Language::TR | Language::RO | Language::ID | Language::SV => "Program", + Language::EN | Language::TR | Language::RO | Language::ID | Language::SV | Language::HU => { + "Program" + } Language::IT => "Programma", Language::DE => "Programm", Language::ZH => "程序", @@ -158,6 +166,7 @@ pub fn no_favorites_saved_translation(language: Language) -> &'static str { Language::UK => "Ще немає збережених улюблених", Language::SV => "Inga favoriter sparade ännu", Language::EL => "Δεν έχει αποθηκευτεί κανένα αγαπημένο στοιχείο ακόμη", + Language::HU => "Még nincsenek elmentve kedvencek", _ => "No favorites saved yet", } } diff --git a/src/translations/types/language.rs b/src/translations/types/language.rs index 83d4b450b..e34488d7a 100644 --- a/src/translations/types/language.rs +++ b/src/translations/types/language.rs @@ -1,14 +1,14 @@ use std::fmt; -use iced::widget::Svg; use iced::widget::svg::Handle; +use iced::widget::Svg; use serde::{Deserialize, Serialize}; -use crate::StyleType; use crate::countries::flags_pictures::{ - CN, CZ, DE, ES, FI, FR, GB, GR, ICONS_SIZE_BIG, ID, IT, JP, KR, NL, PL, PT, RO, RU, SE, TR, TW, - UA, UZ, VN, + CN, CZ, DE, ES, FI, FR, GB, GR, HU, ICONS_SIZE_BIG, ID, IT, JP, KR, NL, PL, PT, RO, RU, SE, TR, + TW, UA, UZ, VN, }; +use crate::StyleType; /// This enum defines the available languages. #[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Hash, Default)] @@ -63,10 +63,12 @@ pub enum Language { NL, /// Czech CS, + /// Hungarian + HU, } impl Language { - pub const ALL: [Language; 23] = [ + pub const ALL: [Language; 24] = [ Language::EN, Language::CS, Language::DE, @@ -74,6 +76,7 @@ impl Language { Language::ES, Language::FI, Language::FR, + Language::HU, Language::ID, Language::IT, Language::JA, @@ -118,6 +121,7 @@ impl Language { Language::ID => ID, Language::NL => NL, Language::CS => CZ, + Language::HU => HU, }))) .width(ICONS_SIZE_BIG) } @@ -139,6 +143,7 @@ impl Language { | Language::ES | Language::SV | Language::EL + | Language::HU ) } } @@ -170,6 +175,7 @@ impl fmt::Display for Language { Language::ID => "Bahasa Indonesia", Language::NL => "Nederlands", Language::CS => "Čeština", + Language::HU => "Magyar", }; write!(f, "{self:?} - {lang_str}") }
glitsj16
glitsj16

📦
golanbenoni
golanbenoni

💵
guilherme-demarchi
guilherme-demarchi

🌍
hirotake111
hirotake111

🌍
islameehassan
islameehassan

💻
louis-ym4
louis-ym4

🎨
luca3s
luca3s

🌍
mbe57
mbe57

🤔
mbe57
mbe57

🤔
pia
pia

🌍
pin
pin

📦
shu-kitamura
shu-kitamura

💻 🌍
starccy
starccy

💻
tiansheng li
tiansheng li

💵
vtiinanen
vtiinanen

🌍
yossarian
yossarian

🌍
yossarian
yossarian

🌍
陈寒彤
陈寒彤

🌍