//! [`recvmsg`], [`sendmsg`], and related functions. #![allow(unsafe_code)] use crate::backend::{self, c}; use crate::fd::{AsFd, BorrowedFd, OwnedFd}; use crate::io::{self, IoSlice, IoSliceMut}; #[cfg(linux_kernel)] use crate::net::UCred; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem::{align_of, size_of, size_of_val, take}; #[cfg(linux_kernel)] use core::ptr::addr_of; use core::{ptr, slice}; use super::{RecvFlags, SendFlags, SocketAddrAny, SocketAddrV4, SocketAddrV6}; /// Macro for defining the amount of space to allocate in a buffer for use with /// [`RecvAncillaryBuffer::new`] and [`SendAncillaryBuffer::new`]. /// /// # Examples /// /// Allocate a buffer for a single file descriptor: /// ``` /// # use rustix::cmsg_space; /// let mut space = [0; rustix::cmsg_space!(ScmRights(1))]; /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))]; /// # } /// ``` /// /// Allocate a buffer for two file descriptors and credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// # } /// ``` #[macro_export] macro_rules! cmsg_space { // Base Rules (ScmRights($len:expr)) => { $crate::net::__cmsg_space( $len * ::core::mem::size_of::<$crate::fd::BorrowedFd<'static>>(), ) }; (ScmCredentials($len:expr)) => { $crate::net::__cmsg_space( $len * ::core::mem::size_of::<$crate::net::UCred>(), ) }; // Combo Rules ($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{ // We only have to add `cmsghdr` alignment once; all other times we can // use `cmsg_aligned_space`. let sum = $crate::cmsg_space!($firstid($firstex)); $( let sum = sum + $crate::cmsg_aligned_space!($restid($restex)); )* sum }}; } /// Like `cmsg_space`, but doesn't add padding for `cmsghdr` alignment. #[doc(hidden)] #[macro_export] macro_rules! cmsg_aligned_space { // Base Rules (ScmRights($len:expr)) => { $crate::net::__cmsg_aligned_space( $len * ::core::mem::size_of::<$crate::fd::BorrowedFd<'static>>(), ) }; (ScmCredentials($len:expr)) => { $crate::net::__cmsg_aligned_space( $len * ::core::mem::size_of::<$crate::net::UCred>(), ) }; // Combo Rules ($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{ let sum = cmsg_aligned_space!($firstid($firstex)); $( let sum = sum + cmsg_aligned_space!($restid($restex)); )* sum }}; } #[doc(hidden)] pub const fn __cmsg_space(len: usize) -> usize { // Add `align_of::()` so that we can align the user-provided // `&[u8]` to the required alignment boundary. let len = len + align_of::(); __cmsg_aligned_space(len) } #[doc(hidden)] pub const fn __cmsg_aligned_space(len: usize) -> usize { // Convert `len` to `u32` for `CMSG_SPACE`. This would be `try_into()` if // we could call that in a `const fn`. let converted_len = len as u32; if converted_len as usize != len { unreachable!(); // `CMSG_SPACE` size overflow } unsafe { c::CMSG_SPACE(converted_len) as usize } } /// Ancillary message for [`sendmsg`], [`sendmsg_v4`], [`sendmsg_v6`], /// [`sendmsg_unix`], and [`sendmsg_any`]. #[non_exhaustive] pub enum SendAncillaryMessage<'slice, 'fd> { /// Send file descriptors. #[doc(alias = "SCM_RIGHTS")] ScmRights(&'slice [BorrowedFd<'fd>]), /// Send process credentials. #[cfg(linux_kernel)] #[doc(alias = "SCM_CREDENTIAL")] ScmCredentials(UCred), } impl SendAncillaryMessage<'_, '_> { /// Get the maximum size of an ancillary message. /// /// This can be helpful in determining the size of the buffer you allocate. pub const fn size(&self) -> usize { match self { Self::ScmRights(slice) => cmsg_space!(ScmRights(slice.len())), #[cfg(linux_kernel)] Self::ScmCredentials(_) => cmsg_space!(ScmCredentials(1)), } } } /// Ancillary message for [`recvmsg`]. #[non_exhaustive] pub enum RecvAncillaryMessage<'a> { /// Received file descriptors. #[doc(alias = "SCM_RIGHTS")] ScmRights(AncillaryIter<'a, OwnedFd>), /// Received process credentials. #[cfg(linux_kernel)] #[doc(alias = "SCM_CREDENTIALS")] ScmCredentials(UCred), } /// Buffer for sending ancillary messages with [`sendmsg`], [`sendmsg_v4`], /// [`sendmsg_v6`], [`sendmsg_unix`], and [`sendmsg_any`]. /// /// Use the [`push`] function to add messages to send. /// /// [`push`]: SendAncillaryBuffer::push pub struct SendAncillaryBuffer<'buf, 'slice, 'fd> { /// Raw byte buffer for messages. buffer: &'buf mut [u8], /// The amount of the buffer that is used. length: usize, /// Phantom data for lifetime of `&'slice [BorrowedFd<'fd>]`. _phantom: PhantomData<&'slice [BorrowedFd<'fd>]>, } impl<'buf> From<&'buf mut [u8]> for SendAncillaryBuffer<'buf, '_, '_> { fn from(buffer: &'buf mut [u8]) -> Self { Self::new(buffer) } } impl Default for SendAncillaryBuffer<'_, '_, '_> { fn default() -> Self { Self { buffer: &mut [], length: 0, _phantom: PhantomData, } } } impl<'buf, 'slice, 'fd> SendAncillaryBuffer<'buf, 'slice, 'fd> { /// Create a new, empty `SendAncillaryBuffer` from a raw byte buffer. /// /// The buffer size may be computed with [`cmsg_space`], or it may be /// zero for an empty buffer, however in that case, consider `default()` /// instead, or even using [`send`] instead of `sendmsg`. /// /// # Examples /// /// Allocate a buffer for a single file descriptor: /// ``` /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmRights(1))]; /// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))]; /// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// Allocate a buffer for two file descriptors and credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// [`send`]: crate::net::send #[inline] pub fn new(buffer: &'buf mut [u8]) -> Self { Self { buffer: align_for_cmsghdr(buffer), length: 0, _phantom: PhantomData, } } /// Returns a pointer to the message data. pub(crate) fn as_control_ptr(&mut self) -> *mut u8 { // When the length is zero, we may be using a `&[]` address, which may // be an invalid but non-null pointer, and on some platforms, that // causes `sendmsg` to fail with `EFAULT` or `EINVAL` #[cfg(not(linux_kernel))] if self.length == 0 { return core::ptr::null_mut(); } self.buffer.as_mut_ptr() } /// Returns the length of the message data. pub(crate) fn control_len(&self) -> usize { self.length } /// Delete all messages from the buffer. pub fn clear(&mut self) { self.length = 0; } /// Add an ancillary message to the buffer. /// /// Returns `true` if the message was added successfully. pub fn push(&mut self, msg: SendAncillaryMessage<'slice, 'fd>) -> bool { match msg { SendAncillaryMessage::ScmRights(fds) => { let fds_bytes = unsafe { slice::from_raw_parts(fds.as_ptr().cast::(), size_of_val(fds)) }; self.push_ancillary(fds_bytes, c::SOL_SOCKET as _, c::SCM_RIGHTS as _) } #[cfg(linux_kernel)] SendAncillaryMessage::ScmCredentials(ucred) => { let ucred_bytes = unsafe { slice::from_raw_parts(addr_of!(ucred).cast::(), size_of_val(&ucred)) }; self.push_ancillary(ucred_bytes, c::SOL_SOCKET as _, c::SCM_CREDENTIALS as _) } } } /// Pushes an ancillary message to the buffer. fn push_ancillary(&mut self, source: &[u8], cmsg_level: c::c_int, cmsg_type: c::c_int) -> bool { macro_rules! leap { ($e:expr) => {{ match ($e) { Some(x) => x, None => return false, } }}; } // Calculate the length of the message. let source_len = leap!(u32::try_from(source.len()).ok()); // Calculate the new length of the buffer. let additional_space = unsafe { c::CMSG_SPACE(source_len) }; let new_length = leap!(self.length.checked_add(additional_space as usize)); let buffer = leap!(self.buffer.get_mut(..new_length)); // Fill the new part of the buffer with zeroes. buffer[self.length..new_length].fill(0); self.length = new_length; // Get the last header in the buffer. let last_header = leap!(messages::Messages::new(buffer).last()); // Set the header fields. last_header.cmsg_len = unsafe { c::CMSG_LEN(source_len) } as _; last_header.cmsg_level = cmsg_level; last_header.cmsg_type = cmsg_type; // Get the pointer to the payload and copy the data. unsafe { let payload = c::CMSG_DATA(last_header); ptr::copy_nonoverlapping(source.as_ptr(), payload, source_len as _); } true } } impl<'slice, 'fd> Extend> for SendAncillaryBuffer<'_, 'slice, 'fd> { fn extend>>(&mut self, iter: T) { // TODO: This could be optimized to add every message in one go. iter.into_iter().all(|msg| self.push(msg)); } } /// Buffer for receiving ancillary messages with [`recvmsg`]. /// /// Use the [`drain`] function to iterate over the received messages. /// /// [`drain`]: RecvAncillaryBuffer::drain #[derive(Default)] pub struct RecvAncillaryBuffer<'buf> { /// Raw byte buffer for messages. buffer: &'buf mut [u8], /// The portion of the buffer we've read from already. read: usize, /// The amount of the buffer that is used. length: usize, } impl<'buf> From<&'buf mut [u8]> for RecvAncillaryBuffer<'buf> { fn from(buffer: &'buf mut [u8]) -> Self { Self::new(buffer) } } impl<'buf> RecvAncillaryBuffer<'buf> { /// Create a new, empty `RecvAncillaryBuffer` from a raw byte buffer. /// /// The buffer size may be computed with [`cmsg_space`], or it may be /// zero for an empty buffer, however in that case, consider `default()` /// instead, or even using [`recv`] instead of `recvmsg`. /// /// # Examples /// /// Allocate a buffer for a single file descriptor: /// ``` /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmRights(1))]; /// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space); /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmCredentials(1))]; /// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// Allocate a buffer for two file descriptors and credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [0; rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// [`recv`]: crate::net::recv #[inline] pub fn new(buffer: &'buf mut [u8]) -> Self { Self { buffer: align_for_cmsghdr(buffer), read: 0, length: 0, } } /// Returns a pointer to the message data. pub(crate) fn as_control_ptr(&mut self) -> *mut u8 { // When the length is zero, we may be using a `&[]` address, which may // be an invalid but non-null pointer, and on some platforms, that // causes `sendmsg` to fail with `EFAULT` or `EINVAL` #[cfg(not(linux_kernel))] if self.buffer.is_empty() { return core::ptr::null_mut(); } self.buffer.as_mut_ptr() } /// Returns the length of the message data. pub(crate) fn control_len(&self) -> usize { self.buffer.len() } /// Set the length of the message data. /// /// # Safety /// /// The buffer must be filled with valid message data. pub(crate) unsafe fn set_control_len(&mut self, len: usize) { self.length = len; self.read = 0; } /// Delete all messages from the buffer. pub(crate) fn clear(&mut self) { self.drain().for_each(drop); } /// Drain all messages from the buffer. pub fn drain(&mut self) -> AncillaryDrain<'_> { AncillaryDrain { messages: messages::Messages::new(&mut self.buffer[self.read..][..self.length]), read: &mut self.read, length: &mut self.length, } } } impl Drop for RecvAncillaryBuffer<'_> { fn drop(&mut self) { self.clear(); } } /// Return a slice of `buffer` starting at the first `cmsghdr` alignment /// boundary. #[inline] fn align_for_cmsghdr(buffer: &mut [u8]) -> &mut [u8] { // If the buffer is empty, we won't be writing anything into it, so it // doesn't need to be aligned. if buffer.is_empty() { return buffer; } let align = align_of::(); let addr = buffer.as_ptr() as usize; let adjusted = (addr + (align - 1)) & align.wrapping_neg(); &mut buffer[adjusted - addr..] } /// An iterator that drains messages from a [`RecvAncillaryBuffer`]. pub struct AncillaryDrain<'buf> { /// Inner iterator over messages. messages: messages::Messages<'buf>, /// Increment the number of messages we've read. read: &'buf mut usize, /// Decrement the total length. length: &'buf mut usize, } impl<'buf> AncillaryDrain<'buf> { /// A closure that converts a message into a [`RecvAncillaryMessage`]. fn cvt_msg( read: &mut usize, length: &mut usize, msg: &c::cmsghdr, ) -> Option> { unsafe { // Advance the `read` pointer. let msg_len = msg.cmsg_len as usize; *read += msg_len; *length -= msg_len; // Get a pointer to the payload. let payload = c::CMSG_DATA(msg); let payload_len = msg.cmsg_len as usize - c::CMSG_LEN(0) as usize; // Get a mutable slice of the payload. let payload: &'buf mut [u8] = slice::from_raw_parts_mut(payload, payload_len); // Determine what type it is. let (level, msg_type) = (msg.cmsg_level, msg.cmsg_type); match (level as _, msg_type as _) { (c::SOL_SOCKET, c::SCM_RIGHTS) => { // Create an iterator that reads out the file descriptors. let fds = AncillaryIter::new(payload); Some(RecvAncillaryMessage::ScmRights(fds)) } #[cfg(linux_kernel)] (c::SOL_SOCKET, c::SCM_CREDENTIALS) => { if payload_len >= size_of::() { let ucred = payload.as_ptr().cast::().read_unaligned(); Some(RecvAncillaryMessage::ScmCredentials(ucred)) } else { None } } _ => None, } } } } impl<'buf> Iterator for AncillaryDrain<'buf> { type Item = RecvAncillaryMessage<'buf>; fn next(&mut self) -> Option { let read = &mut self.read; let length = &mut self.length; self.messages.find_map(|ev| Self::cvt_msg(read, length, ev)) } fn size_hint(&self) -> (usize, Option) { let (_, max) = self.messages.size_hint(); (0, max) } fn fold(self, init: B, f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { let read = self.read; let length = self.length; self.messages .filter_map(|ev| Self::cvt_msg(read, length, ev)) .fold(init, f) } fn count(self) -> usize { let read = self.read; let length = self.length; self.messages .filter_map(|ev| Self::cvt_msg(read, length, ev)) .count() } fn last(self) -> Option where Self: Sized, { let read = self.read; let length = self.length; self.messages .filter_map(|ev| Self::cvt_msg(read, length, ev)) .last() } fn collect>(self) -> B where Self: Sized, { let read = self.read; let length = self.length; self.messages .filter_map(|ev| Self::cvt_msg(read, length, ev)) .collect() } } impl FusedIterator for AncillaryDrain<'_> {} /// `sendmsg(msghdr)`—Sends a message on a socket. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/sendmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sendmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=sendmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/sendmsg.2 /// [OpenBSD]: https://man.openbsd.org/sendmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sendmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/sendmsg #[inline] pub fn sendmsg( socket: impl AsFd, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, flags: SendFlags, ) -> io::Result { backend::net::syscalls::sendmsg(socket.as_fd(), iov, control, flags) } /// `sendmsg(msghdr)`—Sends a message on a socket to a specific IPv4 address. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/sendmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sendmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=sendmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/sendmsg.2 /// [OpenBSD]: https://man.openbsd.org/sendmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sendmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/sendmsg #[inline] pub fn sendmsg_v4( socket: impl AsFd, addr: &SocketAddrV4, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, flags: SendFlags, ) -> io::Result { backend::net::syscalls::sendmsg_v4(socket.as_fd(), addr, iov, control, flags) } /// `sendmsg(msghdr)`—Sends a message on a socket to a specific IPv6 address. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/sendmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sendmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=sendmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/sendmsg.2 /// [OpenBSD]: https://man.openbsd.org/sendmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sendmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/sendmsg #[inline] pub fn sendmsg_v6( socket: impl AsFd, addr: &SocketAddrV6, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, flags: SendFlags, ) -> io::Result { backend::net::syscalls::sendmsg_v6(socket.as_fd(), addr, iov, control, flags) } /// `sendmsg(msghdr)`—Sends a message on a socket to a specific Unix-domain /// address. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/sendmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sendmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=sendmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/sendmsg.2 /// [OpenBSD]: https://man.openbsd.org/sendmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sendmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/sendmsg #[inline] #[cfg(unix)] pub fn sendmsg_unix( socket: impl AsFd, addr: &super::SocketAddrUnix, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, flags: SendFlags, ) -> io::Result { backend::net::syscalls::sendmsg_unix(socket.as_fd(), addr, iov, control, flags) } /// `sendmsg(msghdr)`—Sends a message on a socket to a specific address. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/sendmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sendmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=sendmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/sendmsg.2 /// [OpenBSD]: https://man.openbsd.org/sendmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=sendmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/sendmsg #[inline] pub fn sendmsg_any( socket: impl AsFd, addr: Option<&SocketAddrAny>, iov: &[IoSlice<'_>], control: &mut SendAncillaryBuffer<'_, '_, '_>, flags: SendFlags, ) -> io::Result { match addr { None => backend::net::syscalls::sendmsg(socket.as_fd(), iov, control, flags), Some(SocketAddrAny::V4(addr)) => { backend::net::syscalls::sendmsg_v4(socket.as_fd(), addr, iov, control, flags) } Some(SocketAddrAny::V6(addr)) => { backend::net::syscalls::sendmsg_v6(socket.as_fd(), addr, iov, control, flags) } #[cfg(unix)] Some(SocketAddrAny::Unix(addr)) => { backend::net::syscalls::sendmsg_unix(socket.as_fd(), addr, iov, control, flags) } } } /// `recvmsg(msghdr)`—Receives a message from a socket. /// /// # References /// - [POSIX] /// - [Linux] /// - [Apple] /// - [FreeBSD] /// - [NetBSD] /// - [OpenBSD] /// - [DragonFly BSD] /// - [illumos] /// /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html /// [Linux]: https://man7.org/linux/man-pages/man2/recvmsg.2.html /// [Apple]: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/recvmsg.2.html /// [FreeBSD]: https://man.freebsd.org/cgi/man.cgi?query=recvmsg&sektion=2 /// [NetBSD]: https://man.netbsd.org/recvmsg.2 /// [OpenBSD]: https://man.openbsd.org/recvmsg.2 /// [DragonFly BSD]: https://man.dragonflybsd.org/?command=recvmsg§ion=2 /// [illumos]: https://illumos.org/man/3SOCKET/recvmsg #[inline] pub fn recvmsg( socket: impl AsFd, iov: &mut [IoSliceMut<'_>], control: &mut RecvAncillaryBuffer<'_>, flags: RecvFlags, ) -> io::Result { backend::net::syscalls::recvmsg(socket.as_fd(), iov, control, flags) } /// The result of a successful [`recvmsg`] call. pub struct RecvMsgReturn { /// The number of bytes received. pub bytes: usize, /// The flags received. pub flags: RecvFlags, /// The address of the socket we received from, if any. pub address: Option, } /// An iterator over data in an ancillary buffer. pub struct AncillaryIter<'data, T> { /// The data we're iterating over. data: &'data mut [u8], /// The raw data we're removing. _marker: PhantomData, } impl<'data, T> AncillaryIter<'data, T> { /// Create a new iterator over data in an ancillary buffer. /// /// # Safety /// /// The buffer must contain valid ancillary data. unsafe fn new(data: &'data mut [u8]) -> Self { assert_eq!(data.len() % size_of::(), 0); Self { data, _marker: PhantomData, } } } impl<'data, T> Drop for AncillaryIter<'data, T> { fn drop(&mut self) { self.for_each(drop); } } impl Iterator for AncillaryIter<'_, T> { type Item = T; fn next(&mut self) -> Option { // See if there is a next item. if self.data.len() < size_of::() { return None; } // Get the next item. let item = unsafe { self.data.as_ptr().cast::().read_unaligned() }; // Move forward. let data = take(&mut self.data); self.data = &mut data[size_of::()..]; Some(item) } fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } fn count(self) -> usize { self.len() } fn last(mut self) -> Option { self.next_back() } } impl FusedIterator for AncillaryIter<'_, T> {} impl ExactSizeIterator for AncillaryIter<'_, T> { fn len(&self) -> usize { self.data.len() / size_of::() } } impl DoubleEndedIterator for AncillaryIter<'_, T> { fn next_back(&mut self) -> Option { // See if there is a next item. if self.data.len() < size_of::() { return None; } // Get the next item. let item = unsafe { let ptr = self.data.as_ptr().add(self.data.len() - size_of::()); ptr.cast::().read_unaligned() }; // Move forward. let len = self.data.len(); let data = take(&mut self.data); self.data = &mut data[..len - size_of::()]; Some(item) } } mod messages { use crate::backend::c; use crate::backend::net::msghdr; use core::iter::FusedIterator; use core::marker::PhantomData; use core::ptr::NonNull; /// An iterator over the messages in an ancillary buffer. pub(super) struct Messages<'buf> { /// The message header we're using to iterate over the messages. msghdr: c::msghdr, /// The current pointer to the next message header to return. /// /// This has a lifetime of `'buf`. header: Option>, /// Capture the original lifetime of the buffer. _buffer: PhantomData<&'buf mut [u8]>, } impl<'buf> Messages<'buf> { /// Create a new iterator over messages from a byte buffer. pub(super) fn new(buf: &'buf mut [u8]) -> Self { let msghdr = { let mut h = msghdr::zero_msghdr(); h.msg_control = buf.as_mut_ptr().cast(); h.msg_controllen = buf.len().try_into().expect("buffer too large for msghdr"); h }; // Get the first header. let header = NonNull::new(unsafe { c::CMSG_FIRSTHDR(&msghdr) }); Self { msghdr, header, _buffer: PhantomData, } } } impl<'a> Iterator for Messages<'a> { type Item = &'a mut c::cmsghdr; #[inline] fn next(&mut self) -> Option { // Get the current header. let header = self.header?; // Get the next header. self.header = NonNull::new(unsafe { c::CMSG_NXTHDR(&self.msghdr, header.as_ptr()) }); // If the headers are equal, we're done. if Some(header) == self.header { self.header = None; } // SAFETY: The lifetime of `header` is tied to this. Some(unsafe { &mut *header.as_ptr() }) } fn size_hint(&self) -> (usize, Option) { if self.header.is_some() { // The remaining buffer *could* be filled with zero-length // messages. let max_size = unsafe { c::CMSG_LEN(0) } as usize; let remaining_count = self.msghdr.msg_controllen as usize / max_size; (1, Some(remaining_count)) } else { (0, Some(0)) } } } impl FusedIterator for Messages<'_> {} }