Skip to content

Commit af8e2a8

Browse files
committed
Added sendmmsg and recvmmsg (#574) (#575)
1 parent 4c1cf6b commit af8e2a8

File tree

4 files changed

+471
-1
lines changed

4 files changed

+471
-1
lines changed

src/lib.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,3 +701,208 @@ impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> {
701701
"MsgHdrMut".fmt(fmt)
702702
}
703703
}
704+
705+
/// Configuration of a `sendmmsg(2)` system call.
706+
///
707+
/// This wraps `mmsghdr` on Unix. Also see [`MMsgHdrMut`] for the variant used by `recvmmsg(2)`.
708+
/// This API is not available on Windows.
709+
#[cfg(any(
710+
target_os = "aix",
711+
target_os = "android",
712+
target_os = "freebsd",
713+
target_os = "fuchsia",
714+
target_os = "linux",
715+
target_os = "netbsd",
716+
target_os = "openbsd",
717+
))]
718+
pub struct MMsgHdr<'addr, 'bufs, 'control> {
719+
inner: sys::mmsghdr,
720+
#[allow(clippy::type_complexity)]
721+
_lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>,
722+
}
723+
724+
#[cfg(any(
725+
target_os = "aix",
726+
target_os = "android",
727+
target_os = "freebsd",
728+
target_os = "fuchsia",
729+
target_os = "linux",
730+
target_os = "netbsd",
731+
target_os = "openbsd",
732+
))]
733+
impl<'addr, 'bufs, 'control> MMsgHdr<'addr, 'bufs, 'control> {
734+
/// Create a new `MMsgHdr` with all empty/zero fields.
735+
#[allow(clippy::new_without_default)]
736+
pub fn new() -> MMsgHdr<'addr, 'bufs, 'control> {
737+
// SAFETY: all zero is valid for `mmsghdr`.
738+
MMsgHdr {
739+
inner: unsafe { mem::zeroed() },
740+
_lifetimes: PhantomData,
741+
}
742+
}
743+
744+
/// Set the address (name) of the message.
745+
///
746+
/// Corresponds to setting `msg_name` and `msg_namelen` on Unix.
747+
pub fn with_addr(mut self, addr: &'addr SockAddr) -> Self {
748+
sys::set_msghdr_name(&mut self.inner.msg_hdr, addr);
749+
self
750+
}
751+
752+
/// Set the buffer(s) of the message.
753+
///
754+
/// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix.
755+
pub fn with_buffers(mut self, bufs: &'bufs [IoSlice<'_>]) -> Self {
756+
let ptr = bufs.as_ptr() as *mut _;
757+
sys::set_msghdr_iov(&mut self.inner.msg_hdr, ptr, bufs.len());
758+
self
759+
}
760+
761+
/// Set the control buffer of the message.
762+
///
763+
/// Corresponds to setting `msg_control` and `msg_controllen` on Unix.
764+
pub fn with_control(mut self, buf: &'control [u8]) -> Self {
765+
let ptr = buf.as_ptr() as *mut _;
766+
sys::set_msghdr_control(&mut self.inner.msg_hdr, ptr, buf.len());
767+
self
768+
}
769+
770+
/// Set the flags of the message.
771+
///
772+
/// Corresponds to setting `msg_flags` on Unix.
773+
pub fn with_flags(mut self, flags: sys::c_int) -> Self {
774+
sys::set_msghdr_flags(&mut self.inner.msg_hdr, flags);
775+
self
776+
}
777+
778+
/// Gets the number of sent bytes.
779+
///
780+
/// Corresponds to `msg_len` on Unix.
781+
pub fn data_len(&self) -> usize {
782+
self.inner.msg_len as usize
783+
}
784+
}
785+
786+
#[cfg(any(
787+
target_os = "aix",
788+
target_os = "android",
789+
target_os = "freebsd",
790+
target_os = "fuchsia",
791+
target_os = "linux",
792+
target_os = "netbsd",
793+
target_os = "openbsd",
794+
))]
795+
impl<'name, 'bufs, 'control> fmt::Debug for MMsgHdr<'name, 'bufs, 'control> {
796+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
797+
"MMsgHdr".fmt(fmt)
798+
}
799+
}
800+
801+
/// Configuration of a `recvmmsg(2)` system call.
802+
///
803+
/// This wraps `mmsghdr` on Unix. Also see [`MMsgHdr`] for the variant used by `sendmmsg(2)`.
804+
/// This API is not available on Windows.
805+
#[cfg(any(
806+
target_os = "aix",
807+
target_os = "android",
808+
target_os = "freebsd",
809+
target_os = "fuchsia",
810+
target_os = "linux",
811+
target_os = "netbsd",
812+
target_os = "openbsd",
813+
))]
814+
pub struct MMsgHdrMut<'addr, 'bufs, 'control> {
815+
inner: sys::mmsghdr,
816+
#[allow(clippy::type_complexity)]
817+
_lifetimes: PhantomData<(
818+
&'addr mut SockAddr,
819+
&'bufs mut MaybeUninitSlice<'bufs>,
820+
&'control mut [u8],
821+
)>,
822+
}
823+
824+
#[cfg(any(
825+
target_os = "aix",
826+
target_os = "android",
827+
target_os = "freebsd",
828+
target_os = "fuchsia",
829+
target_os = "linux",
830+
target_os = "netbsd",
831+
target_os = "openbsd",
832+
))]
833+
impl<'addr, 'bufs, 'control> MMsgHdrMut<'addr, 'bufs, 'control> {
834+
/// Create a new `MMsgHdrMut` with all empty/zero fields.
835+
#[allow(clippy::new_without_default)]
836+
pub fn new() -> MMsgHdrMut<'addr, 'bufs, 'control> {
837+
// SAFETY: all zero is valid for `mmsghdr`.
838+
MMsgHdrMut {
839+
inner: unsafe { mem::zeroed() },
840+
_lifetimes: PhantomData,
841+
}
842+
}
843+
844+
/// Set the mutable address (name) of the message.
845+
///
846+
/// Corresponds to setting `msg_name` and `msg_namelen` on Unix.
847+
#[allow(clippy::needless_pass_by_ref_mut)]
848+
pub fn with_addr(mut self, addr: &'addr mut SockAddr) -> Self {
849+
sys::set_msghdr_name(&mut self.inner.msg_hdr, addr);
850+
self
851+
}
852+
853+
/// Set the mutable buffer(s) of the message.
854+
///
855+
/// Corresponds to setting `msg_iov` and `msg_iovlen` on Unix.
856+
pub fn with_buffers(mut self, bufs: &'bufs mut [MaybeUninitSlice<'_>]) -> Self {
857+
sys::set_msghdr_iov(
858+
&mut self.inner.msg_hdr,
859+
bufs.as_mut_ptr().cast(),
860+
bufs.len(),
861+
);
862+
self
863+
}
864+
865+
/// Set the mutable control buffer of the message.
866+
///
867+
/// Corresponds to setting `msg_control` and `msg_controllen` on Unix.
868+
pub fn with_control(mut self, buf: &'control mut [MaybeUninit<u8>]) -> Self {
869+
sys::set_msghdr_control(&mut self.inner.msg_hdr, buf.as_mut_ptr().cast(), buf.len());
870+
self
871+
}
872+
873+
/// Returns the flags of the message.
874+
pub fn flags(&self) -> RecvFlags {
875+
sys::msghdr_flags(&self.inner.msg_hdr)
876+
}
877+
878+
/// Gets the length of the control buffer.
879+
///
880+
/// Can be used to determine how much, if any, of the control buffer was filled by `recvmsg`.
881+
///
882+
/// Corresponds to `msg_controllen` on Unix.
883+
pub fn control_len(&self) -> usize {
884+
sys::msghdr_control_len(&self.inner.msg_hdr)
885+
}
886+
887+
/// Gets the number of received bytes.
888+
///
889+
/// Corresponds to `msg_len` on Unix.
890+
pub fn data_len(&self) -> usize {
891+
self.inner.msg_len as usize
892+
}
893+
}
894+
895+
#[cfg(any(
896+
target_os = "aix",
897+
target_os = "android",
898+
target_os = "freebsd",
899+
target_os = "fuchsia",
900+
target_os = "linux",
901+
target_os = "netbsd",
902+
target_os = "openbsd",
903+
))]
904+
impl<'name, 'bufs, 'control> fmt::Debug for MMsgHdrMut<'name, 'bufs, 'control> {
905+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
906+
"MMsgHdrMut".fmt(fmt)
907+
}
908+
}

