use itertools::Itertools;
#[cfg(test)]
use mockall::{automock, predicate::*};
use pnet::util::MacAddr;
use serde;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt::Display;
use std::hash::Hash;
use std::net::Ipv4Addr;
use std::thread::JoinHandle;
use crate::error::Result;
pub mod arp_scanner;
pub mod full_scanner;
pub mod heartbeat;
pub mod syn_scanner;
pub const IDLE_TIMEOUT: u16 = 10000;
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
pub struct Port {
pub id: u16,
pub service: String,
}
impl Display for Port {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.service.is_empty() {
write!(f, "{}", self.id)
} else {
write!(f, "{}:{}", self.id, self.service)
}
}
}
impl PartialEq for Port {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Hash for Port {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Ord for Port {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for Port {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PortSet(pub HashSet<Port>);
impl PortSet {
pub fn new() -> Self {
Self(HashSet::new())
}
pub fn to_sorted_vec(&self) -> Vec<Port> {
self.0.iter().cloned().sorted().collect()
}
}
impl From<HashSet<Port>> for PortSet {
fn from(value: HashSet<Port>) -> Self {
Self(value)
}
}
fn serialize_to_string<S, T>(
val: &T,
s: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: std::fmt::Display,
{
s.serialize_str(&val.to_string())
}
fn deserialize_from_str<'de, D, T>(d: D) -> std::result::Result<T, D::Error>
where
D: serde::Deserializer<'de>,
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
let s = String::deserialize(d)?;
s.parse::<T>().map_err(serde::de::Error::custom)
}
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
pub struct Device {
pub hostname: String,
pub ip: Ipv4Addr,
#[serde(
serialize_with = "serialize_to_string",
deserialize_with = "deserialize_from_str"
)]
pub mac: MacAddr,
pub vendor: String,
pub is_current_host: bool,
pub is_gateway: bool,
pub open_ports: PortSet,
pub latency_ms: Option<u128>,
pub response_ttl: Option<u8>,
}
impl Default for Device {
fn default() -> Self {
Self {
hostname: "".into(),
ip: Ipv4Addr::UNSPECIFIED,
is_current_host: false,
is_gateway: false,
latency_ms: None,
response_ttl: None,
mac: MacAddr::default(),
open_ports: PortSet::new(),
vendor: "".into(),
}
}
}
impl PartialEq for Device {
fn eq(&self, other: &Self) -> bool {
self.ip == other.ip && self.mac == other.mac
}
}
impl Hash for Device {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.ip.hash(state);
self.mac.hash(state);
}
}
impl Ord for Device {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.ip.cmp(&other.ip)
}
}
impl PartialOrd for Device {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug)]
pub struct Scanning {
pub ip: Ipv4Addr,
pub port: Option<u16>,
}
#[derive(Debug)]
pub enum ScanMessage {
Done,
Info(Scanning),
ARPScanDevice(Device),
SYNScanDevice(Device),
}
#[cfg_attr(test, automock)]
pub trait Scanner: Sync + Send {
fn scan(&self) -> Result<JoinHandle<Result<()>>>;
}