noq_udp/cmsg/
mod.rs

1use std::{
2    ffi::{c_int, c_uchar},
3    ptr,
4};
5
6#[cfg(unix)]
7#[path = "unix.rs"]
8mod imp;
9
10#[cfg(windows)]
11#[path = "windows.rs"]
12mod imp;
13
14pub(crate) use imp::Aligned;
15
16/// Helper to encode a series of control messages (native "cmsgs") to a buffer for use in `sendmsg`
17//  like API.
18///
19/// The operation must be "finished" for the native msghdr to be usable, either by calling `finish`
20/// explicitly or by dropping the `Encoder`.
21pub(crate) struct Encoder<'a, M: MsgHdr> {
22    hdr: &'a mut M,
23    cmsg: Option<&'a mut M::ControlMessage>,
24    len: usize,
25}
26
27impl<'a, M: MsgHdr> Encoder<'a, M> {
28    /// # Safety
29    /// - `hdr` must contain a suitably aligned pointer to a big enough buffer to hold control
30    ///   messages bytes. All bytes of this buffer can be safely written.
31    /// - The `Encoder` must be dropped before `hdr` is passed to a system call, and must not be
32    ///   leaked.
33    pub(crate) unsafe fn new(hdr: &'a mut M) -> Self {
34        Self {
35            cmsg: unsafe { hdr.cmsg_first_hdr().as_mut() },
36            hdr,
37            len: 0,
38        }
39    }
40
41    /// Append a control message to the buffer.
42    ///
43    /// # Panics
44    /// - If insufficient buffer space remains.
45    /// - If `T` has stricter alignment requirements than `M::ControlMessage`
46    pub(crate) fn push<T: Copy>(&mut self, level: c_int, ty: c_int, value: T) {
47        assert!(align_of::<T>() <= align_of::<M::ControlMessage>());
48        let space = M::ControlMessage::cmsg_space(size_of_val(&value));
49        assert!(
50            self.hdr.control_len() >= self.len + space,
51            "control message buffer too small. Required: {}, Available: {}",
52            self.len + space,
53            self.hdr.control_len()
54        );
55        let cmsg = self.cmsg.take().expect("no control buffer space remaining");
56        cmsg.set(level, ty, M::ControlMessage::cmsg_len(size_of_val(&value)));
57        unsafe {
58            ptr::write(cmsg.cmsg_data() as *const T as *mut T, value);
59        }
60        self.len += space;
61        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(cmsg).as_mut() };
62    }
63
64    /// Finishes appending control messages to the buffer
65    pub(crate) fn finish(self) {
66        // Delegates to the `Drop` impl
67    }
68}
69
70// Statically guarantees that the encoding operation is "finished" before the control buffer is read
71// by `sendmsg` like API.
72impl<M: MsgHdr> Drop for Encoder<'_, M> {
73    fn drop(&mut self) {
74        self.hdr.set_control_len(self.len as _);
75    }
76}
77
78/// # Safety
79///
80/// `cmsg` must refer to a native cmsg containing a payload of type `T`
81pub(crate) unsafe fn decode<T: Copy, C: CMsgHdr>(cmsg: &impl CMsgHdr) -> T {
82    assert!(align_of::<T>() <= align_of::<C>());
83    debug_assert_eq!(cmsg.len(), C::cmsg_len(size_of::<T>()));
84    unsafe { ptr::read(cmsg.cmsg_data() as *const T) }
85}
86
87pub(crate) struct Iter<'a, M: MsgHdr> {
88    hdr: &'a M,
89    cmsg: Option<&'a M::ControlMessage>,
90}
91
92impl<'a, M: MsgHdr> Iter<'a, M> {
93    /// # Safety
94    ///
95    /// `hdr` must hold a pointer to memory outliving `'a` which can be soundly read for the
96    /// lifetime of the constructed `Iter` and contains a buffer of native cmsgs, i.e. is aligned
97    //  for native `cmsghdr`, is fully initialized, and has correct internal links.
98    pub(crate) unsafe fn new(hdr: &'a M) -> Self {
99        Self {
100            hdr,
101            cmsg: unsafe { hdr.cmsg_first_hdr().as_ref() },
102        }
103    }
104}
105
106impl<'a, M: MsgHdr> Iterator for Iter<'a, M> {
107    type Item = &'a M::ControlMessage;
108
109    fn next(&mut self) -> Option<Self::Item> {
110        let current = self.cmsg.take()?;
111        self.cmsg = unsafe { self.hdr.cmsg_nxt_hdr(current).as_ref() };
112
113        #[cfg(apple_fast)]
114        {
115            // On MacOS < 14 CMSG_NXTHDR might continuously return a zeroed cmsg. In
116            // such case, return `None` instead, thus indicating the end of
117            // the cmsghdr chain.
118            if current.len() < size_of::<M::ControlMessage>() {
119                return None;
120            }
121        }
122
123        Some(current)
124    }
125}
126
127// Helper traits for native types for control messages
128pub(crate) trait MsgHdr {
129    type ControlMessage: CMsgHdr;
130
131    fn cmsg_first_hdr(&self) -> *mut Self::ControlMessage;
132
133    fn cmsg_nxt_hdr(&self, cmsg: &Self::ControlMessage) -> *mut Self::ControlMessage;
134
135    /// Sets the number of control messages added to this `struct msghdr`.
136    ///
137    /// Note that this is a destructive operation and should only be done as a finalisation
138    /// step.
139    fn set_control_len(&mut self, len: usize);
140
141    fn control_len(&self) -> usize;
142}
143
144pub(crate) trait CMsgHdr {
145    fn cmsg_len(length: usize) -> usize;
146
147    fn cmsg_space(length: usize) -> usize;
148
149    fn cmsg_data(&self) -> *mut c_uchar;
150
151    fn set(&mut self, level: c_int, ty: c_int, len: usize);
152
153    fn len(&self) -> usize;
154}
155
156#[cfg(unix)]
157pub(crate) const LEN: usize = 96;