Skip to main content

compio_io\ancillary/
mod.rs

1//! Ancillary data (control message) support for connected streams.
2//!
3//! Ancillary messages are used to pass out-of-band information such as file
4//! descriptors (Unix domain sockets), credentials, or kTLS record types.
5//!
6//! # Types
7//!
8//! - [`AncillaryBuf`]: A fixed-size, properly aligned stack buffer for
9//!   ancillary messages.
10//! - [`AncillaryBuilder`]: A builder for constructing ancillary messages into a
11//!   [`AncillaryBuf`].
12//! - [`AncillaryIter`]: An iterator over a buffer of ancillary messages.
13//! - [`AncillaryRef`]: A reference to a single ancillary data entry.
14//! - [`AncillaryData`]: Trait for types that can be encoded/decoded as
15//!   ancillary data payloads.
16//! - [`CodecError`]: Error type for encoding/decoding operations.
17//!
18//! # Traits
19//!
20//! - [`AsyncReadAncillary`]: read data together with ancillary data
21//! - [`AsyncWriteAncillary`]: write data together with ancillary data
22//!
23//! # Functions
24//!
25//! - [`ancillary_space`]: Helper function to calculate ancillary message size
26//!   for a type.
27//!
28//! # Modules
29//!
30//! - [`bytemuck_ext`]: Extension module for automatic [`AncillaryData`]
31//!   implementation via bytemuck (requires `bytemuck` feature).
32
33use std::{
34    mem::MaybeUninit,
35    ops::{Deref, DerefMut},
36    ptr,
37};
38
39use compio_buf::{IoBuf, IoBufMut, IoBufMutExt, SetLen, SetLenExt};
40
41mod io;
42
43pub use rustix::net::ReturnFlags;
44
45pub use self::io::*;
46
47#[cfg(feature = "bytemuck")]
48pub mod bytemuck_ext;
49mod sys;
50
51/// Reference to an ancillary (control) message.
52pub struct AncillaryRef<'a>(sys::CMsgRef<'a>);
53
54impl AncillaryRef<'_> {
55    /// Returns the level of the control message.
56    pub fn level(&self) -> i32 {
57        self.0.level()
58    }
59
60    /// Returns the type of the control message.
61    pub fn ty(&self) -> i32 {
62        self.0.ty()
63    }
64
65    /// Returns the length of the control message.
66    #[allow(clippy::len_without_is_empty)]
67    pub fn len(&self) -> usize {
68        self.0.len() as _
69    }
70
71    /// Returns a copy of the data in the control message.
72    pub fn data<T: AncillaryData>(&self) -> Result<T, CodecError> {
73        self.0.decode_data()
74    }
75}
76
77/// An iterator for ancillary (control) messages.
78pub struct AncillaryIter<'a> {
79    inner: sys::CMsgIter,
80    buffer: &'a [u8],
81}
82
83impl<'a> AncillaryIter<'a> {
84    /// Create [`AncillaryIter`] with the given buffer.
85    ///
86    /// # Panics
87    ///
88    /// This function will panic if the buffer is too short or not properly
89    /// aligned.
90    ///
91    /// # Safety
92    ///
93    /// The buffer should contain valid control messages.
94    pub unsafe fn new(buffer: &'a [u8]) -> Self {
95        Self {
96            inner: sys::CMsgIter::new(buffer.as_ptr(), buffer.len()),
97            buffer,
98        }
99    }
100}
101
102impl<'a> Iterator for AncillaryIter<'a> {
103    type Item = AncillaryRef<'a>;
104
105    fn next(&mut self) -> Option<Self::Item> {
106        unsafe {
107            let cmsg = self.inner.current(self.buffer.as_ptr());
108            self.inner.next(self.buffer.as_ptr());
109            cmsg.map(AncillaryRef)
110        }
111    }
112}
113
114/// Helper to construct ancillary (control) messages.
115pub struct AncillaryBuilder<'a, B: ?Sized> {
116    inner: sys::CMsgIter,
117    buffer: &'a mut B,
118}
119
120impl<'a, B: IoBufMut + ?Sized> AncillaryBuilder<'a, B> {
121    /// Create [`AncillaryBuilder`] with the given buffer. The buffer will be
122    /// cleared on creation.
123    ///
124    /// # Panics
125    ///
126    /// This function will panic if the buffer is too short or not properly
127    /// aligned.
128    pub fn new(buffer: &'a mut B) -> Self {
129        // SAFETY: always safe to make it empty.
130        unsafe { buffer.set_len(0) };
131        let slice = buffer.ensure_init();
132        let inner = sys::CMsgIter::new(slice.as_ptr(), slice.len());
133        Self { inner, buffer }
134    }
135
136    /// Append a control message into the buffer.
137    pub fn push<T: AncillaryData>(
138        &mut self,
139        level: i32,
140        ty: i32,
141        value: &T,
142    ) -> Result<(), CodecError> {
143        if !self.inner.is_space_enough(T::SIZE) {
144            return Err(CodecError::BufferTooSmall);
145        }
146
147        // SAFETY: method `new` guarantees the buffer is zeroed and properly
148        // aligned, and we have checked the space.
149        let mut cmsg = unsafe {
150            self.inner
151                .current_mut::<T>(self.buffer.buf_mut_ptr().cast())
152        }
153        .expect("sufficient space");
154        cmsg.set_level(level);
155        cmsg.set_ty(ty);
156        unsafe {
157            self.buffer.advance(cmsg.encode_data(value)?);
158        }
159
160        unsafe { self.inner.next(self.buffer.buf_mut_ptr().cast()) };
161
162        Ok(())
163    }
164}
165
166/// A fixed-size, stack-allocated buffer for ancillary (control) messages.
167///
168/// Properly aligned for the platform's control message header type
169/// (`cmsghdr` on Unix, `CMSGHDR` on Windows), so it can be passed directly
170/// to [`AncillaryIter`] and [`AncillaryBuilder`].
171pub struct AncillaryBuf<const N: usize> {
172    inner: [u8; N],
173    len: usize,
174    _align: [sys::cmsghdr; 0],
175}
176
177impl<const N: usize> AncillaryBuf<N> {
178    /// Create a new zeroed [`AncillaryBuf`].
179    pub fn new() -> Self {
180        Self {
181            inner: [0u8; N],
182            len: 0,
183            _align: [],
184        }
185    }
186
187    /// Create [`AncillaryBuilder`] with this buffer. The buffer will be zeroed
188    /// on creation.
189    ///
190    /// # Panics
191    ///
192    /// This function will panic if this buffer is too short.
193    pub fn builder(&mut self) -> AncillaryBuilder<'_, Self> {
194        AncillaryBuilder::new(self)
195    }
196}
197
198impl<const N: usize> Default for AncillaryBuf<N> {
199    fn default() -> Self {
200        Self::new()
201    }
202}
203
204impl<const N: usize> IoBuf for AncillaryBuf<N> {
205    fn as_init(&self) -> &[u8] {
206        &self.inner[..self.len]
207    }
208}
209
210impl<const N: usize> SetLen for AncillaryBuf<N> {
211    unsafe fn set_len(&mut self, len: usize) {
212        debug_assert!(len <= N);
213        self.len = len;
214    }
215}
216
217impl<const N: usize> IoBufMut for AncillaryBuf<N> {
218    unsafe fn as_uninit(&mut self) -> &mut [MaybeUninit<u8>] {
219        unsafe { std::slice::from_raw_parts_mut(self.inner.as_mut_ptr().cast(), N) }
220    }
221}
222
223impl<const N: usize> Deref for AncillaryBuf<N> {
224    type Target = [u8];
225
226    fn deref(&self) -> &Self::Target {
227        &self.inner[0..self.len]
228    }
229}
230
231impl<const N: usize> DerefMut for AncillaryBuf<N> {
232    fn deref_mut(&mut self) -> &mut Self::Target {
233        &mut self.inner[0..self.len]
234    }
235}
236
237/// Returns the buffer size required to hold one ancillary message carrying a
238/// value of type `T`.
239///
240/// This is the platform-appropriate equivalent of `CMSG_SPACE(T::SIZE)` on
241/// Unix or `WSA_CMSG_SPACE(T::SIZE)` on Windows, and can be used as a const
242/// generic argument for [`AncillaryBuf`].
243pub const fn ancillary_space<T: AncillaryData>() -> usize {
244    // SAFETY: CMSG_SPACE is always safe
245    #[allow(clippy::unnecessary_cast)]
246    unsafe {
247        sys::CMSG_SPACE(T::SIZE as _) as usize
248    }
249}
250
251/// Error that can occur when encoding or decoding ancillary data.
252#[derive(Debug)]
253pub enum CodecError {
254    /// The provided buffer is too small to hold the encoded data.
255    BufferTooSmall,
256    /// Another error occurred during encoding or decoding.
257    Other(Box<dyn std::error::Error + Send + Sync + 'static>),
258}
259
260impl CodecError {
261    /// Create a new [`CodecError::Other`] from any error type.
262    pub fn other(error: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
263        Self::Other(error.into())
264    }
265
266    /// Attempt to downcast the error to a concrete type.
267    ///
268    /// Returns `Some(&T)` if the error is of type `T`, otherwise `None`.
269    pub fn downcast_ref<T: std::error::Error + 'static>(&self) -> Option<&T> {
270        match self {
271            Self::Other(e) => e.downcast_ref(),
272            _ => None,
273        }
274    }
275
276    /// Attempt to downcast the error to a concrete type.
277    ///
278    /// Returns `Some(&mut T)` if the error is of type `T`, otherwise `None`.
279    pub fn downcast_mut<T: std::error::Error + 'static>(&mut self) -> Option<&mut T> {
280        match self {
281            Self::Other(e) => e.downcast_mut(),
282            _ => None,
283        }
284    }
285}
286
287impl std::fmt::Display for CodecError {
288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289        match self {
290            Self::BufferTooSmall => write!(f, "buffer too small for encoding/decoding"),
291            Self::Other(e) => write!(f, "codec error: {}", e),
292        }
293    }
294}
295
296impl std::error::Error for CodecError {
297    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
298        match self {
299            Self::Other(e) => Some(e.as_ref()),
300            _ => None,
301        }
302    }
303}
304
305/// Trait for types that can be encoded and decoded as ancillary data payloads.
306///
307/// This trait enables a type to be used as the data payload in control messages
308/// (ancillary data). Types implementing this trait can be passed to
309/// [`AncillaryBuilder::push`] and retrieved via [`AncillaryRef::data`].
310///
311/// # Built-in Implementations
312///
313/// This trait is implemented for the following platform-specific types:
314///
315/// - Unix: `libc::in_addr`, `libc::in_pktinfo`, `libc::in6_pktinfo`
316/// - Windows: `IN_PKTINFO`, `IN6_PKTINFO`
317///
318/// When the `bytemuck` feature is enabled, this trait is also automatically
319/// implemented for types that implement [`bytemuck_ext::BitwiseAncillaryData`]:
320///
321/// - Primitive types: `()`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `i8`,
322///   `i16`, `i32`, `i64`, `i128`, `isize`, `f32`, `f64`
323/// - Fixed-size arrays of the above types (up to size 512)
324///
325/// For custom types with the `bytemuck` feature enabled, you can implement
326/// [`bytemuck_ext::BitwiseAncillaryData`] to automatically get
327/// [`AncillaryData`] (see [`bytemuck_ext`] for details). Otherwise, you must
328/// manually implement this trait with custom encoding/decoding logic.
329///
330/// # Example
331///
332/// ```
333/// use std::mem::MaybeUninit;
334///
335/// use compio_io::ancillary::{AncillaryData, CodecError};
336///
337/// struct MyData {
338///     value: u32,
339/// }
340///
341/// impl AncillaryData for MyData {
342///     const SIZE: usize = std::mem::size_of::<u32>();
343///
344///     fn encode(&self, buffer: &mut [MaybeUninit<u8>]) -> Result<(), CodecError> {
345///         if buffer.len() < Self::SIZE {
346///             return Err(CodecError::BufferTooSmall);
347///         }
348///         let bytes = self.value.to_ne_bytes();
349///         for (i, &byte) in bytes.iter().enumerate() {
350///             buffer[i] = MaybeUninit::new(byte);
351///         }
352///         Ok(())
353///     }
354///
355///     fn decode(buffer: &[u8]) -> Result<Self, CodecError> {
356///         if buffer.len() < Self::SIZE {
357///             return Err(CodecError::BufferTooSmall);
358///         }
359///         let mut bytes = [0u8; 4];
360///         bytes.copy_from_slice(&buffer[..4]);
361///         Ok(MyData {
362///             value: u32::from_ne_bytes(bytes),
363///         })
364///     }
365/// }
366/// ```
367pub trait AncillaryData: Sized {
368    /// The size in bytes of the encoded representation.
369    ///
370    /// This defaults to `std::mem::size_of::<Self>()` but can be overridden
371    /// for types with custom encoding.
372    const SIZE: usize = std::mem::size_of::<Self>();
373
374    /// Encode this value into the provided buffer.
375    ///
376    /// # Errors
377    ///
378    /// Returns [`CodecError::BufferTooSmall`] if the buffer is too small to
379    /// hold the encoded data, or [`CodecError::Other`] for other encoding
380    /// errors.
381    fn encode(&self, buffer: &mut [MaybeUninit<u8>]) -> Result<(), CodecError>;
382
383    /// Decode a value from the provided buffer.
384    ///
385    /// # Errors
386    ///
387    /// Returns [`CodecError::BufferTooSmall`] if the buffer is too small,
388    /// or [`CodecError::Other`] for other decoding errors.
389    fn decode(buffer: &[u8]) -> Result<Self, CodecError>;
390}
391
392unsafe fn copy_to_bytes<T: AncillaryData>(
393    src: &T,
394    dest: &mut [MaybeUninit<u8>],
395) -> Result<(), CodecError> {
396    if dest.len() < T::SIZE {
397        return Err(CodecError::BufferTooSmall);
398    }
399    unsafe {
400        ptr::copy_nonoverlapping::<u8>(src as *const T as _, dest.as_mut_ptr() as _, T::SIZE);
401    }
402    Ok(())
403}
404
405unsafe fn copy_from_bytes<T: AncillaryData>(src: &[u8]) -> Result<T, CodecError> {
406    if src.len() < T::SIZE {
407        return Err(CodecError::BufferTooSmall);
408    }
409    let src_ptr = src.as_ptr() as *const T;
410    unsafe { Ok(ptr::read_unaligned(src_ptr)) }
411}