mirror of
https://github.com/GyulyVGC/sniffnet.git
synced 2025-12-23 22:29:01 -05:00
replace old filters with BPF filter
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
//!
|
||||
//! It contains elements to select network adapter and traffic filters.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
|
||||
use iced::Length::FillPortion;
|
||||
@@ -26,21 +25,18 @@
|
||||
use crate::gui::types::export_pcap::ExportPcap;
|
||||
use crate::gui::types::message::Message;
|
||||
use crate::networking::types::capture_context::CaptureSource;
|
||||
use crate::networking::types::filters::Filters;
|
||||
use crate::networking::types::ip_collection::AddressCollection;
|
||||
use crate::networking::types::port_collection::PortCollection;
|
||||
use crate::translations::translations::{
|
||||
address_translation, addresses_translation, choose_adapters_translation,
|
||||
ip_version_translation, protocol_translation, select_filters_translation, start_translation,
|
||||
select_filters_translation, start_translation,
|
||||
};
|
||||
use crate::translations::translations_3::{
|
||||
directory_translation, export_capture_translation, file_name_translation, port_translation,
|
||||
directory_translation, export_capture_translation, file_name_translation,
|
||||
};
|
||||
use crate::translations::translations_4::import_capture_translation;
|
||||
use crate::utils::formatted_strings::{get_invalid_filters_string, get_path_termination_string};
|
||||
use crate::utils::formatted_strings::get_path_termination_string;
|
||||
use crate::utils::types::file_info::FileInfo;
|
||||
use crate::utils::types::icon::Icon;
|
||||
use crate::{ConfigSettings, IpVersion, Language, Protocol, StyleType};
|
||||
use crate::{ConfigSettings, Language, StyleType};
|
||||
|
||||
/// Computes the body of gui initial page
|
||||
pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
@@ -61,17 +57,7 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
);
|
||||
let col_capture_source = Column::new().push(col_adapter).push(col_import_pcap);
|
||||
|
||||
let ip_active = &sniffer.filters.ip_versions;
|
||||
let col_ip_buttons = col_ip_buttons(ip_active, font, language);
|
||||
|
||||
let protocol_active = &sniffer.filters.protocols;
|
||||
let col_protocol_buttons = col_protocol_buttons(protocol_active, font, language);
|
||||
|
||||
let address_active = &sniffer.filters.address_str;
|
||||
let col_address_filter = col_address_input(address_active, font, language);
|
||||
|
||||
let port_active = &sniffer.filters.port_str;
|
||||
let col_port_filter = col_port_input(port_active, font, language);
|
||||
let col_bpf_filter = col_bpf_input(&sniffer.bpf_filter, font);
|
||||
|
||||
let filters_pane = Column::new()
|
||||
.width(FillPortion(6))
|
||||
@@ -83,18 +69,7 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
.class(TextType::Title)
|
||||
.size(FONT_SIZE_TITLE),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.push(col_ip_buttons)
|
||||
.push(col_protocol_buttons),
|
||||
)
|
||||
.push(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.push(col_address_filter)
|
||||
.push(col_port_filter),
|
||||
)
|
||||
.push(col_bpf_filter)
|
||||
.push(Rule::horizontal(40))
|
||||
.push(get_export_pcap_group(
|
||||
&sniffer.capture_source,
|
||||
@@ -103,16 +78,11 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
font,
|
||||
))
|
||||
.push(
|
||||
Container::new(button_start(
|
||||
font,
|
||||
language,
|
||||
color_gradient,
|
||||
&sniffer.filters,
|
||||
))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_y(Alignment::Start)
|
||||
.align_x(Alignment::Center),
|
||||
Container::new(button_start(font, language, color_gradient))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_y(Alignment::Start)
|
||||
.align_x(Alignment::Center),
|
||||
);
|
||||
|
||||
let body = Column::new().push(Space::with_height(5)).push(
|
||||
@@ -125,112 +95,20 @@ pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
Container::new(body).height(Length::Fill)
|
||||
}
|
||||
|
||||
fn col_ip_buttons(
|
||||
active_ip_filters: &HashSet<IpVersion>,
|
||||
font: Font,
|
||||
language: Language,
|
||||
) -> Column<'_, Message, StyleType> {
|
||||
let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
|
||||
for option in IpVersion::ALL {
|
||||
let is_active = active_ip_filters.contains(&option);
|
||||
let check_symbol = if is_active { "✔" } else { "✘" };
|
||||
buttons_row = buttons_row.push(
|
||||
Button::new(
|
||||
Text::new(format!("{option} {check_symbol}"))
|
||||
.align_x(Alignment::Center)
|
||||
.align_y(Alignment::Center)
|
||||
.font(font),
|
||||
)
|
||||
.width(80)
|
||||
.height(35)
|
||||
.class(if is_active {
|
||||
ButtonType::BorderedRoundSelected
|
||||
} else {
|
||||
ButtonType::BorderedRound
|
||||
})
|
||||
.on_press(Message::IpVersionSelection(option, !is_active)),
|
||||
);
|
||||
}
|
||||
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(7)
|
||||
.push(
|
||||
Text::new(ip_version_translation(language))
|
||||
.font(font)
|
||||
.class(TextType::Subtitle)
|
||||
.size(FONT_SIZE_SUBTITLE),
|
||||
)
|
||||
.push(buttons_row)
|
||||
}
|
||||
|
||||
fn col_protocol_buttons(
|
||||
active_protocol_filters: &HashSet<Protocol>,
|
||||
font: Font,
|
||||
language: Language,
|
||||
) -> Column<'_, Message, StyleType> {
|
||||
let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5));
|
||||
for option in Protocol::ALL {
|
||||
let is_active = active_protocol_filters.contains(&option);
|
||||
let check_symbol = if is_active { "✔" } else { "✘" };
|
||||
buttons_row = buttons_row.push(
|
||||
Button::new(
|
||||
Text::new(format!("{option} {check_symbol}"))
|
||||
.align_x(Alignment::Center)
|
||||
.align_y(Alignment::Center)
|
||||
.font(font),
|
||||
)
|
||||
.width(80)
|
||||
.height(35)
|
||||
.class(if is_active {
|
||||
ButtonType::BorderedRoundSelected
|
||||
} else {
|
||||
ButtonType::BorderedRound
|
||||
})
|
||||
.on_press(Message::ProtocolSelection(option, !is_active)),
|
||||
);
|
||||
}
|
||||
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(7)
|
||||
.push(
|
||||
Text::new(protocol_translation(language))
|
||||
.font(font)
|
||||
.class(TextType::Subtitle)
|
||||
.size(FONT_SIZE_SUBTITLE),
|
||||
)
|
||||
.push(buttons_row)
|
||||
}
|
||||
|
||||
fn col_address_input(
|
||||
value: &str,
|
||||
font: Font,
|
||||
language: Language,
|
||||
) -> Column<'_, Message, StyleType> {
|
||||
let is_error = if value.is_empty() {
|
||||
false
|
||||
} else {
|
||||
AddressCollection::new(value).is_none()
|
||||
};
|
||||
fn col_bpf_input(value: &str, font: Font) -> Column<'_, Message, StyleType> {
|
||||
let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
|
||||
TextInput::new(AddressCollection::PLACEHOLDER_STR, value)
|
||||
TextInput::new("Berkeley Packet Filter (BPF)", value)
|
||||
.padding([3, 5])
|
||||
.on_input(Message::AddressFilter)
|
||||
.on_input(Message::BpfFilter)
|
||||
.font(font)
|
||||
.width(310)
|
||||
.class(if is_error {
|
||||
TextInputType::Error
|
||||
} else {
|
||||
TextInputType::Standard
|
||||
}),
|
||||
.class(TextInputType::Standard),
|
||||
);
|
||||
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(7)
|
||||
.push(
|
||||
Text::new(address_translation(language))
|
||||
Text::new("Berkeley Packet Filter (BPF)")
|
||||
.font(font)
|
||||
.class(TextType::Subtitle)
|
||||
.size(FONT_SIZE_SUBTITLE),
|
||||
@@ -238,44 +116,12 @@ fn col_address_input(
|
||||
.push(input_row)
|
||||
}
|
||||
|
||||
fn col_port_input(value: &str, font: Font, language: Language) -> Column<'_, Message, StyleType> {
|
||||
let is_error = if value.is_empty() {
|
||||
false
|
||||
} else {
|
||||
PortCollection::new(value).is_none()
|
||||
};
|
||||
let input_row = Row::new().padding(Padding::ZERO.left(5)).push(
|
||||
TextInput::new(PortCollection::PLACEHOLDER_STR, value)
|
||||
.padding([3, 5])
|
||||
.on_input(Message::PortFilter)
|
||||
.font(font)
|
||||
.width(180)
|
||||
.class(if is_error {
|
||||
TextInputType::Error
|
||||
} else {
|
||||
TextInputType::Standard
|
||||
}),
|
||||
);
|
||||
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.spacing(7)
|
||||
.push(
|
||||
Text::new(port_translation(language))
|
||||
.font(font)
|
||||
.class(TextType::Subtitle)
|
||||
.size(FONT_SIZE_SUBTITLE),
|
||||
)
|
||||
.push(input_row)
|
||||
}
|
||||
|
||||
fn button_start(
|
||||
fn button_start<'a>(
|
||||
font: Font,
|
||||
language: Language,
|
||||
color_gradient: GradientType,
|
||||
filters: &Filters,
|
||||
) -> Tooltip<'_, Message, StyleType> {
|
||||
let mut content = button(
|
||||
) -> Tooltip<'a, Message, StyleType> {
|
||||
let content = button(
|
||||
Icon::Rocket
|
||||
.to_text()
|
||||
.size(25)
|
||||
@@ -285,18 +131,12 @@ fn button_start(
|
||||
.padding(10)
|
||||
.height(80)
|
||||
.width(160)
|
||||
.class(ButtonType::Gradient(color_gradient));
|
||||
.class(ButtonType::Gradient(color_gradient))
|
||||
.on_press(Message::Start);
|
||||
|
||||
let mut tooltip = start_translation(language).to_string();
|
||||
let tooltip = start_translation(language).to_string();
|
||||
//tooltip.push_str(" [⏎]");
|
||||
let mut position = Position::Top;
|
||||
|
||||
if filters.are_valid() {
|
||||
content = content.on_press(Message::Start);
|
||||
} else {
|
||||
tooltip = get_invalid_filters_string(filters, language);
|
||||
position = Position::FollowCursor;
|
||||
}
|
||||
let position = Position::Top;
|
||||
|
||||
Tooltip::new(content, Text::new(tooltip).font(font), position)
|
||||
.gap(5)
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
use crate::networking::types::data_info::DataInfo;
|
||||
use crate::networking::types::data_info_host::DataInfoHost;
|
||||
use crate::networking::types::data_representation::DataRepr;
|
||||
use crate::networking::types::filters::Filters;
|
||||
use crate::networking::types::host::Host;
|
||||
use crate::networking::types::service::Service;
|
||||
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
|
||||
@@ -37,7 +36,6 @@
|
||||
};
|
||||
use crate::translations::translations_3::{service_translation, unsupported_link_type_translation};
|
||||
use crate::translations::translations_4::{excluded_translation, reading_from_pcap_translation};
|
||||
use crate::utils::formatted_strings::get_active_filters_string;
|
||||
use crate::utils::types::icon::Icon;
|
||||
use crate::{ConfigSettings, Language, RunningPage, StyleType};
|
||||
use iced::Length::{Fill, FillPortion};
|
||||
@@ -83,7 +81,7 @@ pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
}
|
||||
(observed, 0) => {
|
||||
//no packets have been filtered but some have been observed
|
||||
body = body_no_observed(&sniffer.filters, observed, font, language, dots);
|
||||
body = body_no_observed(&sniffer.bpf_filter, observed, font, language, dots);
|
||||
}
|
||||
(_observed, _filtered) => {
|
||||
//observed > filtered > 0 || observed = filtered > 0
|
||||
@@ -175,7 +173,7 @@ fn body_no_packets<'a>(
|
||||
}
|
||||
|
||||
fn body_no_observed<'a>(
|
||||
filters: &Filters,
|
||||
bpf: &'a str,
|
||||
observed: u128,
|
||||
font: Font,
|
||||
language: Language,
|
||||
@@ -192,7 +190,7 @@ fn body_no_observed<'a>(
|
||||
.align_x(Alignment::Center)
|
||||
.push(vertical_space())
|
||||
.push(Icon::Funnel.to_text().size(60))
|
||||
.push(get_active_filters_col(filters, language, font))
|
||||
.push(get_active_filters_col(bpf, language, font))
|
||||
.push(Rule::horizontal(20))
|
||||
.push(tot_packets_text)
|
||||
.push(Text::new(dots.to_owned()).font(font).size(50))
|
||||
@@ -464,7 +462,7 @@ pub fn service_bar<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
|
||||
fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> {
|
||||
let ConfigSettings {
|
||||
style, language, ..
|
||||
} = sniffer.configs.settings;
|
||||
@@ -585,25 +583,25 @@ fn col_data_representation<'a>(
|
||||
ret_val
|
||||
}
|
||||
|
||||
fn donut_row<'a>(
|
||||
fn donut_row(
|
||||
language: Language,
|
||||
font: Font,
|
||||
sniffer: &Sniffer,
|
||||
) -> Container<'a, Message, StyleType> {
|
||||
) -> Container<'_, Message, StyleType> {
|
||||
let data_repr = sniffer.traffic_chart.data_repr;
|
||||
let filters = &sniffer.filters;
|
||||
let bpf = &sniffer.bpf_filter;
|
||||
|
||||
let (in_data, out_data, filtered_out, dropped) =
|
||||
sniffer.info_traffic.get_thumbnail_data(data_repr);
|
||||
|
||||
let legend_entry_filtered = if filters.none_active() {
|
||||
let legend_entry_filtered = if bpf.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(donut_legend_entry(
|
||||
filtered_out,
|
||||
data_repr,
|
||||
RuleType::FilteredOut,
|
||||
filters,
|
||||
bpf,
|
||||
font,
|
||||
language,
|
||||
))
|
||||
@@ -615,7 +613,7 @@ fn donut_row<'a>(
|
||||
in_data,
|
||||
data_repr,
|
||||
RuleType::Incoming,
|
||||
filters,
|
||||
bpf,
|
||||
font,
|
||||
language,
|
||||
))
|
||||
@@ -623,7 +621,7 @@ fn donut_row<'a>(
|
||||
out_data,
|
||||
data_repr,
|
||||
RuleType::Outgoing,
|
||||
filters,
|
||||
bpf,
|
||||
font,
|
||||
language,
|
||||
))
|
||||
@@ -632,7 +630,7 @@ fn donut_row<'a>(
|
||||
dropped,
|
||||
data_repr,
|
||||
RuleType::Dropped,
|
||||
filters,
|
||||
bpf,
|
||||
font,
|
||||
language,
|
||||
));
|
||||
@@ -658,14 +656,14 @@ fn donut_row<'a>(
|
||||
.align_y(Vertical::Center)
|
||||
}
|
||||
|
||||
fn donut_legend_entry<'a>(
|
||||
fn donut_legend_entry(
|
||||
value: u128,
|
||||
data_repr: DataRepr,
|
||||
rule_type: RuleType,
|
||||
filters: &Filters,
|
||||
bpf: &str,
|
||||
font: Font,
|
||||
language: Language,
|
||||
) -> Row<'a, Message, StyleType> {
|
||||
) -> Row<'_, Message, StyleType> {
|
||||
let value_text = data_repr.formatted_string(value);
|
||||
|
||||
let label = match rule_type {
|
||||
@@ -677,7 +675,7 @@ fn donut_legend_entry<'a>(
|
||||
};
|
||||
|
||||
let tooltip = if matches!(rule_type, RuleType::FilteredOut) {
|
||||
Some(get_active_filters_tooltip(filters, language, font))
|
||||
Some(get_active_filters_tooltip(bpf, language, font))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -794,40 +792,37 @@ fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, Sty
|
||||
.on_press(Message::AddOrRemoveFavorite(host, !is_favorite))
|
||||
}
|
||||
|
||||
fn get_active_filters_col<'a>(
|
||||
filters: &Filters,
|
||||
fn get_active_filters_col(
|
||||
bpf: &str,
|
||||
language: Language,
|
||||
font: Font,
|
||||
) -> Column<'a, Message, StyleType> {
|
||||
) -> Column<'_, Message, StyleType> {
|
||||
let mut ret_val = Column::new().push(
|
||||
Text::new(active_filters_translation(language))
|
||||
.font(font)
|
||||
.class(TextType::Subtitle),
|
||||
);
|
||||
|
||||
if filters.none_active() {
|
||||
if bpf.trim().is_empty() {
|
||||
ret_val = ret_val.push(Text::new(format!(" {}", none_translation(language))).font(font));
|
||||
} else {
|
||||
let filters_string = get_active_filters_string(filters, language);
|
||||
ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
|
||||
ret_val = ret_val.push(Row::new().push(Text::new(bpf).font(font)));
|
||||
}
|
||||
ret_val
|
||||
}
|
||||
|
||||
fn get_active_filters_tooltip<'a>(
|
||||
filters: &Filters,
|
||||
fn get_active_filters_tooltip(
|
||||
bpf: &str,
|
||||
language: Language,
|
||||
font: Font,
|
||||
) -> Tooltip<'a, Message, StyleType> {
|
||||
let filters_string = get_active_filters_string(filters, language);
|
||||
|
||||
) -> Tooltip<'_, Message, StyleType> {
|
||||
let mut ret_val = Column::new().push(
|
||||
Text::new(active_filters_translation(language))
|
||||
.font(font)
|
||||
.class(TextType::Subtitle),
|
||||
);
|
||||
|
||||
ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font)));
|
||||
ret_val = ret_val.push(Row::new().push(Text::new(bpf).font(font)));
|
||||
|
||||
Tooltip::new(
|
||||
Container::new(
|
||||
|
||||
@@ -47,13 +47,10 @@
|
||||
use crate::networking::parse_packets::parse_packets;
|
||||
use crate::networking::types::capture_context::{CaptureContext, CaptureSource, MyPcapImport};
|
||||
use crate::networking::types::data_representation::DataRepr;
|
||||
use crate::networking::types::filters::Filters;
|
||||
use crate::networking::types::host::{Host, HostMessage};
|
||||
use crate::networking::types::host_data_states::HostDataStates;
|
||||
use crate::networking::types::info_traffic::InfoTraffic;
|
||||
use crate::networking::types::ip_collection::AddressCollection;
|
||||
use crate::networking::types::my_device::MyDevice;
|
||||
use crate::networking::types::port_collection::PortCollection;
|
||||
use crate::notifications::notify_and_log::notify_and_log;
|
||||
use crate::notifications::types::logged_notification::LoggedNotification;
|
||||
use crate::notifications::types::notifications::{DataNotification, Notification};
|
||||
@@ -94,8 +91,8 @@ pub struct Sniffer {
|
||||
pub capture_source: CaptureSource,
|
||||
/// List of network devices
|
||||
pub my_devices: Vec<MyDevice>,
|
||||
/// Active filters on the observed traffic
|
||||
pub filters: Filters,
|
||||
/// BPF filter program to be applied to the capture
|
||||
pub bpf_filter: String,
|
||||
/// Signals if a pcap error occurred
|
||||
pub pcap_error: Option<String>,
|
||||
/// Messages status
|
||||
@@ -158,7 +155,7 @@ pub fn new(configs: Configs) -> Self {
|
||||
newer_release_available: None,
|
||||
capture_source: CaptureSource::Device(device),
|
||||
my_devices: Vec::new(),
|
||||
filters: Filters::default(),
|
||||
bpf_filter: String::new(),
|
||||
pcap_error: None,
|
||||
dots_pulse: (".".to_string(), 0),
|
||||
traffic_chart: TrafficChart::new(style, language),
|
||||
@@ -279,31 +276,8 @@ pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
}
|
||||
}
|
||||
Message::DeviceSelection(name) => self.set_device(&name),
|
||||
Message::IpVersionSelection(version, insert) => {
|
||||
if insert {
|
||||
self.filters.ip_versions.insert(version);
|
||||
} else {
|
||||
self.filters.ip_versions.remove(&version);
|
||||
}
|
||||
}
|
||||
Message::ProtocolSelection(protocol, insert) => {
|
||||
if insert {
|
||||
self.filters.protocols.insert(protocol);
|
||||
} else {
|
||||
self.filters.protocols.remove(&protocol);
|
||||
}
|
||||
}
|
||||
Message::AddressFilter(value) => {
|
||||
if let Some(collection) = AddressCollection::new(&value) {
|
||||
self.filters.address_collection = collection;
|
||||
}
|
||||
self.filters.address_str = value;
|
||||
}
|
||||
Message::PortFilter(value) => {
|
||||
if let Some(collection) = PortCollection::new(&value) {
|
||||
self.filters.port_collection = collection;
|
||||
}
|
||||
self.filters.port_str = value;
|
||||
Message::BpfFilter(value) => {
|
||||
self.bpf_filter = value;
|
||||
}
|
||||
Message::DataReprSelection(unit) => self.traffic_chart.change_kind(unit),
|
||||
Message::ReportSortSelection(sort) => {
|
||||
@@ -741,14 +715,14 @@ fn start(&mut self) -> Task<Message> {
|
||||
self.set_device(current_device_name);
|
||||
}
|
||||
let pcap_path = self.export_pcap.full_path();
|
||||
let capture_context = CaptureContext::new(&self.capture_source, pcap_path.as_ref());
|
||||
let capture_context =
|
||||
CaptureContext::new(&self.capture_source, pcap_path.as_ref(), &self.bpf_filter);
|
||||
self.pcap_error = capture_context.error().map(ToString::to_string);
|
||||
self.running_page = RunningPage::Overview;
|
||||
|
||||
if capture_context.error().is_none() {
|
||||
// no pcap error
|
||||
let curr_cap_id = self.current_capture_rx.0;
|
||||
let filters = self.filters.clone();
|
||||
let mmdb_readers = self.mmdb_readers.clone();
|
||||
self.capture_source
|
||||
.set_link_type(capture_context.my_link_type());
|
||||
@@ -762,7 +736,6 @@ fn start(&mut self) -> Task<Message> {
|
||||
parse_packets(
|
||||
curr_cap_id,
|
||||
capture_source,
|
||||
&filters,
|
||||
&mmdb_readers,
|
||||
capture_context,
|
||||
&tx,
|
||||
@@ -971,9 +944,7 @@ fn shortcut_return(&mut self) -> Task<Message> {
|
||||
&& self.settings_page.is_none()
|
||||
&& self.modal.is_none()
|
||||
{
|
||||
if self.filters.are_valid() {
|
||||
return Task::done(Message::Start);
|
||||
}
|
||||
return Task::done(Message::Start);
|
||||
} else if self.modal.eq(&Some(MyModal::Reset)) {
|
||||
return Task::done(Message::Reset);
|
||||
} else if self.modal.eq(&Some(MyModal::Quit)) {
|
||||
|
||||
@@ -13,7 +13,7 @@ pub enum TextInputType {
|
||||
#[default]
|
||||
Standard,
|
||||
Badge,
|
||||
Error,
|
||||
// Error,
|
||||
}
|
||||
|
||||
const TEXT_INPUT_BORDER_RADIUS: f32 = 5.0;
|
||||
@@ -25,7 +25,8 @@ fn active(&self, style: &StyleType) -> Style {
|
||||
Style {
|
||||
background: Background::Color(match self {
|
||||
TextInputType::Badge => Color::TRANSPARENT,
|
||||
_ => Color {
|
||||
// TextInputType::Error |
|
||||
TextInputType::Standard => Color {
|
||||
a: ext.alpha_round_borders,
|
||||
..ext.buttons_color
|
||||
},
|
||||
@@ -36,7 +37,7 @@ fn active(&self, style: &StyleType) -> Style {
|
||||
color: match self {
|
||||
TextInputType::Badge => Color::TRANSPARENT,
|
||||
TextInputType::Standard => ext.buttons_color,
|
||||
TextInputType::Error => ext.red_alert_color,
|
||||
// TextInputType::Error => ext.red_alert_color,
|
||||
},
|
||||
},
|
||||
icon: Color {
|
||||
@@ -51,17 +52,17 @@ fn active(&self, style: &StyleType) -> Style {
|
||||
|
||||
fn focused(&self, style: &StyleType) -> Style {
|
||||
let colors = style.get_palette();
|
||||
let ext = style.get_extension();
|
||||
// let ext = style.get_extension();
|
||||
let is_nightly = style.get_extension().is_nightly;
|
||||
Style {
|
||||
background: Background::Color(colors.primary),
|
||||
border: Border {
|
||||
radius: TEXT_INPUT_BORDER_RADIUS.into(),
|
||||
width: BORDER_WIDTH,
|
||||
color: match self {
|
||||
TextInputType::Error => ext.red_alert_color,
|
||||
_ => colors.secondary,
|
||||
},
|
||||
color: colors.secondary, // match self {
|
||||
// TextInputType::Error => ext.red_alert_color,
|
||||
// _ => colors.secondary,
|
||||
// },
|
||||
},
|
||||
icon: Color {
|
||||
a: if is_nightly { 0.2 } else { 0.7 },
|
||||
@@ -114,15 +115,16 @@ fn hovered(&self, style: &StyleType) -> Style {
|
||||
Style {
|
||||
background: Background::Color(match self {
|
||||
TextInputType::Badge => Color::TRANSPARENT,
|
||||
_ => ext.buttons_color,
|
||||
// TextInputType::Error |
|
||||
TextInputType::Standard => ext.buttons_color,
|
||||
}),
|
||||
border: Border {
|
||||
radius: TEXT_INPUT_BORDER_RADIUS.into(),
|
||||
width: BORDER_WIDTH,
|
||||
color: match self {
|
||||
TextInputType::Error => ext.red_alert_color,
|
||||
_ => colors.secondary,
|
||||
},
|
||||
color: colors.secondary, // match self {
|
||||
// TextInputType::Error => ext.red_alert_color,
|
||||
// _ => colors.secondary,
|
||||
// },
|
||||
},
|
||||
icon: Color {
|
||||
a: if ext.is_nightly { 0.2 } else { 0.7 },
|
||||
@@ -140,7 +142,8 @@ fn disabled(&self, style: &StyleType) -> Style {
|
||||
Style {
|
||||
background: Background::Color(match self {
|
||||
TextInputType::Badge => Color::TRANSPARENT,
|
||||
_ => Color {
|
||||
// TextInputType::Error |
|
||||
TextInputType::Standard => Color {
|
||||
a: ext.alpha_round_containers,
|
||||
..ext.buttons_color
|
||||
},
|
||||
@@ -154,10 +157,10 @@ fn disabled(&self, style: &StyleType) -> Style {
|
||||
a: ext.alpha_round_borders,
|
||||
..ext.buttons_color
|
||||
},
|
||||
TextInputType::Error => Color {
|
||||
a: ext.alpha_round_borders,
|
||||
..ext.red_alert_color
|
||||
},
|
||||
// TextInputType::Error => Color {
|
||||
// a: ext.alpha_round_borders,
|
||||
// ..ext.red_alert_color
|
||||
// },
|
||||
},
|
||||
},
|
||||
icon: Color {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
use crate::report::types::sort_type::SortType;
|
||||
use crate::utils::types::file_info::FileInfo;
|
||||
use crate::utils::types::web_page::WebPage;
|
||||
use crate::{IpVersion, Language, Protocol, ReportSortType, StyleType};
|
||||
use crate::{Language, ReportSortType, StyleType};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Messages types that permit reacting to application interactions/subscriptions
|
||||
@@ -24,14 +24,8 @@ pub enum Message {
|
||||
TickRun(usize, InfoTraffic, Vec<HostMessage>, bool),
|
||||
/// Select network device
|
||||
DeviceSelection(String),
|
||||
/// Select IP filter
|
||||
IpVersionSelection(IpVersion, bool),
|
||||
/// Select protocol filter
|
||||
ProtocolSelection(Protocol, bool),
|
||||
/// Changed address filter
|
||||
AddressFilter(String),
|
||||
/// Changed port filter
|
||||
PortFilter(String),
|
||||
/// Changed BPF filter
|
||||
BpfFilter(String),
|
||||
/// Select data representation to use
|
||||
DataReprSelection(DataRepr),
|
||||
/// Select report sort type to be displayed (inspect page)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
use crate::networking::types::capture_context::{CaptureContext, CaptureSource};
|
||||
use crate::networking::types::data_info::DataInfo;
|
||||
use crate::networking::types::data_info_host::DataInfoHost;
|
||||
use crate::networking::types::filters::Filters;
|
||||
use crate::networking::types::host::{Host, HostMessage};
|
||||
use crate::networking::types::icmp_type::IcmpType;
|
||||
use crate::networking::types::info_traffic::InfoTraffic;
|
||||
@@ -40,7 +39,6 @@
|
||||
pub fn parse_packets(
|
||||
cap_id: usize,
|
||||
mut cs: CaptureSource,
|
||||
filters: &Filters,
|
||||
mmdb_readers: &MmdbReaders,
|
||||
capture_context: CaptureContext,
|
||||
tx: &Sender<BackendTrafficMessage>,
|
||||
@@ -137,144 +135,138 @@ pub fn parse_packets(
|
||||
continue;
|
||||
};
|
||||
|
||||
let passed_filters = filters.matches(&packet_filters_fields);
|
||||
if passed_filters {
|
||||
// save this packet to PCAP file
|
||||
if let Some(file) = savefile.as_mut() {
|
||||
file.write(&packet);
|
||||
}
|
||||
// update the map
|
||||
let (traffic_direction, service) = modify_or_insert_in_map(
|
||||
&mut info_traffic_msg,
|
||||
&key,
|
||||
&cs,
|
||||
mac_addresses,
|
||||
icmp_type,
|
||||
arp_type,
|
||||
exchanged_bytes,
|
||||
);
|
||||
|
||||
info_traffic_msg
|
||||
.tot_data_info
|
||||
.add_packet(exchanged_bytes, traffic_direction);
|
||||
|
||||
// check the rDNS status of this address and act accordingly
|
||||
let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
|
||||
let mut r_dns_waiting_resolution = false;
|
||||
let mut resolutions_lock = resolutions_state.lock().unwrap();
|
||||
let r_dns_already_resolved = resolutions_lock
|
||||
.addresses_resolved
|
||||
.contains_key(&address_to_lookup);
|
||||
if !r_dns_already_resolved {
|
||||
r_dns_waiting_resolution = resolutions_lock
|
||||
.addresses_waiting_resolution
|
||||
.contains_key(&address_to_lookup);
|
||||
}
|
||||
|
||||
match (r_dns_waiting_resolution, r_dns_already_resolved) {
|
||||
(false, false) => {
|
||||
// rDNS not requested yet (first occurrence of this address to lookup)
|
||||
|
||||
// Add this address to the map of addresses waiting for a resolution
|
||||
// Useful to NOT perform again a rDNS lookup for this entry
|
||||
resolutions_lock.addresses_waiting_resolution.insert(
|
||||
address_to_lookup,
|
||||
DataInfo::new_with_first_packet(
|
||||
exchanged_bytes,
|
||||
traffic_direction,
|
||||
),
|
||||
);
|
||||
drop(resolutions_lock);
|
||||
|
||||
// launch new thread to resolve host name
|
||||
let key2 = key;
|
||||
let resolutions_state2 = resolutions_state.clone();
|
||||
let new_hosts_to_send2 = new_hosts_to_send.clone();
|
||||
let interface_addresses = cs.get_addresses().clone();
|
||||
let mmdb_readers_2 = mmdb_readers.clone();
|
||||
let tx2 = tx.clone();
|
||||
let _ = thread::Builder::new()
|
||||
.name("thread_reverse_dns_lookup".to_string())
|
||||
.spawn(move || {
|
||||
reverse_dns_lookup(
|
||||
&resolutions_state2,
|
||||
&new_hosts_to_send2,
|
||||
&key2,
|
||||
traffic_direction,
|
||||
&interface_addresses,
|
||||
&mmdb_readers_2,
|
||||
&tx2,
|
||||
);
|
||||
})
|
||||
.log_err(location!());
|
||||
}
|
||||
(true, false) => {
|
||||
// waiting for a previously requested rDNS resolution
|
||||
// update the corresponding waiting address data
|
||||
resolutions_lock
|
||||
.addresses_waiting_resolution
|
||||
.entry(address_to_lookup)
|
||||
.and_modify(|data_info| {
|
||||
data_info.add_packet(exchanged_bytes, traffic_direction);
|
||||
});
|
||||
drop(resolutions_lock);
|
||||
}
|
||||
(_, true) => {
|
||||
// rDNS already resolved
|
||||
// update the corresponding host's data info
|
||||
let host = resolutions_lock
|
||||
.addresses_resolved
|
||||
.get(&address_to_lookup)
|
||||
.unwrap_or(&Host::default())
|
||||
.clone();
|
||||
drop(resolutions_lock);
|
||||
info_traffic_msg
|
||||
.hosts
|
||||
.entry(host)
|
||||
.and_modify(|data_info_host| {
|
||||
data_info_host
|
||||
.data_info
|
||||
.add_packet(exchanged_bytes, traffic_direction);
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
let my_interface_addresses = cs.get_addresses();
|
||||
let traffic_type = get_traffic_type(
|
||||
&address_to_lookup,
|
||||
my_interface_addresses,
|
||||
traffic_direction,
|
||||
);
|
||||
let is_loopback = address_to_lookup.is_loopback();
|
||||
let is_local = is_local_connection(
|
||||
&address_to_lookup,
|
||||
my_interface_addresses,
|
||||
);
|
||||
let is_bogon = is_bogon(&address_to_lookup);
|
||||
DataInfoHost {
|
||||
data_info: DataInfo::new_with_first_packet(
|
||||
exchanged_bytes,
|
||||
traffic_direction,
|
||||
),
|
||||
is_favorite: false,
|
||||
is_loopback,
|
||||
is_local,
|
||||
is_bogon,
|
||||
traffic_type,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//increment the packet count for the sniffed service
|
||||
info_traffic_msg
|
||||
.services
|
||||
.entry(service)
|
||||
.and_modify(|data_info| {
|
||||
data_info.add_packet(exchanged_bytes, traffic_direction);
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
|
||||
});
|
||||
// save this packet to PCAP file
|
||||
if let Some(file) = savefile.as_mut() {
|
||||
file.write(&packet);
|
||||
}
|
||||
// update the map
|
||||
let (traffic_direction, service) = modify_or_insert_in_map(
|
||||
&mut info_traffic_msg,
|
||||
&key,
|
||||
&cs,
|
||||
mac_addresses,
|
||||
icmp_type,
|
||||
arp_type,
|
||||
exchanged_bytes,
|
||||
);
|
||||
|
||||
info_traffic_msg
|
||||
.tot_data_info
|
||||
.add_packet(exchanged_bytes, traffic_direction);
|
||||
|
||||
// check the rDNS status of this address and act accordingly
|
||||
let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
|
||||
let mut r_dns_waiting_resolution = false;
|
||||
let mut resolutions_lock = resolutions_state.lock().unwrap();
|
||||
let r_dns_already_resolved = resolutions_lock
|
||||
.addresses_resolved
|
||||
.contains_key(&address_to_lookup);
|
||||
if !r_dns_already_resolved {
|
||||
r_dns_waiting_resolution = resolutions_lock
|
||||
.addresses_waiting_resolution
|
||||
.contains_key(&address_to_lookup);
|
||||
}
|
||||
|
||||
match (r_dns_waiting_resolution, r_dns_already_resolved) {
|
||||
(false, false) => {
|
||||
// rDNS not requested yet (first occurrence of this address to lookup)
|
||||
|
||||
// Add this address to the map of addresses waiting for a resolution
|
||||
// Useful to NOT perform again a rDNS lookup for this entry
|
||||
resolutions_lock.addresses_waiting_resolution.insert(
|
||||
address_to_lookup,
|
||||
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction),
|
||||
);
|
||||
drop(resolutions_lock);
|
||||
|
||||
// launch new thread to resolve host name
|
||||
let key2 = key;
|
||||
let resolutions_state2 = resolutions_state.clone();
|
||||
let new_hosts_to_send2 = new_hosts_to_send.clone();
|
||||
let interface_addresses = cs.get_addresses().clone();
|
||||
let mmdb_readers_2 = mmdb_readers.clone();
|
||||
let tx2 = tx.clone();
|
||||
let _ = thread::Builder::new()
|
||||
.name("thread_reverse_dns_lookup".to_string())
|
||||
.spawn(move || {
|
||||
reverse_dns_lookup(
|
||||
&resolutions_state2,
|
||||
&new_hosts_to_send2,
|
||||
&key2,
|
||||
traffic_direction,
|
||||
&interface_addresses,
|
||||
&mmdb_readers_2,
|
||||
&tx2,
|
||||
);
|
||||
})
|
||||
.log_err(location!());
|
||||
}
|
||||
(true, false) => {
|
||||
// waiting for a previously requested rDNS resolution
|
||||
// update the corresponding waiting address data
|
||||
resolutions_lock
|
||||
.addresses_waiting_resolution
|
||||
.entry(address_to_lookup)
|
||||
.and_modify(|data_info| {
|
||||
data_info.add_packet(exchanged_bytes, traffic_direction);
|
||||
});
|
||||
drop(resolutions_lock);
|
||||
}
|
||||
(_, true) => {
|
||||
// rDNS already resolved
|
||||
// update the corresponding host's data info
|
||||
let host = resolutions_lock
|
||||
.addresses_resolved
|
||||
.get(&address_to_lookup)
|
||||
.unwrap_or(&Host::default())
|
||||
.clone();
|
||||
drop(resolutions_lock);
|
||||
info_traffic_msg
|
||||
.hosts
|
||||
.entry(host)
|
||||
.and_modify(|data_info_host| {
|
||||
data_info_host
|
||||
.data_info
|
||||
.add_packet(exchanged_bytes, traffic_direction);
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
let my_interface_addresses = cs.get_addresses();
|
||||
let traffic_type = get_traffic_type(
|
||||
&address_to_lookup,
|
||||
my_interface_addresses,
|
||||
traffic_direction,
|
||||
);
|
||||
let is_loopback = address_to_lookup.is_loopback();
|
||||
let is_local = is_local_connection(
|
||||
&address_to_lookup,
|
||||
my_interface_addresses,
|
||||
);
|
||||
let is_bogon = is_bogon(&address_to_lookup);
|
||||
DataInfoHost {
|
||||
data_info: DataInfo::new_with_first_packet(
|
||||
exchanged_bytes,
|
||||
traffic_direction,
|
||||
),
|
||||
is_favorite: false,
|
||||
is_loopback,
|
||||
is_local,
|
||||
is_bogon,
|
||||
traffic_type,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//increment the packet count for the sniffed service
|
||||
info_traffic_msg
|
||||
.services
|
||||
.entry(service)
|
||||
.and_modify(|data_info| {
|
||||
data_info.add_packet(exchanged_bytes, traffic_direction);
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction)
|
||||
});
|
||||
|
||||
//increment number of sniffed packets and bytes
|
||||
info_traffic_msg.all_packets += 1;
|
||||
|
||||
@@ -14,11 +14,16 @@ pub enum CaptureContext {
|
||||
}
|
||||
|
||||
impl CaptureContext {
|
||||
pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>) -> Self {
|
||||
let cap_type = match CaptureType::from_source(source, pcap_out_path) {
|
||||
pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, bpf: &str) -> Self {
|
||||
let mut cap_type = match CaptureType::from_source(source, pcap_out_path) {
|
||||
Ok(c) => c,
|
||||
Err(e) => return Self::Error(e.to_string()),
|
||||
};
|
||||
|
||||
if let Err(e) = cap_type.set_bpf(bpf) {
|
||||
return Self::Error(e.to_string());
|
||||
}
|
||||
|
||||
let cap = match cap_type {
|
||||
CaptureType::Live(cap) => cap,
|
||||
CaptureType::Offline(cap) => return Self::new_offline(cap),
|
||||
@@ -131,6 +136,13 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result
|
||||
CaptureSource::File(file) => Ok(Self::Offline(Capture::from_file(&file.path)?)),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_bpf(&mut self, bpf: &str) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Live(cap) => cap.filter(bpf, true),
|
||||
Self::Offline(cap) => cap.filter(bpf, true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
//! Module defining the `Filters` struct, which represents the possible filters applicable on network traffic.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::networking::types::ip_collection::AddressCollection;
|
||||
use crate::networking::types::packet_filters_fields::PacketFiltersFields;
|
||||
use crate::networking::types::port_collection::PortCollection;
|
||||
use crate::{IpVersion, Protocol};
|
||||
|
||||
/// Possible filters applicable to network traffic
|
||||
#[derive(Clone)]
|
||||
pub struct Filters {
|
||||
/// Internet Protocol versions
|
||||
pub ip_versions: HashSet<IpVersion>,
|
||||
/// Protocols
|
||||
pub protocols: HashSet<Protocol>,
|
||||
/// IP addresses string in Initial page text input
|
||||
pub address_str: String,
|
||||
/// IP address collection to match against traffic
|
||||
pub address_collection: AddressCollection,
|
||||
/// Ports string in Initial page text input
|
||||
pub port_str: String,
|
||||
/// Port collection to match against traffic
|
||||
pub port_collection: PortCollection,
|
||||
}
|
||||
|
||||
impl Default for Filters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ip_versions: HashSet::from(IpVersion::ALL),
|
||||
protocols: HashSet::from(Protocol::ALL),
|
||||
address_str: String::new(),
|
||||
address_collection: AddressCollection::default(),
|
||||
port_str: String::new(),
|
||||
port_collection: PortCollection::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filters {
|
||||
/// Checks whether the filters match the current packet's protocols
|
||||
pub fn matches(&self, packet_filters_fields: &PacketFiltersFields) -> bool {
|
||||
self.ip_versions.contains(&packet_filters_fields.ip_version)
|
||||
&& self.protocols.contains(&packet_filters_fields.protocol)
|
||||
&& (self
|
||||
.address_collection
|
||||
.contains(&packet_filters_fields.source)
|
||||
|| self
|
||||
.address_collection
|
||||
.contains(&packet_filters_fields.dest))
|
||||
&& (self.port_collection.contains(packet_filters_fields.sport)
|
||||
|| self.port_collection.contains(packet_filters_fields.dport))
|
||||
}
|
||||
|
||||
pub fn are_valid(&self) -> bool {
|
||||
self.ip_version_valid()
|
||||
&& self.protocol_valid()
|
||||
&& self.address_valid()
|
||||
&& self.port_valid()
|
||||
}
|
||||
|
||||
pub fn ip_version_valid(&self) -> bool {
|
||||
!self.ip_versions.is_empty()
|
||||
}
|
||||
|
||||
pub fn protocol_valid(&self) -> bool {
|
||||
!self.protocols.is_empty()
|
||||
}
|
||||
|
||||
pub fn address_valid(&self) -> bool {
|
||||
AddressCollection::new(&self.address_str).is_some()
|
||||
}
|
||||
|
||||
pub fn port_valid(&self) -> bool {
|
||||
PortCollection::new(&self.port_str).is_some()
|
||||
}
|
||||
|
||||
pub fn none_active(&self) -> bool {
|
||||
!self.ip_version_active()
|
||||
&& !self.protocol_active()
|
||||
&& !self.address_active()
|
||||
&& !self.port_active()
|
||||
}
|
||||
|
||||
pub fn ip_version_active(&self) -> bool {
|
||||
self.ip_versions.len() != IpVersion::ALL.len()
|
||||
}
|
||||
|
||||
pub fn protocol_active(&self) -> bool {
|
||||
self.protocols.len() != Protocol::ALL.len()
|
||||
}
|
||||
|
||||
pub fn address_active(&self) -> bool {
|
||||
self.address_collection != AddressCollection::default()
|
||||
}
|
||||
|
||||
pub fn port_active(&self) -> bool {
|
||||
self.port_collection != PortCollection::default()
|
||||
}
|
||||
|
||||
pub fn pretty_print_ip(&self) -> String {
|
||||
format!("{:?}", self.ip_versions)
|
||||
.replace('{', "")
|
||||
.replace('}', "")
|
||||
}
|
||||
|
||||
pub fn pretty_print_protocol(&self) -> String {
|
||||
format!("{:?}", self.protocols)
|
||||
.replace('{', "")
|
||||
.replace('}', "")
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,6 @@ impl AddressCollection {
|
||||
const SEPARATOR: char = ',';
|
||||
const RANGE_SEPARATOR: char = '-';
|
||||
|
||||
pub const PLACEHOLDER_STR: &'static str =
|
||||
"0.0.0.0-255.255.255.255, ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff";
|
||||
|
||||
pub(crate) fn new(str: &str) -> Option<Self> {
|
||||
let str = str.replace(' ', "");
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
impl IpVersion {
|
||||
pub(crate) const ALL: [IpVersion; 2] = [IpVersion::IPv4, IpVersion::IPv6];
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
pub mod data_info;
|
||||
pub mod data_info_host;
|
||||
pub mod data_representation;
|
||||
pub mod filters;
|
||||
pub mod host;
|
||||
pub mod host_data_states;
|
||||
pub mod icmp_type;
|
||||
@@ -17,7 +16,6 @@
|
||||
pub mod my_device;
|
||||
pub mod my_link_type;
|
||||
pub mod packet_filters_fields;
|
||||
pub mod port_collection;
|
||||
pub mod protocol;
|
||||
pub mod service;
|
||||
pub mod service_query;
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
pub(crate) struct PortCollection {
|
||||
pub(crate) ports: Vec<u16>,
|
||||
pub(crate) ranges: Vec<RangeInclusive<u16>>,
|
||||
}
|
||||
|
||||
impl PortCollection {
|
||||
const SEPARATOR: char = ',';
|
||||
const RANGE_SEPARATOR: char = '-';
|
||||
|
||||
pub const PLACEHOLDER_STR: &'static str = "0-65535";
|
||||
|
||||
pub(crate) fn new(str: &str) -> Option<Self> {
|
||||
let str = str.replace(' ', "");
|
||||
|
||||
if str.is_empty() {
|
||||
return Some(Self::default());
|
||||
}
|
||||
|
||||
let mut ports = 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) {
|
||||
// port 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_port = u16::from_str(lower_str).ok()?;
|
||||
let upper_port = u16::from_str(upper_str).ok()?;
|
||||
let range = RangeInclusive::new(lower_port, upper_port);
|
||||
if range.is_empty() {
|
||||
return None;
|
||||
}
|
||||
ranges.push(range);
|
||||
} else {
|
||||
// individual port
|
||||
let port = u16::from_str(object).ok()?;
|
||||
ports.push(port);
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self { ports, ranges })
|
||||
}
|
||||
|
||||
pub(crate) fn contains(&self, port: Option<u16>) -> bool {
|
||||
// ignore port filter in case of ICMP or ARP
|
||||
let Some(p) = port else {
|
||||
return true;
|
||||
};
|
||||
|
||||
for range in &self.ranges {
|
||||
if range.contains(&p) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
self.ports.contains(&p)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PortCollection {
|
||||
fn default() -> Self {
|
||||
PortCollection {
|
||||
ports: vec![],
|
||||
ranges: vec![RangeInclusive::new(u16::MIN, u16::MAX)],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::networking::types::port_collection::PortCollection;
|
||||
|
||||
#[test]
|
||||
fn test_default_collection_contains_everything() {
|
||||
let collection = PortCollection::default();
|
||||
assert!(collection.contains(Some(0)));
|
||||
assert!(collection.contains(Some(1)));
|
||||
assert!(collection.contains(Some(2)));
|
||||
assert!(collection.contains(Some(80)));
|
||||
assert!(collection.contains(Some(8080)));
|
||||
assert!(collection.contains(Some(55333)));
|
||||
assert!(collection.contains(Some(65535)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_port_collections() {
|
||||
assert_eq!(
|
||||
PortCollection::new("0").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![0],
|
||||
ranges: vec![]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PortCollection::new(" 0 ").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![0],
|
||||
ranges: vec![]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PortCollection::new("1,2,3,4,999").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![1, 2, 3, 4, 999],
|
||||
ranges: vec![]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PortCollection::new("1, 2, 3, 4, 900-999").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![1, 2, 3, 4],
|
||||
ranges: vec![900..=999]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PortCollection::new("1 - 999").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![],
|
||||
ranges: vec![1..=999]
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PortCollection::new(" 1,2,10-20,3,4, 999-1200 ").unwrap(),
|
||||
PortCollection {
|
||||
ports: vec![1, 2, 3, 4],
|
||||
ranges: vec![10..=20, 999..=1200]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_port_collections_invalid() {
|
||||
assert_eq!(PortCollection::new("1,2,10-20,3,4,-1200"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("1,2,10-20,3,4,999:1200"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("1,2,10-20,3,4,999-1200,"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("999-1"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("1:999"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("1-2-3"), None);
|
||||
|
||||
assert_eq!(PortCollection::new("1-2-"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_port_collection_contains() {
|
||||
let collection = PortCollection::new("1,2,25-30,55,101-117").unwrap();
|
||||
assert!(collection.contains(Some(1)));
|
||||
assert!(collection.contains(Some(2)));
|
||||
assert!(collection.contains(Some(25)));
|
||||
assert!(collection.contains(Some(27)));
|
||||
assert!(collection.contains(Some(30)));
|
||||
assert!(collection.contains(Some(55)));
|
||||
assert!(collection.contains(Some(101)));
|
||||
assert!(collection.contains(Some(109)));
|
||||
assert!(collection.contains(Some(117)));
|
||||
assert!(!collection.contains(Some(4)));
|
||||
assert!(!collection.contains(Some(24)));
|
||||
assert!(!collection.contains(Some(31)));
|
||||
assert!(!collection.contains(Some(100)));
|
||||
assert!(!collection.contains(Some(118)));
|
||||
assert!(!collection.contains(Some(8080)));
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,6 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
pub const ALL: [Protocol; 4] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP];
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -163,33 +163,33 @@ pub fn addresses_translation(language: Language) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ip_version_translation(language: Language) -> &'static str {
|
||||
match language {
|
||||
Language::EN => "IP version",
|
||||
Language::IT => "Versione IP",
|
||||
Language::FR => "Version IP",
|
||||
Language::ES => "Versión IP",
|
||||
Language::PL => "Wersja IP",
|
||||
Language::DE => "IP Version",
|
||||
Language::UK => "Версія IP",
|
||||
Language::ZH => "目标IP协议版本",
|
||||
Language::ZH_TW => "IP 版本",
|
||||
Language::RO => "Versiune IP",
|
||||
Language::KO => "IP 버전",
|
||||
Language::TR => "IP versiyonu",
|
||||
Language::RU => "Версия IP",
|
||||
Language::PT => "Versão de IP",
|
||||
Language::EL => "Έκδοση IP",
|
||||
// Language::FA => "نسخهٔ IP",
|
||||
Language::SV => "IP-version",
|
||||
Language::FI => "IP-versio",
|
||||
Language::JA => "IP バージョン",
|
||||
Language::UZ => "IP versiyasi",
|
||||
Language::VI => "Phiên bản IP",
|
||||
Language::ID => "Versi IP",
|
||||
Language::NL => "IP versie",
|
||||
}
|
||||
}
|
||||
// pub fn ip_version_translation(language: Language) -> &'static str {
|
||||
// match language {
|
||||
// Language::EN => "IP version",
|
||||
// Language::IT => "Versione IP",
|
||||
// Language::FR => "Version IP",
|
||||
// Language::ES => "Versión IP",
|
||||
// Language::PL => "Wersja IP",
|
||||
// Language::DE => "IP Version",
|
||||
// Language::UK => "Версія IP",
|
||||
// Language::ZH => "目标IP协议版本",
|
||||
// Language::ZH_TW => "IP 版本",
|
||||
// Language::RO => "Versiune IP",
|
||||
// Language::KO => "IP 버전",
|
||||
// Language::TR => "IP versiyonu",
|
||||
// Language::RU => "Версия IP",
|
||||
// Language::PT => "Versão de IP",
|
||||
// Language::EL => "Έκδοση IP",
|
||||
// // Language::FA => "نسخهٔ IP",
|
||||
// Language::SV => "IP-version",
|
||||
// Language::FI => "IP-versio",
|
||||
// Language::JA => "IP バージョン",
|
||||
// Language::UZ => "IP versiyasi",
|
||||
// Language::VI => "Phiên bản IP",
|
||||
// Language::ID => "Versi IP",
|
||||
// Language::NL => "IP versie",
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn transport_protocol_translation(language: Language) -> &'static str {
|
||||
// match language {
|
||||
|
||||
@@ -190,31 +190,31 @@ pub fn port_translation(language: Language) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invalid_filters_translation(language: Language) -> &'static str {
|
||||
match language {
|
||||
Language::EN => "Invalid filters",
|
||||
// Language::FA => "صافی نامعتبر",
|
||||
Language::ES | Language::PT => "Filtros inválidos",
|
||||
Language::IT => "Filtri non validi",
|
||||
Language::FR => "Filtres invalides",
|
||||
Language::DE => "Ungültige Filter",
|
||||
Language::PL => "Nieprawidłowe filtry",
|
||||
Language::RU => "Неверный формат фильтров",
|
||||
Language::RO => "Filtre invalide",
|
||||
Language::JA => "無効なフィルター",
|
||||
Language::UZ => "Noto'g'ri filtrlar",
|
||||
Language::SV => "Ogiltiga filter",
|
||||
Language::VI => "Bộ lọc không khả dụng",
|
||||
Language::ZH => "无效的过滤器",
|
||||
Language::ZH_TW => "無效的篩選器",
|
||||
Language::KO => "잘못된 필터",
|
||||
Language::TR => "Geçersiz filtreler",
|
||||
Language::UK => "Неправильний формат фільтрів",
|
||||
Language::ID => "Filter salah",
|
||||
Language::NL => "Ongeldige filters",
|
||||
_ => "Invalid filters",
|
||||
}
|
||||
}
|
||||
// pub fn invalid_filters_translation(language: Language) -> &'static str {
|
||||
// match language {
|
||||
// Language::EN => "Invalid filters",
|
||||
// // Language::FA => "صافی نامعتبر",
|
||||
// Language::ES | Language::PT => "Filtros inválidos",
|
||||
// Language::IT => "Filtri non validi",
|
||||
// Language::FR => "Filtres invalides",
|
||||
// Language::DE => "Ungültige Filter",
|
||||
// Language::PL => "Nieprawidłowe filtry",
|
||||
// Language::RU => "Неверный формат фильтров",
|
||||
// Language::RO => "Filtre invalide",
|
||||
// Language::JA => "無効なフィルター",
|
||||
// Language::UZ => "Noto'g'ri filtrlar",
|
||||
// Language::SV => "Ogiltiga filter",
|
||||
// Language::VI => "Bộ lọc không khả dụng",
|
||||
// Language::ZH => "无效的过滤器",
|
||||
// Language::ZH_TW => "無效的篩選器",
|
||||
// Language::KO => "잘못된 필터",
|
||||
// Language::TR => "Geçersiz filtreler",
|
||||
// Language::UK => "Неправильний формат фільтрів",
|
||||
// Language::ID => "Filter salah",
|
||||
// Language::NL => "Ongeldige filters",
|
||||
// _ => "Invalid filters",
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn messages_translation(language: Language) -> &'static str {
|
||||
match language {
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
use std::cmp::min;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::Language;
|
||||
use crate::networking::types::filters::Filters;
|
||||
use crate::translations::translations::{
|
||||
address_translation, ip_version_translation, protocol_translation,
|
||||
};
|
||||
use crate::translations::translations_3::{invalid_filters_translation, port_translation};
|
||||
use crate::utils::types::timestamp::Timestamp;
|
||||
use chrono::{Local, TimeZone};
|
||||
use std::fmt::Write;
|
||||
|
||||
/// Application version number (to be displayed in gui footer)
|
||||
pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -27,61 +20,6 @@
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn get_invalid_filters_string(filters: &Filters, language: Language) -> String {
|
||||
let mut ret_val = format!("{}:", invalid_filters_translation(language));
|
||||
if !filters.ip_version_valid() {
|
||||
let _ = write!(ret_val, "\n • {}", ip_version_translation(language));
|
||||
}
|
||||
if !filters.protocol_valid() {
|
||||
let _ = write!(ret_val, "\n • {}", protocol_translation(language));
|
||||
}
|
||||
if !filters.address_valid() {
|
||||
let _ = write!(ret_val, "\n • {}", address_translation(language));
|
||||
}
|
||||
if !filters.port_valid() {
|
||||
let _ = write!(ret_val, "\n • {}", port_translation(language));
|
||||
}
|
||||
ret_val
|
||||
}
|
||||
|
||||
/// Computes the string representing the active filters
|
||||
pub fn get_active_filters_string(filters: &Filters, language: Language) -> String {
|
||||
let mut filters_string = String::new();
|
||||
if filters.ip_version_active() {
|
||||
let _ = writeln!(
|
||||
filters_string,
|
||||
"• {}: {}",
|
||||
ip_version_translation(language),
|
||||
filters.pretty_print_ip()
|
||||
);
|
||||
}
|
||||
if filters.protocol_active() {
|
||||
let _ = writeln!(
|
||||
filters_string,
|
||||
"• {}: {}",
|
||||
protocol_translation(language),
|
||||
filters.pretty_print_protocol()
|
||||
);
|
||||
}
|
||||
if filters.address_active() {
|
||||
let _ = writeln!(
|
||||
filters_string,
|
||||
"• {}: {}",
|
||||
address_translation(language),
|
||||
filters.address_str
|
||||
);
|
||||
}
|
||||
if filters.port_active() {
|
||||
let _ = writeln!(
|
||||
filters_string,
|
||||
"• {}: {}",
|
||||
port_translation(language),
|
||||
filters.port_str
|
||||
);
|
||||
}
|
||||
filters_string
|
||||
}
|
||||
|
||||
pub fn print_cli_welcome_message() {
|
||||
let ver = APP_VERSION;
|
||||
print!(
|
||||
|
||||
Reference in New Issue
Block a user