feat: created Service enum

This commit is contained in:
Giuliano Bellini s294739
2024-02-06 10:55:12 +01:00
parent 72a1f5582e
commit 8d56c447b7
8 changed files with 68 additions and 219 deletions

View File

@@ -23,16 +23,16 @@ fn set_icon() {
fn build_services_phf() {
let path = Path::new(&env::var("OUT_DIR").unwrap()).join("services.rs");
let mut file = BufWriter::new(File::create(&path).unwrap());
let mut file = BufWriter::new(File::create(path).unwrap());
let mut services_map = phf_codegen::Map::new();
let input = BufReader::new(File::open("./services.txt").unwrap());
for line in input.lines().flatten() {
let mut parts = line.split("\t");
let mut parts = line.split('\t');
let service = format!("\"{}\"", parts.next().unwrap());
let key = parts.next().unwrap().to_uppercase();
services_map.entry(key, &*service);
services_map.entry(key, &service);
}
write!(
@@ -41,5 +41,5 @@ fn build_services_phf() {
services_map.build()
)
.unwrap();
write!(&mut file, ";\n").unwrap();
writeln!(&mut file, ";").unwrap();
}

View File

@@ -48,7 +48,7 @@
get_active_filters_string, get_formatted_bytes_string_with_b, get_percentage_string,
};
use crate::utils::types::icon::Icon;
use crate::{AppProtocol, ChartType, ConfigSettings, Language, RunningPage, StyleType};
use crate::{ChartType, ConfigSettings, Language, RunningPage, Service, StyleType};
/// Computes the body of gui overview page
pub fn overview_page(sniffer: &Sniffer) -> Container<Message, Renderer<StyleType>> {
@@ -398,7 +398,7 @@ fn col_app(width: f32, sniffer: &Sniffer) -> Column<'static, Message, Renderer<S
);
// check if Unknown is longer than the first entry
if app == &"?" && incoming_bar_len + outgoing_bar_len > width * 0.88 {
if app == &Service::Unknown && incoming_bar_len + outgoing_bar_len > width * 0.88 {
let incoming_proportion = incoming_bar_len / (incoming_bar_len + outgoing_bar_len);
incoming_bar_len = width * 0.88 * incoming_proportion;
outgoing_bar_len = width * 0.88 * (1.0 - incoming_proportion);

View File

@@ -19,7 +19,7 @@
use gui::styles::types::style_type::StyleType;
use gui::types::runtime_data::RunTimeData;
use gui::types::sniffer::Sniffer;
use networking::types::app_protocol::AppProtocol;
use networking::types::app_protocol::Service;
use networking::types::byte_multiple::ByteMultiple;
use networking::types::info_traffic::InfoTraffic;
use networking::types::ip_version::IpVersion;

View File

@@ -12,6 +12,7 @@
use crate::mmdb::country::get_country;
use crate::mmdb::types::mmdb_reader::MmdbReader;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::app_protocol::Service;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::host::Host;
use crate::networking::types::icmp_type::{IcmpType, IcmpTypeV4, IcmpTypeV6};
@@ -153,50 +154,56 @@ fn analyze_transport_header(
}
}
pub fn get_app_protocol(key: &AddressPortPair, traffic_direction: TrafficDirection) -> String {
pub fn get_app_protocol(key: &AddressPortPair, traffic_direction: TrafficDirection) -> Service {
if key.port1.is_none() || key.port2.is_none() {
return "-".to_string();
return Service::NotApplicable;
}
let get_query_key = |port: u16, protocol: Protocol| format!("{}/{}", port, protocol);
let get_query_key = |port: u16, protocol: Protocol| format!("{port}/{protocol}");
// to return the service associated with the highest score:
// score = service_is_some * (port_is_well_known + bonus_direction)
// service_is_some: 1 if some, 0 if none
// service_is_some: 1 if some, 0 if unknown
// port_is_well_known: 3 if well known, 1 if not
// bonus_direction: +1 assigned to remote port
let compute_service_score = |service: &&str, port: u16, bonus_direction: bool| {
let service_is_some = if service.eq(&"?") { 0 } else { 1 };
let compute_service_score = |service: &Service, port: u16, bonus_direction: bool| {
let service_is_some = u8::from(service != &Service::Unknown);
let port_is_well_known = if port < 1024 { 3 } else { 1 };
let bonus_direction = if bonus_direction { 1 } else { 0 };
return service_is_some * (port_is_well_known + bonus_direction);
let bonus_direction = u8::from(bonus_direction);
service_is_some * (port_is_well_known + bonus_direction)
};
let port1 = key.port1.unwrap();
let port2 = key.port2.unwrap();
let service1 = SERVICES
.get(&get_query_key(port1, key.protocol))
.unwrap_or(&"?");
let service2 = SERVICES
.get(&get_query_key(port2, key.protocol))
.unwrap_or(&"?");
let service1 = Service::from_str(
SERVICES
.get(&get_query_key(port1, key.protocol))
.unwrap_or(&"?"),
)
.unwrap_or_default();
let service2 = Service::from_str(
SERVICES
.get(&get_query_key(port2, key.protocol))
.unwrap_or(&"?"),
)
.unwrap_or_default();
let score1 = compute_service_score(
service1,
&service1,
port1,
traffic_direction.ne(&TrafficDirection::Outgoing),
);
let score2 = compute_service_score(
service2,
&service2,
port2,
traffic_direction.eq(&TrafficDirection::Outgoing),
);
if score1 > score2 {
service1.to_string()
service1
} else {
service2.to_string()
service2
}
}
@@ -211,7 +218,7 @@ pub fn modify_or_insert_in_map(
) -> InfoAddressPortPair {
let now = Local::now();
let mut traffic_direction = TrafficDirection::default();
let mut application_protocol = "?".to_string();
let mut application_protocol = Service::Unknown;
if !info_traffic_mutex.lock().unwrap().map.contains_key(key) {
// first occurrence of key

View File

@@ -1,58 +1,11 @@
use std::fmt;
use std::str::FromStr;
/// Enum representing the possible observed values of application layer protocol.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[allow(clippy::upper_case_acronyms)]
pub enum AppProtocol {
/// File Transfer Protocol
FTP,
/// Secure Shell
SSH,
/// Telnet
Telnet,
/// Simple Mail Transfer Protocol
SMTP,
/// Terminal Access Controller Access-Control System
TACACS,
/// Domain Name System
DNS,
/// Dynamic Host Configuration Protocol
DHCP,
/// Trivial File Transfer Protocol
TFTP,
/// Hypertext Transfer Protocol
HTTP,
/// Post Office Protocol
POP,
/// Network Time Protocol
NTP,
/// NetBIOS
NetBIOS,
/// Post Office Protocol 3 over TLS/SSL
POP3S,
/// Internet Message Access Protocol
IMAP,
/// Simple Network Management Protocol
SNMP,
/// Border Gateway Protocol
BGP,
/// Lightweight Directory Access Protocol
LDAP,
///Hypertext Transfer Protocol over TLS/SSL
HTTPS,
/// Lightweight Directory Access Protocol over TLS/SSL
LDAPS,
/// File Transfer Protocol over TLS/SSL
FTPS,
/// Multicast DNS
#[allow(non_camel_case_types)]
mDNS,
///Internet Message Access Protocol over TLS/SSL
IMAPS,
/// Simple Service Discovery Protocol
SSDP,
/// Extensible Messaging and Presence Protocol |
XMPP,
/// Upper layer services.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum Service {
/// One of the known services.
Name(String),
/// Not identified
#[default]
Unknown,
@@ -60,149 +13,41 @@ pub enum AppProtocol {
NotApplicable,
}
/// Given an integer in the range `0..=65535`, this function returns an `Option<AppProtocol>` containing
/// the respective application protocol represented by a value of the `AppProtocol` enum.
/// Only the most common application layer protocols are considered; if a unknown port number
/// is provided, this function returns `None`.
///
/// # Arguments
///
/// * `port` - An integer representing the transport layer port to be mapped to
/// an application layer protocol.
///
/// # Examples
///
/// ```
/// let x = from_port_to_application_protocol(25);
/// //Simple Mail Transfer Protocol
/// assert_eq!(x, Option::Some(AppProtocol::SMTP));
///
/// let y = from_port_to_application_protocol(1999);
/// //Unknown port-to-protocol mapping
/// assert_eq!(y, Option::None);
/// ```
pub fn from_port_to_application_protocol(port: Option<u16>) -> AppProtocol {
if let Some(res) = port {
match res {
20..=21 => AppProtocol::FTP,
22 => AppProtocol::SSH,
23 => AppProtocol::Telnet,
25 => AppProtocol::SMTP,
49 => AppProtocol::TACACS,
53 => AppProtocol::DNS,
67..=68 => AppProtocol::DHCP,
69 => AppProtocol::TFTP,
80 | 8080 => AppProtocol::HTTP,
109..=110 => AppProtocol::POP,
123 => AppProtocol::NTP,
137..=139 => AppProtocol::NetBIOS,
143 | 220 => AppProtocol::IMAP,
161..=162 | 199 => AppProtocol::SNMP,
179 => AppProtocol::BGP,
389 => AppProtocol::LDAP,
443 => AppProtocol::HTTPS,
636 => AppProtocol::LDAPS,
989..=990 => AppProtocol::FTPS,
993 => AppProtocol::IMAPS,
995 => AppProtocol::POP3S,
1900 => AppProtocol::SSDP,
5222 => AppProtocol::XMPP,
5353 => AppProtocol::mDNS,
_ => AppProtocol::Unknown,
}
} else {
// ICMP
AppProtocol::NotApplicable
}
}
impl fmt::Display for AppProtocol {
impl fmt::Display for Service {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.eq(&AppProtocol::Unknown) {
write!(f, "?")
} else if self.eq(&AppProtocol::NotApplicable) {
write!(f, "-")
} else {
write!(f, "{self:?}")
match self {
Service::Name(name) => write!(f, "{name}"),
Service::Unknown => write!(f, "?"),
Service::NotApplicable => write!(f, "-"),
}
}
}
// impl AppProtocol {
// pub(crate) const ALL: [AppProtocol; 25] = [
// AppProtocol::Unknown,
// AppProtocol::BGP,
// AppProtocol::DHCP,
// AppProtocol::DNS,
// AppProtocol::FTP,
// AppProtocol::FTPS,
// AppProtocol::HTTP,
// AppProtocol::HTTPS,
// AppProtocol::IMAP,
// AppProtocol::IMAPS,
// AppProtocol::LDAP,
// AppProtocol::LDAPS,
// AppProtocol::mDNS,
// AppProtocol::NetBIOS,
// AppProtocol::NTP,
// AppProtocol::POP,
// AppProtocol::POP3S,
// AppProtocol::SMTP,
// AppProtocol::SNMP,
// AppProtocol::SSDP,
// AppProtocol::SSH,
// AppProtocol::TACACS,
// AppProtocol::Telnet,
// AppProtocol::TFTP,
// AppProtocol::XMPP,
// ];
// }
impl FromStr for Service {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"" | "?" => Self::Unknown,
"-" => Self::NotApplicable,
name => Self::Name(name.to_string()),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_port_to_application_protocol_ftp() {
let result1 = from_port_to_application_protocol(Some(20));
assert_eq!(AppProtocol::FTP, result1);
let result2 = from_port_to_application_protocol(Some(21));
assert_eq!(AppProtocol::FTP, result2);
}
#[test]
fn from_port_to_application_protocol_ssh() {
let result = from_port_to_application_protocol(Some(22));
assert_eq!(AppProtocol::SSH, result);
}
#[test]
fn from_port_to_application_protocol_unknown() {
let result = from_port_to_application_protocol(Some(500));
assert_eq!(AppProtocol::Unknown, result);
}
#[test]
fn from_port_to_application_protocol_not_applicable() {
let result = from_port_to_application_protocol(None);
assert_eq!(AppProtocol::NotApplicable, result);
}
#[test]
fn app_protocol_display_ftp() {
let test_str = AppProtocol::FTP.to_string();
assert_eq!(test_str, "FTP");
}
#[test]
fn app_protocol_display_unknown() {
let test_str = AppProtocol::Unknown.to_string();
let test_str = Service::Unknown.to_string();
assert_eq!(test_str, "?");
}
#[test]
fn app_protocol_display_not_applicable() {
let test_str = AppProtocol::NotApplicable.to_string();
let test_str = Service::NotApplicable.to_string();
assert_eq!(test_str, "-");
}
}

View File

@@ -7,7 +7,7 @@
use crate::networking::types::icmp_type::IcmpType;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::AppProtocol;
use crate::Service;
/// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic.
///
@@ -27,7 +27,7 @@ pub struct InfoAddressPortPair {
/// Last occurrence of information exchange featuring the associate address:port pair as a source or destination.
pub final_timestamp: DateTime<Local>,
/// Application layer protocol carried by the associated address:port pair.
pub app_protocol: String,
pub app_protocol: Service,
/// Determines if the connection is incoming or outgoing
pub traffic_direction: TrafficDirection,
/// Types of the ICMP messages exchanged, with the relative count (this is empty if not ICMP)

View File

@@ -9,7 +9,7 @@
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::networking::types::traffic_direction::TrafficDirection;
use crate::AppProtocol;
use crate::Service;
/// Struct to be shared between the threads in charge of parsing packets and update reports.
pub struct InfoTraffic {
@@ -34,7 +34,7 @@ pub struct InfoTraffic {
/// Collection of favorite hosts that exchanged data in the last interval
pub favorites_last_interval: HashSet<Host>,
/// Map of the application layer protocols with their data info
pub app_protocols: HashMap<String, DataInfo>,
pub app_protocols: HashMap<Service, DataInfo>,
/// Map of the addresses waiting for a rDNS resolution; used to NOT send multiple rDNS for the same address
pub addresses_waiting_resolution: HashMap<String, DataInfo>,
/// Map of the resolved addresses with their full rDNS value and the corresponding host

View File

@@ -8,7 +8,7 @@
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
use crate::report::types::sort_type::SortType;
use crate::{AppProtocol, ChartType, InfoTraffic, ReportSortType, Sniffer};
use crate::{ChartType, InfoTraffic, ReportSortType, Service, Sniffer};
/// Returns the elements which satisfy the search constraints and belong to the given page,
/// and the total number of elements which satisfy the search constraints
@@ -87,18 +87,18 @@ pub fn get_host_entries(
pub fn get_app_entries(
info_traffic: &Arc<Mutex<InfoTraffic>>,
chart_type: ChartType,
) -> Vec<(String, DataInfo)> {
) -> Vec<(Service, DataInfo)> {
let info_traffic_lock = info_traffic.lock().unwrap();
let mut sorted_vec: Vec<(&String, &DataInfo)> = info_traffic_lock
let mut sorted_vec: Vec<(&Service, &DataInfo)> = info_traffic_lock
.app_protocols
.iter()
.filter(|(app_protocol, _)| app_protocol != &"-")
.filter(|(app_protocol, _)| app_protocol != &&Service::NotApplicable)
.collect();
sorted_vec.sort_by(|&(p1, a), &(p2, b)| {
if p1 == "?" {
if p1 == &Service::Unknown {
Ordering::Greater
} else if p2 == &"?" {
} else if p2 == &Service::Unknown {
Ordering::Less
} else {
match chart_type {
@@ -108,8 +108,5 @@ pub fn get_app_entries(
}
});
sorted_vec
.iter()
.map(|e| (String::from(e.0), *e.1))
.collect()
sorted_vec.iter().map(|e| (e.0.clone(), *e.1)).collect()
}