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 crate::consts::*;
7use crate::error::{Error, Result};
8use crate::parse::parse_netlink_message;
9use crate::proc_event::ProcEvent;
10
11/// Iterator over multiple netlink messages packed into a single receive buffer.
12///
13/// The netlink protocol can deliver multiple messages in one `recv` call
14/// (multi-part messages). This iterator handles walking through them.
15pub struct NetlinkMessageIter<'a> {
16    buf: &'a [u8],
17    pos: usize,
18    len: usize,
19}
20
21impl<'a> NetlinkMessageIter<'a> {
22    /// Create a new iterator over `len` bytes starting at `buf`.
23    pub fn new(buf: &'a [u8], len: usize) -> Self {
24        NetlinkMessageIter { buf, pos: 0, len }
25    }
26}
27
28impl<'a> Iterator for NetlinkMessageIter<'a> {
29    type Item = Result<Option<ProcEvent>>;
30
31    fn next(&mut self) -> Option<Self::Item> {
32        if self.pos >= self.len {
33            return None;
34        }
35
36        let remaining = self.len - self.pos;
37        if remaining < SIZE_NLMSGHDR {
38            return Some(Err(Error::Truncated));
39        }
40
41        let nlmsg_len = read_u32(&self.buf[self.pos..], 0) as usize;
42        if nlmsg_len < SIZE_NLMSGHDR || nlmsg_len > remaining {
43            return Some(Err(Error::Truncated));
44        }
45
46        let msg_slice = &self.buf[self.pos..self.pos + nlmsg_len];
47        let nlmsg_type = read_u16(msg_slice, 4);
48
49        // Check for end of multi-part message.
50        // Kernel connector protocol uses NLMSG_DONE as the message type for
51        // ALL data messages (including proc events). A true multi-part DONE
52        // has no payload (nlmsg_len == SIZE_NLMSGHDR), while connector data
53        // messages have a cn_msg payload (nlmsg_len > SIZE_NLMSGHDR).
54        if nlmsg_type == NLMSG_DONE && nlmsg_len == SIZE_NLMSGHDR {
55            self.pos = self.len; // consume all remaining
56            return None; // Done is not an event, stop iteration
57        }
58
59        // Parse this single message
60        let result = parse_netlink_message(msg_slice, nlmsg_len);
61
62        // Advance position (aligned)
63        self.pos += nlmsg_align(nlmsg_len);
64
65        Some(result)
66    }
67}
68
69// ---------------------------------------------------------------------------
70// Wire format helpers (private)
71// ---------------------------------------------------------------------------
72
73/// Read a `u32` from a byte slice at a given offset (native endian).
74#[inline]
75fn read_u32(buf: &[u8], off: usize) -> u32 {
76    let arr: [u8; 4] = buf[off..off + 4].try_into().unwrap();
77    u32::from_ne_bytes(arr)
78}
79
80/// Read a `u16` from a byte slice at a given offset (native endian).
81#[inline]
82fn read_u16(buf: &[u8], off: usize) -> u16 {
83    let arr: [u8; 2] = buf[off..off + 2].try_into().unwrap();
84    u16::from_ne_bytes(arr)
85}