1use derive_builder::Builder;
4use pnet::packet::{Packet, ethernet, ip, ipv4, tcp};
5use std::{
6 collections::HashMap,
7 net::Ipv4Addr,
8 sync::{self, Arc, LazyLock, mpsc},
9 thread::{self, JoinHandle},
10 time::Duration,
11};
12
13use crate::{
14 error::{RLanLibError, Result},
15 network::NetworkInterface,
16 packet::{
17 self, rst_packet::RstPacketBuilder, syn_packet::SynPacketBuilder,
18 wire::Wire,
19 },
20 scanners::{PortSet, Scanning, heartbeat::HeartBeat},
21 targets::ports::PortTargets,
22};
23
24use super::{Device, Port, ScanMessage, Scanner};
25
26static SERVICES: LazyLock<HashMap<u16, &str>> = LazyLock::new(|| {
27 HashMap::from([
28 (20, "ftp-data"),
29 (21, "ftp"),
30 (22, "ssh"),
31 (23, "telnet"),
32 (25, "smtp"),
33 (53, "dns"),
34 (80, "http"),
35 (110, "pop3"),
36 (143, "imap"),
37 (443, "https"),
38 (445, "microsoft-ds"),
39 (587, "submission"),
40 (993, "imaps"),
41 (995, "pop3s"),
42 (1433, "mssql"),
43 (3306, "mysql"),
44 (3389, "rdp"),
45 (5432, "postgresql"),
46 (5900, "vnc"),
47 (6379, "redis"),
48 (8080, "http-alt"),
49 (8443, "https-alt"),
50 (27017, "mongodb"),
51 ])
52});
53
54#[derive(Clone, Builder)]
56#[builder(setter(into))]
57pub struct SYNScanner {
58 interface: Arc<NetworkInterface>,
60 wire: Wire,
62 targets: Vec<Device>,
64 ports: Arc<PortTargets>,
66 source_port: u16,
68 idle_timeout: Duration,
70 notifier: mpsc::Sender<ScanMessage>,
72}
73
74impl SYNScanner {
75 pub fn builder() -> SYNScannerBuilder {
77 SYNScannerBuilder::default()
78 }
79
80 fn process_port(&self, port: u16) -> Result<()> {
81 for device in self.targets.iter() {
82 thread::sleep(packet::DEFAULT_PACKET_SEND_TIMING);
84
85 log::debug!("scanning SYN target: {}:{}", device.ip, port);
86
87 let dest_ipv4 = device.ip;
88 let dest_mac = device.mac;
89
90 let syn_packet = SynPacketBuilder::default()
91 .source_ip(self.interface.ipv4)
92 .source_mac(self.interface.mac)
93 .source_port(self.source_port)
94 .dest_ip(dest_ipv4)
95 .dest_mac(dest_mac)
96 .dest_port(port)
97 .build()?;
98
99 let pkt_buf = syn_packet.to_raw();
100
101 self.notifier
103 .send(ScanMessage::Info(Scanning {
104 ip: device.ip,
105 port: Some(port),
106 }))
107 .map_err(RLanLibError::from_channel_send_error)?;
108
109 let mut sender = self.wire.0.lock()?;
110
111 sender.send(&pkt_buf).map_err(|e| RLanLibError::Scan {
113 ip: Some(device.ip.to_string()),
114 port: Some(port.to_string()),
115 error: e.to_string(),
116 })?;
117 }
118
119 Ok(())
120 }
121
122 fn process_incoming_packet(
123 &self,
124 pkt: &[u8],
125 device_map: &HashMap<Ipv4Addr, Device>,
126 ) -> Result<()> {
127 let Some(eth) = ethernet::EthernetPacket::new(pkt) else {
128 return Ok(());
129 };
130
131 let Some(header) = ipv4::Ipv4Packet::new(eth.payload()) else {
132 return Ok(());
133 };
134
135 let device_ip = header.get_source();
136 let protocol = header.get_next_level_protocol();
137 let payload = header.payload();
138
139 if protocol != ip::IpNextHeaderProtocols::Tcp {
140 return Ok(());
141 }
142
143 let Some(tcp_packet) = tcp::TcpPacket::new(payload) else {
144 return Ok(());
145 };
146
147 let destination_port = tcp_packet.get_destination();
148 let matches_destination = destination_port == self.source_port;
149 let flags: u8 = tcp_packet.get_flags();
150 let sequence = tcp_packet.get_sequence();
151 let is_syn_ack = flags == tcp::TcpFlags::SYN + tcp::TcpFlags::ACK;
152
153 if !matches_destination || !is_syn_ack {
154 return Ok(());
155 }
156
157 let Some(device) = device_map.get(&device_ip) else {
158 return Ok(());
159 };
160
161 let port = tcp_packet.get_source();
162
163 let dest_ipv4 = device.ip;
167 let dest_mac = device.mac;
168
169 let rst_packet = RstPacketBuilder::default()
170 .source_ip(self.interface.ipv4)
171 .source_mac(self.interface.mac)
172 .source_port(self.source_port)
173 .dest_ip(dest_ipv4)
174 .dest_mac(dest_mac)
175 .dest_port(port)
176 .sequence_number(sequence + 1)
177 .build()?;
178
179 let rst_packet = rst_packet.to_raw();
180
181 let mut rst_sender = self.wire.0.lock()?;
182
183 log::debug!("sending RST packet to {}:{}", device.ip, port);
184
185 rst_sender.send(&rst_packet)?;
186
187 let service = SERVICES
188 .get(&port)
189 .map(|s| s.to_string())
190 .unwrap_or_default();
191
192 let mut ports = PortSet::new();
193 ports.0.insert(Port { id: port, service });
194
195 self.notifier
196 .send(ScanMessage::SYNScanDevice(Device {
197 open_ports: ports,
198 ..device.clone()
199 }))
200 .map_err(RLanLibError::from_channel_send_error)?;
201
202 Ok(())
203 }
204
205 fn read_packets(
208 &self,
209 done_rx: mpsc::Receiver<()>,
210 ) -> Result<JoinHandle<Result<()>>> {
211 let self_clone = self.clone();
212 let (heartbeat_tx, heartbeat_rx) = sync::mpsc::channel::<()>();
213
214 let heartbeat = HeartBeat::builder()
215 .source_mac(self.interface.mac)
216 .source_ipv4(self.interface.ipv4)
217 .source_port(self.source_port)
218 .packet_sender(Arc::clone(&self.wire.0))
219 .build()?;
220
221 let heart_handle = heartbeat.start_in_thread(heartbeat_rx)?;
222
223 Ok(thread::spawn(move || -> Result<()> {
224 let mut reader = self_clone.wire.1.lock()?;
225
226 let device_map: HashMap<Ipv4Addr, Device> = self_clone
228 .targets
229 .iter()
230 .map(|d| (d.ip, d.clone()))
231 .collect();
232
233 loop {
234 if done_rx.try_recv().is_ok() {
235 log::debug!("exiting syn packet reader");
236 if let Err(e) = heartbeat_tx.send(()) {
237 log::error!("failed to stop heartbeat: {}", e);
238 }
239
240 break;
241 }
242
243 let pkt = reader.next_packet()?;
244 self_clone.process_incoming_packet(pkt, &device_map)?;
245 }
246
247 heart_handle.join()??;
248
249 Ok(())
250 }))
251 }
252}
253
254impl Scanner for SYNScanner {
256 fn scan(&self) -> Result<JoinHandle<Result<()>>> {
257 log::debug!("performing SYN scan on targets: {:?}", self.targets);
258
259 let self_clone = self.clone();
260 let (done_tx, done_rx) = mpsc::channel::<()>();
261
262 log::debug!("starting syn packet reader");
263
264 let read_handle = self.read_packets(done_rx)?;
265
266 let handle = thread::spawn(move || -> Result<()> {
268 let mut scan_error: Option<RLanLibError> = None;
269
270 if let Err(err) =
271 self_clone.ports.lazy_loop(|p| self_clone.process_port(p))
272 {
273 scan_error = Some(err);
274 }
275
276 thread::sleep(self_clone.idle_timeout);
277
278 self_clone
279 .notifier
280 .send(ScanMessage::Done)
281 .map_err(RLanLibError::from_channel_send_error)?;
282
283 let _ = done_tx.send(());
286
287 let read_result = read_handle.join()?;
288
289 if let Some(err) = scan_error {
290 return Err(err);
291 }
292
293 read_result
294 });
295
296 Ok(handle)
297 }
298}
299
300#[cfg(test)]
301#[path = "./syn_scanner_tests.rs"]
302mod tests;