rusty_pcap/pcap/tokio_impl/
reader.rs

1//! Asynchronous reader for PCAP files
2use crate::{
3    pcap::PcapParseError, pcap::file_header::PcapFileHeader, pcap::packet_header::PacketHeader,
4};
5use tokio::io::{AsyncRead, AsyncReadExt};
6#[derive(Debug)]
7pub struct AsyncPcapReader<R: AsyncRead + Unpin> {
8    reader: R,
9    /// Buffer for packet data
10    buffer: Box<[u8]>,
11    /// Buffer for packet header
12    header_buffer: [u8; 16],
13    file_header: PcapFileHeader,
14}
15impl<R: AsyncRead + Unpin> AsyncPcapReader<R> {
16    /// Creates a new `AsyncPcapReader` from a reader
17    /// Returns `Ok(Self)` on success, or `Err` if there was an error
18    /// reading the file header
19    ///
20    /// A buffer is allocated based on the snap length in the file header
21    pub async fn new(mut reader: R) -> Result<Self, PcapParseError> {
22        let mut file_header = [0u8; 24];
23        reader.read_exact(&mut file_header).await?;
24        let file_header = PcapFileHeader::try_from(&file_header)?;
25        let buffer = vec![0u8; file_header.snap_length as usize].into_boxed_slice();
26        Ok(Self {
27            reader,
28            buffer,
29            file_header,
30            header_buffer: [0; 16],
31        })
32    }
33    /// Returns the file header of the pcap file
34    pub fn file_header(&self) -> &PcapFileHeader {
35        &self.file_header
36    }
37    /// Reads the next packet from the pcap file
38    /// Returns `Ok(None)` if there are no more packets to read
39    /// Returns `Err` if there was an error reading the packet
40    pub async fn next_packet(&mut self) -> Result<Option<(PacketHeader, &[u8])>, PcapParseError> {
41        if let Err(err) = self.reader.read_exact(&mut self.header_buffer).await {
42            if err.kind() == std::io::ErrorKind::UnexpectedEof {
43                return Ok(None); // No more packets
44            } else {
45                return Err(PcapParseError::IO(err));
46            }
47        }
48        let packet_header = PacketHeader::parse_bytes(
49            &self.header_buffer,
50            self.file_header.magic_number_and_endianness.endianness,
51            &self.file_header.version,
52        )?;
53        // Check if the included length is greater than the snap length
54        // This is a sanity check to prevent reading more data than allocated
55        if packet_header.include_len > self.file_header.snap_length {
56            return Err(PcapParseError::InvalidPacketLength {
57                snap_length: self.file_header.snap_length,
58                incl_len: packet_header.include_len,
59            });
60        }
61        let mut_buffer: &mut [u8] = &mut self.buffer;
62        self.reader
63            .read_exact(&mut mut_buffer[0..(packet_header.include_len as usize)])
64            .await?;
65
66        Ok(Some((
67            packet_header,
68            &self.buffer[..(packet_header.include_len as usize)],
69        )))
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use etherparse::{NetSlice, SlicedPacket};
76
77    use super::*;
78    #[tokio::test]
79    async fn read_packets_from_file() {
80        let file = tokio::fs::File::open("test_data/test.pcap")
81            .await
82            .expect("Failed to open test.pcap");
83        let mut reader = AsyncPcapReader::new(file)
84            .await
85            .expect("Failed to create SyncPcapReader");
86
87        while let Ok(Some((header, data))) = reader.next_packet().await {
88            println!("Packet Header: {:?}", header);
89            let parse = SlicedPacket::from_ethernet(data).expect("Failed to parse packet");
90            if let Some(net_slice) = parse.net {
91                match net_slice {
92                    NetSlice::Ipv4(ipv4) => {
93                        println!("IPv4 Packet: {:?}", ipv4.header());
94                        println!("IPv4 Destin: {:?}", ipv4.header().destination_addr());
95                        println!("IPv4 Source: {:?}", ipv4.header().source_addr());
96                    }
97                    NetSlice::Ipv6(ipv6) => {
98                        println!("IPv6 Packet: {:?}", ipv6.header());
99                    }
100                    NetSlice::Arp(arp) => {
101                        println!("ARP Packet: {:?}", arp);
102                    }
103                }
104            } else {
105                println!("Non Packet");
106            }
107        }
108    }
109}