Skip to main content

dope_core/io/socket/msg/
mod.rs

1use core::{marker, ptr};
2
3use crate::{
4    backend,
5    io::{socket, transfer},
6};
7
8pub mod raw;
9
10/// Maximum number of scatter/gather entries accepted by one socket message.
11pub const MAX_IOVECS: usize = <backend::Backend as backend::Socket>::MAX_IOVECS;
12
13/// Address-stable storage for one send iovec.
14/// Values carry no borrow by themselves; safe pointer installation goes
15/// through [`Builder`] and [`Parts`].
16#[derive(Clone, Copy, Debug)]
17#[repr(transparent)]
18pub struct Iovec {
19    raw: libc::iovec,
20}
21
22impl Iovec {
23    #[must_use]
24    pub const fn empty() -> Self {
25        Self {
26            raw: libc::iovec {
27                iov_base: ptr::null_mut(),
28                iov_len: 0,
29            },
30        }
31    }
32
33    fn from_slice(buf: &[u8]) -> Self {
34        Self {
35            raw: libc::iovec {
36                iov_base: buf.as_ptr().cast_mut().cast(),
37                iov_len: buf.len(),
38            },
39        }
40    }
41
42    #[must_use]
43    pub fn len(&self) -> usize {
44        self.raw.iov_len
45    }
46
47    #[must_use]
48    pub fn is_empty(&self) -> bool {
49        self.raw.iov_len == 0
50    }
51}
52
53/// Address-stable storage for a kernel `msghdr`.
54/// The storage is deliberately not `Copy`: a configured header is the root of
55/// a raw pointer graph and must be frozen through [`Message`].
56#[derive(Debug)]
57#[repr(transparent)]
58pub struct Header {
59    raw: libc::msghdr,
60}
61
62impl Header {
63    #[must_use]
64    pub const fn new() -> Self {
65        Self {
66            raw: libc::msghdr {
67                msg_name: ptr::null_mut(),
68                msg_namelen: 0,
69                msg_iov: ptr::null_mut(),
70                msg_iovlen: 0,
71                msg_control: ptr::null_mut(),
72                msg_controllen: 0,
73                msg_flags: 0,
74            },
75        }
76    }
77
78    fn bind_iovs(&mut self, iovs: &[Iovec]) {
79        self.raw.msg_iov = if iovs.is_empty() {
80            ptr::null_mut()
81        } else {
82            iovs.as_ptr().cast_mut().cast()
83        };
84        self.raw.msg_iovlen = iovs.len() as _;
85    }
86}
87
88impl Default for Header {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94/// A complete immutable send pointer graph.
95#[derive(Clone, Copy)]
96#[repr(transparent)]
97pub struct Message<'a> {
98    header: *const libc::msghdr,
99    graph: marker::PhantomData<&'a Header>,
100}
101
102impl Message<'_> {
103    pub(crate) fn raw(self) -> *const libc::msghdr {
104        self.header
105    }
106}
107
108/// A send message together with the iovecs whose bytes it proves live.
109pub struct Vectored<'a> {
110    message: Message<'a>,
111    iovs: &'a [Iovec],
112    bytes: transfer::Len,
113}
114
115impl<'a> Vectored<'a> {
116    #[must_use]
117    pub fn message(&self) -> Message<'a> {
118        self.message
119    }
120
121    pub fn iter(&self) -> impl Iterator<Item = &'a [u8]> + '_ {
122        let iovs: &'a [Iovec] = self.iovs;
123        iovs.iter().map(raw::Project::slice)
124    }
125
126    #[must_use]
127    pub fn bytes(&self) -> usize {
128        self.bytes.into_usize()
129    }
130
131    #[must_use]
132    pub fn is_empty(&self) -> bool {
133        self.bytes == transfer::Len::ZERO
134    }
135}
136
137impl<'a> Vectored<'a> {
138    fn from_parts(header: &'a mut Header, iovs: &'a [Iovec], bytes: transfer::Len) -> Self {
139        Self {
140            message: Message {
141                header: ptr::from_ref(&header.raw),
142                graph: marker::PhantomData,
143            },
144            iovs,
145            bytes,
146        }
147    }
148}
149
150/// A fixed number of byte slices with their exact bounded total.
151pub struct Parts<'a, const N: usize> {
152    slices: [&'a [u8]; N],
153    bytes: transfer::Len,
154}
155
156impl<'a, const N: usize> Parts<'a, N> {
157    pub fn try_new(slices: [&'a [u8]; N]) -> Option<Self> {
158        if N > MAX_IOVECS {
159            return None;
160        }
161        let mut bytes = transfer::Len::ZERO;
162        for slice in &slices {
163            bytes = bytes.checked_add(slice.len())?;
164        }
165        Some(Self { slices, bytes })
166    }
167}
168
169impl<'a> Parts<'a, 1> {
170    #[must_use]
171    pub fn single(part: raw::Part<'a>) -> Self {
172        let (slice, bytes) = part.into_parts();
173        Self {
174            slices: [slice],
175            bytes,
176        }
177    }
178}
179
180impl<'a> Parts<'a, 2> {
181    #[must_use]
182    pub fn prefixes(limit: transfer::Len, sources: [&'a [u8]; 2]) -> Self {
183        let first_len = sources[0].len().min(limit.into_usize());
184        let second_len = sources[1].len().min(limit.into_usize() - first_len);
185        let slices = [&sources[0][..first_len], &sources[1][..second_len]];
186        Self {
187            slices,
188            bytes: transfer::Len::from_bounded(first_len + second_len),
189        }
190    }
191}
192
193/// Configures one send header while retaining every installed resource for `'a`.
194///
195/// Payloads cannot be released while a finished message can still reach the
196/// kernel:
197///
198/// ```compile_fail
199/// use dope_core::io::socket::msg::{Header, Iovec, Parts, Builder};
200///
201/// let mut header = Header::new();
202/// let mut iovs = [Iovec::empty()];
203/// let message = {
204///     let payload = vec![1_u8, 2, 3];
205///     let parts = Parts::try_new([payload.as_slice()]).expect("three bytes fit");
206///     Builder::new(&mut header).finish(&mut iovs, parts)
207/// };
208/// drop(message);
209/// ```
210pub struct Builder<'a> {
211    header: &'a mut Header,
212    graph: marker::PhantomData<&'a Header>,
213}
214
215impl<'a> Builder<'a> {
216    pub fn new(header: &'a mut Header) -> Self {
217        *header = Header::new();
218        Self {
219            header,
220            graph: marker::PhantomData,
221        }
222    }
223
224    pub fn name(&mut self, addr: &'a socket::raw::Inet) {
225        self.header.raw.msg_name = addr.ptr().cast_mut().cast();
226        self.header.raw.msg_namelen = addr.socklen();
227    }
228
229    /// Installs ancillary bytes and keeps them borrowed with the whole graph.
230    #[doc(hidden)]
231    pub fn control(&mut self, control: &'a [u8]) {
232        self.header.raw.msg_control = control.as_ptr().cast_mut().cast();
233        self.header.raw.msg_controllen = control.len() as _;
234    }
235
236    #[must_use]
237    pub fn finish<const N: usize>(
238        self,
239        iovs: &'a mut [Iovec; N],
240        parts: Parts<'a, N>,
241    ) -> Vectored<'a> {
242        let Parts { slices, bytes } = parts;
243        *iovs = slices.map(Iovec::from_slice);
244        let iovs: &'a [Iovec] = iovs;
245        self.header.bind_iovs(iovs);
246        Vectored {
247            message: Message {
248                header: ptr::from_ref(&self.header.raw),
249                graph: self.graph,
250            },
251            iovs,
252            bytes,
253        }
254    }
255}
256
257const _: () = assert!(size_of::<Iovec>() == size_of::<libc::iovec>());
258const _: () = assert!(align_of::<Iovec>() == align_of::<libc::iovec>());
259const _: () = assert!(size_of::<Header>() == size_of::<libc::msghdr>());
260const _: () = assert!(align_of::<Header>() == align_of::<libc::msghdr>());
261const _: () = assert!(size_of::<Message<'static>>() == size_of::<*const libc::msghdr>());
262const _: () = assert!(MAX_IOVECS >= 2);