Skip to content

Commit ffac960

Browse files
committed
Added sendmmsg and recvmmsg (#574) (#575)
1 parent 758a9e2 commit ffac960

File tree

4 files changed

+495
-1
lines changed

4 files changed

+495
-1
lines changed

src/lib.rs

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

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)