src/socket.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ use crate::sys::{self, c_int, getsockopt, setsockopt, Bool};
2424
#[cfg(all(unix, not(target_os = "redox")))]
2525
use crate::MsgHdrMut;
2626
use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type};
27+
#[cfg(any(
28+
target_os = "aix",
29+
target_os = "android",
30+
target_os = "freebsd",
31+
target_os = "fuchsia",
32+
target_os = "linux",
33+
target_os = "netbsd",
34+
target_os = "openbsd",
35+
))]
36+
use crate::{MMsgHdr, MMsgHdrMut};
2737
#[cfg(not(target_os = "redox"))]
2838
use crate::{MaybeUninitSlice, MsgHdr, RecvFlags};
2939

@@ -648,6 +658,27 @@ impl Socket {
648658
sys::recvmsg(self.as_raw(), msg, flags)
649659
}
650660

661+
/// Receive a list of messages on a socket using a message structure.
662+
/// Note that the timeout is buggy on Linux, see BUGS section in the Linux manual page.
663+
#[doc = man_links!(recvmmsg(2))]
664+
#[cfg(any(
665+
target_os = "aix",
666+
target_os = "android",
667+
target_os = "freebsd",
668+
target_os = "fuchsia",
669+
target_os = "linux",
670+
target_os = "netbsd",
671+
target_os = "openbsd",
672+
))]
673+
pub fn recvmmsg(
674+
&self,
675+
msgs: &mut [MMsgHdrMut<'_, '_, '_>],
676+
flags: sys::c_int,
677+
timeout: Option<Duration>,
678+
) -> io::Result<usize> {
679+
sys::recvmmsg(self.as_raw(), msgs, flags, timeout)
680+
}
681+
651682
/// Sends data on the socket to a connected peer.
652683
///
653684
/// This is typically used on TCP sockets or datagram sockets which have
@@ -749,6 +780,25 @@ impl Socket {
749780
pub fn sendmsg(&self, msg: &MsgHdr<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
750781
sys::sendmsg(self.as_raw(), msg, flags)
751782
}
783+
784+
/// Send a list of messages on a socket using a message structure.
785+
#[doc = man_links!(sendmmsg(2))]
786+
#[cfg(any(
787+
target_os = "aix",
788+
target_os = "android",
789+
target_os = "freebsd",
790+
target_os = "fuchsia",
791+
target_os = "linux",
792+
target_os = "netbsd",
793+
target_os = "openbsd",
794+
))]
795+
pub fn sendmmsg(
796+
&self,
797+
msgs: &mut [MMsgHdr<'_, '_, '_>],
798+
flags: sys::c_int,
799+
) -> io::Result<usize> {
800+
sys::sendmmsg(self.as_raw(), msgs, flags)
801+
}
752802
}
753803

754804
/// Set `SOCK_CLOEXEC` and `NO_HANDLE_INHERIT` on the `ty`pe on platforms that

0 commit comments

Comments
 (0)