1use std::net::{IpAddr, SocketAddr};
2
3use etherparse::{err::packet, NetSlice, SlicedPacket, TransportSlice};
4use pcap::{Activated, Capture, Packet, PacketCodec, PacketIter, Savefile};
5use thiserror::Error;
6
7pub struct UdpIter<S: Activated + ?Sized> {
8 inner: PacketIter<S, UdpCodec>,
9}
10
11#[derive(Debug, Error)]
12pub enum DissectError {
13 #[error("{0}")]
14 Cap(#[from] pcap::Error),
15 #[error("{0}")]
16 ParsePacket(#[from] packet::SliceError),
17}
18
19impl<S: Activated + ?Sized> UdpIter<S> {
20 pub fn new(capture: Capture<S>, file: Option<Savefile>) -> Self {
21 UdpIter {
22 inner: capture.iter(UdpCodec { file }),
23 }
24 }
25}
26
27impl<S: Activated + ?Sized> Iterator for UdpIter<S> {
28 type Item = Result<(SocketAddr, SocketAddr, Box<[u8]>), DissectError>;
29
30 fn next(&mut self) -> Option<Self::Item> {
31 loop {
32 match self
33 .inner
34 .next()?
35 .map_err(DissectError::Cap)
36 .and_then(|x| x)
37 .transpose()
38 {
39 Some(v) => break Some(v),
40 None => continue,
41 }
42 }
43 }
44}
45
46struct UdpCodec {
47 file: Option<Savefile>,
48}
49
50impl PacketCodec for UdpCodec {
51 type Item = Result<Option<(SocketAddr, SocketAddr, Box<[u8]>)>, DissectError>;
52
53 fn decode(&mut self, packet: Packet<'_>) -> Self::Item {
54 let eth = SlicedPacket::from_ethernet(packet.data)?;
55 if let (Some(net), Some(transport)) = (eth.net, eth.transport) {
56 let (src_ip, dst_ip) = match net {
57 NetSlice::Ipv4(ip) => (
58 IpAddr::V4(ip.header().source().into()),
59 IpAddr::V4(ip.header().destination().into()),
60 ),
61 NetSlice::Ipv6(ip) => (
62 IpAddr::V6(ip.header().source().into()),
63 IpAddr::V6(ip.header().destination().into()),
64 ),
65 NetSlice::Arp(_) => return Ok(None),
66 };
67 let (src_port, dst_port, slice) = match transport {
68 TransportSlice::Udp(udp) => {
69 (udp.source_port(), udp.destination_port(), udp.payload())
70 }
71 _ => return Ok(None),
72 };
73 if let Some(file) = &mut self.file {
74 file.write(&packet);
75 file.flush()?;
76 }
77
78 Ok(Some((
79 SocketAddr::new(src_ip, src_port),
80 SocketAddr::new(dst_ip, dst_port),
81 slice.to_vec().into_boxed_slice(),
82 )))
83 } else {
84 Ok(None)
85 }
86 }
87}