Skip to content

Commit 0f8e90f

Browse files
committed
Added sendmmsg and recvmmsg (#574) (#575)
1 parent 9a30f4f commit 0f8e90f

4 files changed

Lines changed: 525 additions & 1 deletion

File tree

src/lib.rs

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

src/socket.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ 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(all(
28+
feature = "all",
29+
any(
30+
target_os = "aix",
31+
target_os = "android",
32+
target_os = "freebsd",
33+
target_os = "fuchsia",
34+
target_os = "linux",
35+
target_os = "netbsd",
36+
target_os = "openbsd",
37+
)
38+
))]
39+
use crate::{MMsgHdr, MMsgHdrMut};
2740
#[cfg(not(target_os = "redox"))]
2841
use crate::{MaybeUninitSlice, MsgHdr, RecvFlags};
2942

@@ -632,6 +645,30 @@ impl Socket {
632645
sys::recvmsg(self.as_raw(), msg, flags)
633646
}
634647

648+
/// Receive a list of messages on a socket using a message structure.
649+
/// Note that the timeout is buggy on Linux, see BUGS section in the Linux manual page.
650+
#[doc = man_links!(recvmmsg(2))]
651+
#[cfg(all(
652+
feature = "all",
653+
any(
654+
target_os = "aix",
655+
target_os = "android",
656+
target_os = "freebsd",
657+
target_os = "fuchsia",
658+
target_os = "linux",
659+
target_os = "netbsd",
660+
target_os = "openbsd",
661+
)
662+
))]
663+
pub fn recvmmsg(
664+
&self,
665+
msgs: &mut [MMsgHdrMut<'_, '_, '_>],
666+
flags: sys::c_int,
667+
timeout: Option<Duration>,
668+
) -> io::Result<usize> {
669+
sys::recvmmsg(self.as_raw(), msgs, flags, timeout)
670+
}
671+
635672
/// Sends data on the socket to a connected peer.
636673
///
637674
/// This is typically used on TCP sockets or datagram sockets which have
@@ -733,6 +770,28 @@ impl Socket {
733770
pub fn sendmsg(&self, msg: &MsgHdr<'_, '_, '_>, flags: sys::c_int) -> io::Result<usize> {
734771
sys::sendmsg(self.as_raw(), msg, flags)
735772
}
773+
774+
/// Send a list of messages on a socket using a message structure.
775+
#[doc = man_links!(sendmmsg(2))]
776+
#[cfg(all(
777+
feature = "all",
778+
any(
779+
target_os = "aix",
780+
target_os = "android",
781+
target_os = "freebsd",
782+
target_os = "fuchsia",
783+
target_os = "linux",
784+
target_os = "netbsd",
785+
target_os = "openbsd",
786+
)
787+
))]
788+
pub fn sendmmsg(
789+
&self,
790+
msgs: &mut [MMsgHdr<'_, '_, '_>],
791+
flags: sys::c_int,
792+
) -> io::Result<usize> {
793+
sys::sendmmsg(self.as_raw(), msgs, flags)
794+
}
736795
}
737796

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

0 commit comments

Comments
 (0)