Skip to content

Commit 1eed9f4

Browse files
committed
Add preallocated buffer version of sendmsg
1 parent e5ac667 commit 1eed9f4

File tree

2 files changed

+92
-8
lines changed

2 files changed

+92
-8
lines changed

src/sys/socket/mod.rs

+39-7
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,20 @@ pub use self::addr::{SockaddrLike, SockaddrStorage};
3939
pub use self::addr::{AddressFamily, UnixAddr};
4040
#[cfg(not(solarish))]
4141
pub use self::addr::{AddressFamily, UnixAddr};
42-
#[cfg(not(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox")))]
42+
#[cfg(not(any(
43+
solarish,
44+
target_os = "haiku",
45+
target_os = "hurd",
46+
target_os = "redox"
47+
)))]
4348
#[cfg(feature = "net")]
4449
pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
45-
#[cfg(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox"))]
50+
#[cfg(any(
51+
solarish,
52+
target_os = "haiku",
53+
target_os = "hurd",
54+
target_os = "redox"
55+
))]
4656
#[cfg(feature = "net")]
4757
pub use self::addr::{SockaddrIn, SockaddrIn6};
4858

@@ -794,17 +804,17 @@ pub enum ControlMessageOwned {
794804
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
795805
Ipv6HopLimit(i32),
796806

797-
/// Retrieve the DSCP (ToS) header field of the incoming IPv4 packet.
807+
/// Retrieve the DSCP (ToS) header field of the incoming IPv4 packet.
798808
#[cfg(any(linux_android, target_os = "freebsd"))]
799809
#[cfg(feature = "net")]
800810
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
801811
Ipv4Tos(u8),
802812

803-
/// Retrieve the DSCP (Traffic Class) header field of the incoming IPv6 packet.
813+
/// Retrieve the DSCP (Traffic Class) header field of the incoming IPv6 packet.
804814
#[cfg(any(linux_android, target_os = "freebsd"))]
805815
#[cfg(feature = "net")]
806816
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
807-
Ipv6TClass(i32),
817+
Ipv6TClass(i32),
808818

809819
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
810820
/// packets from a single sender.
@@ -1577,7 +1587,7 @@ impl<'a> ControlMessage<'a> {
15771587
/// by ancillary data. Optionally direct the message at the given address,
15781588
/// as with sendto.
15791589
///
1580-
/// Allocates if cmsgs is nonempty.
1590+
/// Allocates if cmsgs is nonempty, use [`sendmsg_prealloc()`] if you want to use a pre-allocated buffer.
15811591
///
15821592
/// # Examples
15831593
/// When not directing to any specific address, use `()` for the generic type
@@ -1631,6 +1641,29 @@ pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
16311641
}
16321642

16331643

1644+
/// `sendmsg_prealloc()` is the same as [`sendmsg()`] but it accepts a preallocated
1645+
/// `cmsg` buffer vector.
1646+
///
1647+
/// Send data in scatter-gather vectors to a socket, possibly accompanied
1648+
/// by ancillary data. Optionally direct the message at the given address,
1649+
/// as with sendto.
1650+
pub fn sendmsg_prealloc<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
1651+
flags: MsgFlags, addr: Option<&S>, cmsg_buffer: &mut Vec<u8>) -> Result<usize>
1652+
where S: SockaddrLike
1653+
{
1654+
1655+
if cmsg_buffer.len() < cmsgs.len() {
1656+
return Err(Errno::ENOBUFS);
1657+
}
1658+
1659+
let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
1660+
1661+
let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
1662+
1663+
Errno::result(ret).map(|r| r as usize)
1664+
}
1665+
1666+
16341667
/// An extension of `sendmsg` that allows the caller to transmit multiple
16351668
/// messages on a socket using a single system call. This has performance
16361669
/// benefits for some applications.
@@ -2456,4 +2489,3 @@ pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
24562489
Errno::result(shutdown(df, how)).map(drop)
24572490
}
24582491
}
2459-

test/sys/test_socket.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,6 @@ pub fn test_sendmsg_ipv4packetinfo() {
12041204
}
12051205

12061206
let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
1207-
12081207
sendmsg(
12091208
sock.as_raw_fd(),
12101209
&iov,
@@ -1215,6 +1214,59 @@ pub fn test_sendmsg_ipv4packetinfo() {
12151214
.expect("sendmsg");
12161215
}
12171216

1217+
#[cfg(any(target_os = "linux", apple_targets, target_os = "netbsd"))]
1218+
#[test]
1219+
pub fn test_sendmsg_prealloc_ipv4packetinfo() {
1220+
use cfg_if::cfg_if;
1221+
use nix::sys::socket::{
1222+
bind, sendmsg_prealloc, socket, AddressFamily, ControlMessage,
1223+
MsgFlags, SockFlag, SockType, SockaddrIn,
1224+
};
1225+
use std::io::IoSlice;
1226+
1227+
let sock = socket(
1228+
AddressFamily::Inet,
1229+
SockType::Datagram,
1230+
SockFlag::empty(),
1231+
None,
1232+
)
1233+
.expect("socket failed");
1234+
1235+
let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000);
1236+
1237+
bind(sock.as_raw_fd(), &sock_addr).expect("bind failed");
1238+
1239+
let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
1240+
let iov = [IoSlice::new(&slice)];
1241+
1242+
cfg_if! {
1243+
if #[cfg(target_os = "netbsd")] {
1244+
let pi = libc::in_pktinfo {
1245+
ipi_ifindex: 0, /* Unspecified interface */
1246+
ipi_addr: libc::in_addr { s_addr: 0 },
1247+
};
1248+
} else {
1249+
let pi = libc::in_pktinfo {
1250+
ipi_ifindex: 0, /* Unspecified interface */
1251+
ipi_addr: libc::in_addr { s_addr: 0 },
1252+
ipi_spec_dst: sock_addr.as_ref().sin_addr,
1253+
};
1254+
}
1255+
}
1256+
1257+
let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
1258+
let mut cmsg_buffer = vec![0 as u8; 32]; // same size as sendmsg_ipv4packetinfo
1259+
sendmsg_prealloc(
1260+
sock.as_raw_fd(),
1261+
&iov,
1262+
&cmsg,
1263+
MsgFlags::empty(),
1264+
Some(&sock_addr),
1265+
&mut cmsg_buffer,
1266+
)
1267+
.expect("sendmsg_prealloc");
1268+
}
1269+
12181270
// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
12191271
// This creates a (udp) socket bound to ip6-localhost, then sends a message to
12201272
// itself but uses Ipv6PacketInfo to force the source address to be

0 commit comments

Comments
 (0)