Skip to main content

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}