Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Socket::{send,recv}msg and MsgHdr(Mut) #447

Merged
merged 10 commits into from
Jun 8, 2023
141 changes: 141 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
#![doc(test(attr(deny(warnings))))]

use std::fmt;
#[cfg(not(target_os = "redox"))]
use std::io::IoSlice;
#[cfg(not(target_os = "redox"))]
use std::marker::PhantomData;
#[cfg(not(target_os = "redox"))]
use std::mem;
use std::mem::MaybeUninit;
use std::net::SocketAddr;
use std::ops::{Deref, DerefMut};
Expand Down Expand Up @@ -562,3 +568,138 @@ impl TcpKeepalive {
}
}
}

/// Configuration of a `sendmsg(2)` system call.
///
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`]
/// for the variant used by `recvmsg(2)`.
#[cfg(not(target_os = "redox"))]
pub struct MsgHdr<'addr, 'bufs, 'control> {
inner: sys::msghdr,
#[allow(clippy::type_complexity)]
_lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
}

#[cfg(not(target_os = "redox"))]
impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> {
/// Create a new `MsgHdr` with all empty/zero fields.
#[allow(clippy::new_without_default)]
pub fn new() -> MsgHdr<'addr, 'bufs, 'control> {
// SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
MsgHdr {
inner: unsafe { mem::zeroed() },
_lifetimes: PhantomData,
}
}

/// Set the address (name) of the message.
///
/// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
/// and `namelen` on Windows.
pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
sys::set_msghdr_name(&mut self.inner, addr);
self
}

/// Set the buffer(s) of the message.
///
/// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
/// and `dwBufferCount` on Windows.
pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
let ptr = bufs.as_ptr() as *mut _;
sys::set_msghdr_iov(&mut self.inner, ptr, bufs.len());
self
}

/// Set the control buffer of the message.
///
/// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
/// `Control` on Windows.
pub fn with_control(mut self, buf: &'control [u8]) -> Self {
let ptr = buf.as_ptr() as *mut _;
sys::set_msghdr_control(&mut self.inner, ptr, buf.len());
self
}

/// Set the flags of the message.
///
/// Corresponds to setting `msg_flags` on Unix and `dwFlags` on Windows.
pub fn with_flags(mut self, flags: sys::c_int) -> Self {
sys::set_msghdr_flags(&mut self.inner, flags);
self
}
}

#[cfg(not(target_os = "redox"))]
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
"MsgHdr".fmt(fmt)
}
}

/// Configuration of a `recvmsg(2)` system call.
///
/// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for
/// the variant used by `sendmsg(2)`.
#[cfg(not(target_os = "redox"))]
pub struct MsgHdrMut<'addr, 'bufs, 'control> {
inner: sys::msghdr,
#[allow(clippy::type_complexity)]
_lifetimes: PhantomData<(
&'addr mut SockAddr,
&'bufs mut MaybeUninitSlice<'bufs>,
&'control mut [u8],
)>,
}

#[cfg(not(target_os = "redox"))]
impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> {
/// Create a new `MsgHdrMut` with all empty/zero fields.
#[allow(clippy::new_without_default)]
pub fn new() -> MsgHdrMut<'addr, 'bufs, 'control> {
// SAFETY: all zero is valid for `msghdr` and `WSAMSG`.
MsgHdrMut {
inner: unsafe { mem::zeroed() },
_lifetimes: PhantomData,
}
}

/// Set the mutable address (name) of the message.
///
/// Corresponds to setting `msg_name` and `msg_namelen` on Unix and `name`
/// and `namelen` on Windows.
pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
sys::set_msghdr_name(&mut self.inner, addr);
self
}

/// Set the mutable buffer(s) of the message.
///
/// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix and `lpBuffers`
/// and `dwBufferCount` on Windows.
pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
sys::set_msghdr_iov(&mut self.inner, bufs.as_mut_ptr().cast(), bufs.len());
self
}

/// Set the mutable control buffer of the message.
///
/// Corresponds to setting `msg_control` and `msg_controllen` on Unix and
/// `Control` on Windows.
pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
sys::set_msghdr_control(&mut self.inner, buf.as_mut_ptr().cast(), buf.len());
self
}

/// Returns the flags of the message.
pub fn flags(&self) -> RecvFlags {
sys::msghdr_flags(&self.inner)
}
}

#[cfg(not(target_os = "redox"))]
impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
"MsgHdrMut".fmt(fmt)
}
}
6 changes: 0 additions & 6 deletions src/sockaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,6 @@ impl SockAddr {
self.storage.ss_family == AF_UNIX as sa_family_t
}

/// Returns a raw pointer to the address storage.
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) const fn as_storage_ptr(&self) -> *const sockaddr_storage {
&self.storage
}

/// Returns this address as a `SocketAddr` if it is in the `AF_INET` (IPv4)
/// or `AF_INET6` (IPv6) family, otherwise returns `None`.
pub fn as_socket(&self) -> Option<SocketAddr> {
Expand Down
25 changes: 24 additions & 1 deletion src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ use std::os::windows::io::{FromRawSocket, IntoRawSocket};
use std::time::Duration;

use crate::sys::{self, c_int, getsockopt, setsockopt, Bool};
#[cfg(all(unix, not(target_os = "redox")))]
use crate::MsgHdrMut;
use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
#[cfg(not(target_os = "redox"))]
use crate::{MaybeUninitSlice, RecvFlags};
use crate::{MaybeUninitSlice, MsgHdr, RecvFlags};

/// Owned wrapper around a system socket.
///
Expand Down Expand Up @@ -627,6 +629,19 @@ impl Socket {
sys::peek_sender(self.as_raw())
}

/// Receive a message from a socket using a message structure.
///
/// This is not supported on Windows as calling `WSARecvMsg` (the `recvmsg`
/// equivalent) is not straight forward on Windows. See
/// <https://github.com/microsoft/Windows-classic-samples/blob/7cbd99ac1d2b4a0beffbaba29ea63d024ceff700/Samples/Win7Samples/netds/winsock/recvmsg/rmmc.cpp>
/// for an example (in C++).
#[doc = man_links!(recvmsg(2))]
#[cfg(all(unix, not(target_os = "redox")))]
#[cfg_attr(docsrs, doc(cfg(all(unix, not(target_os = "redox")))))]
pub fn recvmsg(&self, msg: &mut MsgHdrMut<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
sys::recvmsg(self.as_raw(), msg, flags)
}

/// Sends data on the socket to a connected peer.
///
/// This is typically used on TCP sockets or datagram sockets which have
Expand Down Expand Up @@ -725,6 +740,14 @@ impl Socket {
) -> io::Result<usize> {
sys::send_to_vectored(self.as_raw(), bufs, addr, flags)
}

/// Send a message on a socket using a message structure.
#[doc = man_links!(sendmsg(2))]
#[cfg(not(target_os = "redox"))]
#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))]
pub fn sendmsg(&self, msg: &MsgHdr<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
sys::sendmsg(self.as_raw(), msg, flags)
}
}

/// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that
Expand Down
Loading