/// 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 std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))]; /// # let _: &[MaybeUninit<u8>] = space.as_slice(); /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmCredentials(1))]; /// # let _: &[MaybeUninit<u8>] = space.as_slice(); /// # } /// ``` /// /// Allocate a buffer for two file descriptors and credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// # let _: &[MaybeUninit<u8>] = space.as_slice(); /// # } /// ``` #[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>(),
)
};
(TxTime($len:expr)) => {
$crate::net::__cmsg_space(
$len * ::core::mem::size_of::<::core::primitive::u64>(),
)
};
// 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
}};
}
// Combo Rules
($firstid:ident($firstex:expr), $($restid:ident($restex:expr)),*) => {{ let sum = $crate::cmsg_aligned_space!($firstid($firstex));
$( let sum = sum + $crate::cmsg_aligned_space!($restid($restex));
)*
sum
}};
}
/// Helper function for [`cmsg_space`]. #[doc(hidden)] pubconstfn __cmsg_space(len: usize) -> usize { // Add `align_of::<c::cmsghdr>()` so that we can align the user-provided // `&[u8]` to the required alignment boundary. let len = len + align_of::<c::cmsghdr>();
__cmsg_aligned_space(len)
}
/// Helper function for [`cmsg_aligned_space`]. #[doc(hidden)] pubconstfn __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`] and [`sendmsg_addr`]. #[non_exhaustive] pubenum 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), /// Transmission time, in nanoseconds. The value will be interpreted by /// whichever clock was configured on the socket with [`set_txtime`]. /// /// [`set_txtime`]: crate::net::sockopt::set_txtime #[cfg(target_os = "linux")] #[doc(alias = "SCM_TXTIME")]
TxTime(u64),
}
impl SendAncillaryMessage<'_, '_> { /// Get the maximum size of an ancillary message. /// /// This can be used to determine the size of the buffer to allocate for a /// [`SendAncillaryBuffer::new`] with one message. pubconstfn size(&self) -> usize { matchself { Self::ScmRights(slice) => cmsg_space!(ScmRights(slice.len())), #[cfg(linux_kernel)] Self::ScmCredentials(_) => cmsg_space!(ScmCredentials(1)), #[cfg(target_os = "linux")] Self::TxTime(_) => cmsg_space!(TxTime(1)),
}
}
}
/// Ancillary message for [`recvmsg`]. #[non_exhaustive] pubenum 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`] and /// [`sendmsg_addr`]. /// /// Use the [`push`] function to add messages to send. /// /// [`push`]: SendAncillaryBuffer::push pubstruct SendAncillaryBuffer<'buf, 'slice, 'fd> { /// Raw byte buffer for messages.
buffer: &'buf mut [MaybeUninit<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, '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 std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))]; /// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); 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 std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::SendAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// let mut cmsg_buffer = SendAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// [`send`]: crate::net::send #[inline] pubfn new(buffer: &'buf mut [MaybeUninit<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(&mutself) -> *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))] ifself.length == 0 { return core::ptr::null_mut();
}
self.buffer.as_mut_ptr().cast()
}
/// Returns the length of the message data. pub(crate) fn control_len(&self) -> usize { self.length
}
/// Delete all messages from the buffer. pubfn clear(&mutself) { self.length = 0;
}
/// Add an ancillary message to the buffer. /// /// Returns `true` if the message was added successfully. pubfn push(&mutself, msg: SendAncillaryMessage<'slice, 'fd>) -> bool { match msg {
SendAncillaryMessage::ScmRights(fds) => { let fds_bytes = unsafe { slice::from_raw_parts(fds.as_ptr().cast::<u8>(), 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::<u8>(), size_of_val(&ucred))
}; self.push_ancillary(ucred_bytes, c::SOL_SOCKET as _, c::SCM_CREDENTIALS as _)
} #[cfg(target_os = "linux")]
SendAncillaryMessage::TxTime(tx_time) => { let tx_time_bytes = unsafe {
slice::from_raw_parts(addr_of!(tx_time).cast::<u8>(), size_of_val(&tx_time))
}; self.push_ancillary(tx_time_bytes, c::SOL_SOCKET as _, c::SO_TXTIME as _)
}
}
}
/// Pushes an ancillary message to the buffer. fn push_ancillary(&mutself, source: &[u8], cmsg_level: c::c_int, cmsg_type: c::c_int) -> bool {
macro_rules! leap {
($e:expr) => {{ match ($e) {
Some(x) => x,
None => returnfalse,
}
}};
}
// 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(MaybeUninit::new(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 usize);
}
true
}
}
impl<'slice, 'fd> Extend<SendAncillaryMessage<'slice, 'fd>> for SendAncillaryBuffer<'_, 'slice, 'fd>
{ fn extend<T: IntoIterator<Item = SendAncillaryMessage<'slice, 'fd>>>(&>mutself, 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)] pubstruct RecvAncillaryBuffer<'buf> { /// Raw byte buffer for messages.
buffer: &'buf mut [MaybeUninit<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> 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 std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(1))]; /// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space); /// ``` /// /// Allocate a buffer for credentials: /// ``` /// # #[cfg(linux_kernel)] /// # { /// # use std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); 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 std::mem::MaybeUninit; /// # use rustix::cmsg_space; /// # use rustix::net::RecvAncillaryBuffer; /// let mut space = [MaybeUninit::uninit(); rustix::cmsg_space!(ScmRights(2), ScmCredentials(1))]; /// let mut cmsg_buffer = RecvAncillaryBuffer::new(&mut space); /// # } /// ``` /// /// [`recv`]: crate::net::recv #[inline] pubfn new(buffer: &'buf mut [MaybeUninit<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(&mutself) -> *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))] ifself.buffer.is_empty() { return core::ptr::null_mut();
}
self.buffer.as_mut_ptr().cast()
}
/// 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) unsafefn set_control_len(&mutself, len: usize) { self.length = len; self.read = 0;
}
/// Delete all messages from the buffer. pub(crate) fn clear(&mutself) { self.drain().for_each(drop);
}
/// Drain all messages from the buffer. pubfn drain(&mutself) -> AncillaryDrain<'_> {
AncillaryDrain {
messages: messages::Messages::new(&mutself.buffer[self.read..][..self.length]),
read_and_length: Some((&mutself.read, &>mutself.length)),
}
}
}
impl Drop for RecvAncillaryBuffer<'_> { fn drop(&mutself) { self.clear();
}
}
/// Return a slice of `buffer` starting at the first `cmsghdr` alignment /// boundary. #[inline] fn align_for_cmsghdr(buffer: &mut [MaybeUninit<u8>]) -> &='color:red'>mut [MaybeUninit<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::<c::cmsghdr>(); 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`]. pubstruct AncillaryDrain<'buf> { /// Inner iterator over messages.
messages: messages::Messages<'buf>,
/// Increment the number of messages we've read. /// Decrement the total length.
read_and_length: Option<(&'buf mut usize, &'buf mut usize)>,
}
impl<'buf> AncillaryDrain<'buf> { /// Create an iterator for control messages that were received without /// [`RecvAncillaryBuffer`]. /// /// # Safety /// /// The buffer must contain valid message data (or be empty). pubunsafefn parse(buffer: &'buf mut [u8]) -> Self { Self {
messages: messages::Messages::new(buffer),
read_and_length: None,
}
}
/// A closure that converts a message into a [`RecvAncillaryMessage`]. fn cvt_msg(msg: &c::cmsghdr) -> Option<RecvAncillaryMessage<'buf>> { unsafe { // 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);
fn collect<B: FromIterator<Self::Item>>(mutself) -> B where Self: Sized,
{ self.messages
.filter_map(|ev| Self::advance(&mutself.read_and_length, ev))
.collect()
}
}
impl FusedIterator for AncillaryDrain<'_> {}
/// An ABI-compatible wrapper for `mmsghdr`, for sending multiple messages with /// [sendmmsg]. #[cfg(target_os = "linux")] #[repr(transparent)] pubstruct MMsgHdr<'a> {
raw: c::mmsghdr,
_phantom: PhantomData<&'a mut ()>,
}
#[cfg(target_os = "linux")] impl<'a> MMsgHdr<'a> { /// Constructs a new message with no destination address. pubfn new(iov: &'a [IoSlice<'_>], control: &'a mut SendAncillaryBuffer<'_, '_, '_>) -> Self { Self::wrap(noaddr_msghdr(iov, control))
}
/// Constructs a new message to a specific address. /// /// This requires a `SocketAddrAny` instead of using `impl SocketAddrArg`; /// to obtain a `SocketAddrAny`, use [`SocketAddrArg::as_any`]. pubfn new_with_addr(
addr: &'a SocketAddrAny,
iov: &'a [IoSlice<'_>],
control: &'a mut SendAncillaryBuffer<'_, '_, '_>,
) -> Self { // The reason we use `SocketAddrAny` instead of `SocketAddrArg` here, // and avoid `use_msghdr`, is that we need a pointer that will remain // valid for the duration of the `'a` lifetime. `SocketAddrAny` can // give us a pointer directly, so we use that. letmut msghdr = noaddr_msghdr(iov, control);
msghdr.msg_name = addr.as_ptr() as _;
msghdr.msg_namelen = bitcast!(addr.addr_len());
/// Returns the number of bytes sent. This will return 0 until after a /// successful call to [sendmmsg]. pubfn bytes_sent(&self) -> usize { self.raw.msg_len as usize
}
}
/// The result of a successful [`recvmsg`] call. #[derive(Debug, Clone)] pubstruct RecvMsg { /// The number of bytes received. /// /// When `RecvFlags::TRUNC` is in use, this may be greater than the length /// of the buffer, as it reflects the number of bytes received before /// truncation into the buffer. pub bytes: usize,
/// The flags received. pub flags: ReturnFlags,
/// The address of the socket we received from, if any. pub address: Option<SocketAddrAny>,
}
/// An iterator over data in an ancillary buffer. pubstruct AncillaryIter<'data, T> { /// The data we're iterating over.
data: &'data mut [u8],
/// The raw data we're removing.
_marker: PhantomData<T>,
}
impl<'data, T> AncillaryIter<'data, T> { /// Create a new iterator over data in an ancillary buffer. /// /// # Safety /// /// The buffer must contain valid ancillary data. unsafefn new(data: &'data mut [u8]) -> Self {
assert_eq!(data.len() % size_of::<T>(), 0);
Self {
data,
_marker: PhantomData,
}
}
}
impl<'data, T> Drop for AncillaryIter<'data, T> { fn drop(&mutself) { self.for_each(drop);
}
}
impl<T> Iterator for AncillaryIter<'_, T> { type Item = T;
fn next(&mutself) -> Option<Self::Item> { // See if there is a next item. ifself.data.len() < size_of::<T>() { return None;
}
// Get the next item. let item = unsafe { self.data.as_ptr().cast::<T>().read_unaligned() };
// Move forward. let data = take(&mutself.data); self.data = &mut data[size_of::<T>()..];
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) { let len = self.len();
(len, Some(len))
}
impl<T> DoubleEndedIterator for AncillaryIter<'_, T> { fn next_back(&mutself) -> Option<Self::Item> { // See if there is a next item. ifself.data.len() < size_of::<T>() { return None;
}
// Get the next item. let item = unsafe { let ptr = self.data.as_ptr().add(self.data.len() - size_of::<T>());
ptr.cast::<T>().read_unaligned()
};
// Move forward. let len = self.data.len(); let data = take(&mutself.data); self.data = &mut data[..len - size_of::<T>()];
Some(item)
}
}
mod messages { usecrate::backend::c; usecrate::backend::net::msghdr; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem::MaybeUninit; 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<NonNull<c::cmsghdr>>,
/// Capture the original lifetime of the buffer.
_buffer: PhantomData<&'buf mut [MaybeUninit<u8>]>,
}
pub(super) trait AllowedMsgBufType {} impl AllowedMsgBufType for u8 {} impl AllowedMsgBufType for MaybeUninit<u8> {}
impl<'buf> Messages<'buf> { /// Create a new iterator over messages from a byte buffer. pub(super) fn new(buf: &'buf mut [impl AllowedMsgBufType]) -> Self { letmut msghdr = msghdr::zero_msghdr();
msghdr.msg_control = buf.as_mut_ptr().cast();
msghdr.msg_controllen = buf.len().try_into().expect("buffer too large for msghdr");
// Get the first header. let header = NonNull::new(unsafe { c::CMSG_FIRSTHDR(&msghdr) });
impl<'a> Iterator for Messages<'a> { type Item = &'a mut c::cmsghdr;
#[inline] fn next(&mutself) -> Option<Self::Item> { // 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<usize>) { ifself.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<'_> {}
}
#[cfg(test)] mod tests { #[no_implicit_prelude] mod hygiene { #[allow(unused_macros)] #[test] fn macro_hygiene() { // This `u64` is `!Sized`, so `cmsg_space!` will fail if it tries to get its size with // `size_of()`. #[allow(dead_code, non_camel_case_types)] struct u64([u8]);
// Ensure that when `cmsg*_space!` calls itself recursively, it really calls itself and // not these macros.
macro_rules! cmsg_space {
($($tt:tt)*) => {{ let v: usize = ::core::panic!("Wrong cmsg_space! macro called");
v
}};
}
macro_rules! cmsg_aligned_space {
($($tt:tt)*) => {{ let v: usize = ::core::panic!("Wrong cmsg_aligned_space! macro called");
v
}};
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.