1use itertools::Itertools;
9#[cfg(test)]
10use mockall::{automock, predicate::*};
11
12use pnet::util::MacAddr;
13use serde;
14use serde::{Deserialize, Serialize};
15use std::collections::HashSet;
16use std::fmt::Display;
17use std::hash::Hash;
18use std::net::Ipv4Addr;
19use std::thread::JoinHandle;
20
21use crate::error::Result;
22
23pub mod arp_scanner;
24pub mod full_scanner;
25pub mod heartbeat;
26pub mod syn_scanner;
27
28pub const IDLE_TIMEOUT: u16 = 10000;
30
31#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
32pub struct Port {
34 pub id: u16,
36 pub service: String,
38}
39
40impl Display for Port {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 if self.service.is_empty() {
43 write!(f, "{}", self.id)
44 } else {
45 write!(f, "{}:{}", self.id, self.service)
46 }
47 }
48}
49
50impl PartialEq for Port {
51 fn eq(&self, other: &Self) -> bool {
52 self.id == other.id
53 }
54}
55
56impl Hash for Port {
57 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
58 self.id.hash(state);
59 }
60}
61
62impl Ord for Port {
63 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
64 self.id.cmp(&other.id)
65 }
66}
67
68impl PartialOrd for Port {
69 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
70 Some(self.cmp(other))
71 }
72}
73
74#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
77pub struct PortSet(pub HashSet<Port>);
78
79impl PortSet {
80 pub fn new() -> Self {
82 Self(HashSet::new())
83 }
84
85 pub fn to_sorted_vec(&self) -> Vec<Port> {
87 self.0.iter().cloned().sorted().collect()
88 }
89}
90
91impl From<HashSet<Port>> for PortSet {
92 fn from(value: HashSet<Port>) -> Self {
93 Self(value)
94 }
95}
96
97fn serialize_to_string<S, T>(
98 val: &T,
99 s: S,
100) -> std::result::Result<S::Ok, S::Error>
101where
102 S: serde::Serializer,
103 T: std::fmt::Display,
104{
105 s.serialize_str(&val.to_string())
106}
107
108fn deserialize_from_str<'de, D, T>(d: D) -> std::result::Result<T, D::Error>
109where
110 D: serde::Deserializer<'de>,
111 T: std::str::FromStr,
112 T::Err: std::fmt::Display,
113{
114 let s = String::deserialize(d)?;
115 s.parse::<T>().map_err(serde::de::Error::custom)
116}
117
118#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
120pub struct Device {
122 pub hostname: String,
124 pub ip: Ipv4Addr,
126 #[serde(
128 serialize_with = "serialize_to_string",
129 deserialize_with = "deserialize_from_str"
130 )]
131 pub mac: MacAddr,
132 pub vendor: String,
134 pub is_current_host: bool,
136 pub is_gateway: bool,
138 #[serde(default)]
140 pub open_ports: PortSet,
141 #[serde(default)]
143 pub latency_ms: Option<u128>,
144 #[serde(default)]
146 pub response_ttl: Option<u8>,
147}
148
149impl Default for Device {
150 fn default() -> Self {
151 Self {
152 hostname: "".into(),
153 ip: Ipv4Addr::UNSPECIFIED,
154 is_current_host: false,
155 is_gateway: false,
156 latency_ms: None,
157 response_ttl: None,
158 mac: MacAddr::default(),
159 open_ports: PortSet::new(),
160 vendor: "".into(),
161 }
162 }
163}
164
165impl PartialEq for Device {
166 fn eq(&self, other: &Self) -> bool {
167 self.ip == other.ip && self.mac == other.mac
168 }
169}
170
171impl Hash for Device {
172 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
173 self.ip.hash(state);
174 self.mac.hash(state);
175 }
176}
177
178impl Ord for Device {
179 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
180 self.ip.cmp(&other.ip)
181 }
182}
183
184impl PartialOrd for Device {
185 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
186 Some(self.cmp(other))
187 }
188}
189
190#[derive(Debug)]
191pub struct Scanning {
193 pub ip: Ipv4Addr,
195 pub port: Option<u16>,
197}
198
199#[derive(Debug)]
200pub enum ScanMessage {
203 Done,
205 Info(Scanning),
207 ARPScanDevice(Device),
209 SYNScanDevice(Device),
211}
212
213#[cfg_attr(test, automock)]
214pub trait Scanner: Sync + Send {
216 fn scan(&self) -> Result<JoinHandle<Result<()>>>;
218}
219
220#[cfg(test)]
221#[path = "./scanners_tests.rs"]
222mod tests;