1use cu29_traits::CuResult;
2use pcap::Capture;
3use std::net::UdpSocket;
4use std::path::Path;
5use std::thread::sleep;
6use std::time::{Duration, Instant};
7
8pub struct PcapStreamer {
10 capture: Capture<pcap::Offline>,
11 socket: UdpSocket,
12 target_addr: String,
13 last_packet_ts: Option<Duration>,
14 start_instant: Instant,
15}
16
17impl PcapStreamer {
18 pub fn new(file_path: impl AsRef<Path>, target_addr: impl Into<String>) -> Self {
19 let capture = Capture::from_file(file_path).expect("Failed to open pcap file");
20 let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind UDP socket");
21 Self {
22 capture,
23 socket,
24 target_addr: target_addr.into(),
25 last_packet_ts: None,
26 start_instant: Instant::now(),
27 }
28 }
29
30 pub fn send_next<const PS: usize>(&mut self) -> CuResult<bool> {
34 let packet = match self.capture.next_packet() {
36 Ok(packet) => packet,
37 Err(_) => return Ok(false), };
39
40 let payload_offset = 42;
42 if packet.data.len() < payload_offset + PS {
43 return Err("Packet too short".into());
44 }
45
46 let payload = &packet.data[payload_offset..payload_offset + PS];
48
49 let ts = packet.header.ts;
51 let packet_ts = Duration::new(ts.tv_sec as u64, ts.tv_usec as u32 * 1000);
52
53 if let Some(last_ts) = self.last_packet_ts {
54 let elapsed = self.start_instant.elapsed();
56 if packet_ts > last_ts {
57 let wait_time = packet_ts - last_ts;
58 if elapsed < wait_time {
59 sleep(wait_time - elapsed);
60 }
61 }
62 }
63 self.last_packet_ts = Some(packet_ts);
64 self.socket
65 .send_to(payload, &self.target_addr)
66 .expect("Failed to send packet");
67 Ok(true)
68 }
69}