proc_connector/iter.rs
1//! Netlink message iterator for multi-part messages.
2//!
3//! This module contains the `NetlinkMessageIter` struct for iterating
4//! over multiple netlink messages packed into a single receive buffer.
5
6use std::iter::FusedIterator;
7
8use crate::consts::*;
9use crate::error::{Error, Result};
10use crate::parse::parse_netlink_message;
11use crate::proc_event::ProcEvent;
12
13/// Iterator over multiple netlink messages packed into a single receive buffer.
14///
15/// The netlink protocol can deliver multiple messages in one `recv` call
16/// (multi-part messages). This iterator handles walking through them.
17///
18/// # Example
19///
20/// ```no_run
21/// use proc_connector::{ProcConnector, NetlinkMessageIter};
22///
23/// let conn = ProcConnector::new().unwrap();
24/// let mut buf = vec![0u8; 4096];
25/// let n = conn.recv_raw(&mut buf).unwrap();
26///
27/// for msg in NetlinkMessageIter::new(&buf, n) {
28/// match msg {
29/// Ok(Some(event)) => println!("{event}"),
30/// Ok(None) => continue, // control message
31/// Err(e) => eprintln!("error: {e}"),
32/// }
33/// }
34/// ```
35pub struct NetlinkMessageIter<'a> {
36 buf: &'a [u8],
37 pos: usize,
38 len: usize,
39}
40
41impl<'a> NetlinkMessageIter<'a> {
42 /// Create a new iterator over `len` bytes starting at `buf`.
43 ///
44 /// # Example
45 ///
46 /// ```
47 /// use proc_connector::NetlinkMessageIter;
48 ///
49 /// let buf = vec![0u8; 4096];
50 /// let iter = NetlinkMessageIter::new(&buf, 0);
51 /// assert_eq!(iter.count(), 0);
52 /// ```
53 pub fn new(buf: &'a [u8], len: usize) -> Self {
54 NetlinkMessageIter { buf, pos: 0, len }
55 }
56}
57
58impl<'a> Iterator for NetlinkMessageIter<'a> {
59 type Item = Result<Option<ProcEvent>>;
60
61 fn next(&mut self) -> Option<Self::Item> {
62 if self.pos >= self.len {
63 return None;
64 }
65
66 let remaining = self.len - self.pos;
67 if remaining < SIZE_NLMSGHDR {
68 self.pos = self.len;
69 return Some(Err(Error::Truncated));
70 }
71
72 let nlmsg_len = read_u32(&self.buf[self.pos..], 0) as usize;
73 if nlmsg_len < SIZE_NLMSGHDR || nlmsg_len > remaining {
74 self.pos = self.len;
75 return Some(Err(Error::Truncated));
76 }
77
78 let msg_slice = &self.buf[self.pos..self.pos + nlmsg_len];
79 let nlmsg_type = read_u16(msg_slice, 4);
80
81 // Check for end of multi-part message.
82 // Kernel connector protocol uses NLMSG_DONE as the message type for
83 // ALL data messages (including proc events). A true multi-part DONE
84 // has no payload (nlmsg_len == SIZE_NLMSGHDR), while connector data
85 // messages have a cn_msg payload (nlmsg_len > SIZE_NLMSGHDR).
86 if nlmsg_type == NLMSG_DONE && nlmsg_len == SIZE_NLMSGHDR {
87 self.pos = self.len; // consume all remaining
88 return None; // Done is not an event, stop iteration
89 }
90
91 // Parse this single message
92 let result = parse_netlink_message(msg_slice, nlmsg_len);
93
94 // Advance position (aligned)
95 self.pos += nlmsg_align(nlmsg_len);
96
97 Some(result)
98 }
99}
100
101impl FusedIterator for NetlinkMessageIter<'_> {}
102
103// ---------------------------------------------------------------------------
104// Wire format helpers (private)
105// ---------------------------------------------------------------------------
106
107/// Read a `u32` from a byte slice at a given offset (native endian).
108#[inline]
109fn read_u32(buf: &[u8], off: usize) -> u32 {
110 let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
111 u32::from_ne_bytes(arr)
112}
113
114/// Read a `u16` from a byte slice at a given offset (native endian).
115#[inline]
116fn read_u16(buf: &[u8], off: usize) -> u16 {
117 let arr: [u8; 2] = buf[off..off + 2].try_into().unwrap();
118 u16::from_ne_bytes(arr)
119}