Skip to content

Commit

Permalink
Add sendmsg/recvmsg
Browse files Browse the repository at this point in the history
  • Loading branch information
notgull committed Jan 8, 2023
1 parent 811e5ea commit 42b1fce
Show file tree
Hide file tree
Showing 18 changed files with 1,908 additions and 19 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ once_cell = { version = "1.5.2", optional = true }
# libc backend can be selected via adding `--cfg=rustix_use_libc` to
# `RUSTFLAGS` or enabling the `use-libc` cargo feature.
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"), all(target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "powerpc64", target_arch = "riscv64", target_arch = "mips", target_arch = "mips64")))))'.dependencies]
linux-raw-sys = { version = "0.1.2", default-features = false, features = ["general", "errno", "ioctl", "no_std"] }
linux-raw-sys = { version = "0.1.4", default-features = false, features = ["general", "errno", "ioctl", "no_std"] }
libc_errno = { package = "errno", version = "0.2.8", default-features = false, optional = true }
libc = { version = "0.2.133", features = ["extra_traits"], optional = true }

Expand Down
42 changes: 42 additions & 0 deletions src/backend/libc/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,45 @@ pub(super) fn ret_send_recv(len: isize) -> io::Result<c::ssize_t> {
pub(super) fn ret_send_recv(len: i32) -> io::Result<c::ssize_t> {
ret_ssize_t(len as isize)
}

/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(windows, target_os = "wasm32")),
any(
target_os = "android",
all(target_os = "linux", not(target_env = "musl"))
)
))]
#[inline]
pub(super) fn msg_iov_len(len: usize) -> c::size_t {
len
}

/// Convert the value to the `msg_iovlen` field of a `msghdr` struct.
#[cfg(all(
not(any(windows, target_os = "wasm32")),
not(any(
target_os = "android",
all(target_os = "linux", not(target_env = "musl"))
))
))]
#[inline]
pub(super) fn msg_iov_len(len: usize) -> c::c_int {
use core::convert::TryInto;
len.try_into().unwrap_or(c::c_int::MAX)
}

/// Convert the value to a `socklen_t`.
#[cfg(not(any(windows, target_os = "wasm32", target_os = "android")))]
#[inline]
pub(super) fn msg_control_len(len: usize) -> c::socklen_t {
use core::convert::TryInto;
len.try_into().unwrap_or(c::socklen_t::MAX)
}

/// Convert the value to a `size_t`.
#[cfg(target_os = "android")]
#[inline]
pub(super) fn msg_control_len(len: usize) -> c::size_t {
len
}
2 changes: 2 additions & 0 deletions src/backend/libc/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub(crate) mod addr;
pub(crate) mod ext;
#[cfg(not(any(target_os = "redox", target_os = "wasi", windows)))]
pub(crate) mod msghdr;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod syscalls;
Expand Down
121 changes: 121 additions & 0 deletions src/backend/libc/net/msghdr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! Utilities for dealing with message headers.
//!
//! These take closures rather than returning a `c::msghdr` directly because the
//! message headers may reference stack-local data.
use super::super::c;
use super::super::conv::{msg_control_len, msg_iov_len};
use super::super::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};

use crate::io;
use crate::net::{SocketAddrV4, SocketAddrV6};
use crate::utils::as_ptr;

use core::mem::{size_of, zeroed, MaybeUninit};
use core::ptr::null_mut;

/// Create a message header intended to receive a datagram.
pub(crate) fn with_recv_msghdr<R>(
name: &mut MaybeUninit<c::sockaddr_storage>,
iov: &mut [io::IoSliceMut<'_>],
control: &mut crate::net::RecvAncillaryBuffer<'_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let namelen = size_of::<c::sockaddr_storage>() as c::socklen_t;

f(c::msghdr {
msg_name: name.as_mut_ptr().cast(),
msg_namelen: namelen,
msg_iov: iov.as_mut_ptr().cast(),
msg_iovlen: msg_iov_len(iov.len()),
msg_control: control.as_control_ptr().cast(),
msg_controllen: msg_control_len(control.control_len()),

// Zero-initialize any padding bytes.
..unsafe { zeroed() }
})
}

/// Create a message header intended to send without an address.
pub(crate) fn with_noaddr_msghdr<R>(
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
f(c::msghdr {
msg_name: null_mut(),
msg_namelen: 0,
msg_iov: iov.as_ptr() as _,
msg_iovlen: msg_iov_len(iov.len()),
msg_control: control.as_control_ptr().cast(),
msg_controllen: msg_control_len(control.control_len()),

// Zero-initialize any padding bytes.
..unsafe { zeroed() }
})
}

/// Create a message header intended to send with an IPv4 address.
pub(crate) fn with_v4_msghdr<R>(
addr: &SocketAddrV4,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let encoded = unsafe { encode_sockaddr_v4(addr) };

f(c::msghdr {
msg_name: as_ptr(&encoded) as _,
msg_namelen: size_of::<SocketAddrV4>() as _,
msg_iov: iov.as_ptr() as _,
msg_iovlen: msg_iov_len(iov.len()),
msg_control: control.as_control_ptr().cast(),
msg_controllen: msg_control_len(control.control_len()),

// Zero-initialize any padding bytes.
..unsafe { zeroed() }
})
}

/// Create a message header intended to send with an IPv6 address.
pub(crate) fn with_v6_msghdr<R>(
addr: &SocketAddrV6,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
let encoded = unsafe { encode_sockaddr_v6(addr) };

f(c::msghdr {
msg_name: as_ptr(&encoded) as _,
msg_namelen: size_of::<SocketAddrV6>() as _,
msg_iov: iov.as_ptr() as _,
msg_iovlen: msg_iov_len(iov.len()),
msg_control: control.as_control_ptr().cast(),
msg_controllen: msg_control_len(control.control_len()),

// Zero-initialize any padding bytes.
..unsafe { zeroed() }
})
}

/// Create a message header intended to send with a Unix address.
#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) fn with_unix_msghdr<R>(
addr: &crate::net::SocketAddrUnix,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
f: impl FnOnce(c::msghdr) -> R,
) -> R {
f(c::msghdr {
msg_name: as_ptr(addr) as _,
msg_namelen: addr.addr_len(),
msg_iov: iov.as_ptr() as _,
msg_iovlen: msg_iov_len(iov.len()),
msg_control: control.as_control_ptr().cast(),
msg_controllen: msg_control_len(control.control_len()),

// Zero-initialize any padding bytes.
..unsafe { zeroed() }
})
}
93 changes: 93 additions & 0 deletions src/backend/libc/net/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use super::super::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_rec
#[cfg(unix)]
use super::addr::SocketAddrUnix;
use super::ext::{in6_addr_new, in_addr_new};
#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
use super::msghdr::{with_noaddr_msghdr, with_recv_msghdr, with_v4_msghdr, with_v6_msghdr};
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
use super::read_sockaddr::initialize_family_to_unspec;
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
Expand Down Expand Up @@ -250,6 +252,97 @@ pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> {
}
}

#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn recvmsg(
sockfd: BorrowedFd<'_>,
iov: &mut [io::IoSliceMut<'_>],
control: &mut crate::net::RecvAncillaryBuffer<'_>,
msg_flags: RecvFlags,
) -> io::Result<crate::net::RecvMsgResult> {
let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit();

with_recv_msghdr(&mut storage, iov, control, |mut msghdr| {
let result = unsafe {
ret_send_recv(c::recvmsg(
borrowed_fd(sockfd),
&mut msghdr,
msg_flags.bits(),
))
};

result.map(|bytes| {
// Get the address of the sender, if any.
let addr =
unsafe { maybe_read_sockaddr_os(msghdr.msg_name as _, msghdr.msg_namelen as _) };

crate::net::RecvMsgResult {
bytes: bytes as usize,
address: addr,
flags: RecvFlags::from_bits_truncate(msghdr.msg_flags),
}
})
})
}

#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendmsg_noaddr(
sockfd: BorrowedFd<'_>,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_noaddr_msghdr(iov, control, |msghdr| {
let len =
unsafe { ret_send_recv(c::sendmsg(borrowed_fd(sockfd), &msghdr, msg_flags.bits()))? };
Ok(len as usize)
})
}

#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendmsg_v4(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrV4,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_v4_msghdr(addr, iov, control, |msghdr| {
let len =
unsafe { ret_send_recv(c::sendmsg(borrowed_fd(sockfd), &msghdr, msg_flags.bits()))? };
Ok(len as usize)
})
}

#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))]
pub(crate) fn sendmsg_v6(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrV6,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
with_v6_msghdr(addr, iov, control, |msghdr| {
let len =
unsafe { ret_send_recv(c::sendmsg(borrowed_fd(sockfd), &msghdr, msg_flags.bits()))? };
Ok(len as usize)
})
}

#[cfg(all(unix, not(target_os = "redox")))]
pub(crate) fn sendmsg_unix(
sockfd: BorrowedFd<'_>,
addr: &SocketAddrUnix,
iov: &[io::IoSlice<'_>],
control: &mut crate::net::SendAncillaryBuffer<'_, '_, '_>,
msg_flags: SendFlags,
) -> io::Result<usize> {
super::msghdr::with_unix_msghdr(addr, iov, control, |msghdr| {
let len =
unsafe { ret_send_recv(c::sendmsg(borrowed_fd(sockfd), &msghdr, msg_flags.bits()))? };
Ok(len as usize)
})
}

#[cfg(not(any(
windows,
target_os = "haiku",
Expand Down
35 changes: 18 additions & 17 deletions src/backend/linux_raw/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,26 @@
#![allow(unused_imports)]

pub(crate) use linux_raw_sys::cmsg_macros::*;
pub(crate) use linux_raw_sys::ctypes::*;
pub(crate) use linux_raw_sys::errno::EINVAL;
pub(crate) use linux_raw_sys::general::{
AF_DECnet, __kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
in6_addr, in_addr, iovec, ip_mreq, ipv6_mreq, linger, sockaddr, sockaddr_in, sockaddr_in6,
sockaddr_un, socklen_t, AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH,
AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN,
AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX,
AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE,
AF_X25, IPPROTO_AH, IPPROTO_BEETPH, IPPROTO_COMP, IPPROTO_DCCP, IPPROTO_EGP, IPPROTO_ENCAP,
IPPROTO_ESP, IPPROTO_ETHERNET, IPPROTO_FRAGMENT, IPPROTO_GRE, IPPROTO_ICMP, IPPROTO_ICMPV6,
IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IPIP, IPPROTO_IPV6, IPPROTO_MH, IPPROTO_MPLS,
IPPROTO_MPTCP, IPPROTO_MTP, IPPROTO_PIM, IPPROTO_PUP, IPPROTO_RAW, IPPROTO_ROUTING,
IPPROTO_RSVP, IPPROTO_SCTP, IPPROTO_TCP, IPPROTO_TP, IPPROTO_UDP, IPPROTO_UDPLITE,
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, IP_ADD_MEMBERSHIP,
IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_CMSG_CLOEXEC, MSG_CONFIRM,
MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK,
MSG_TRUNC, MSG_WAITALL, O_CLOEXEC, O_NONBLOCK, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM,
SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_LINGER,
SO_PASSCRED, SO_RCVTIMEO_NEW, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD,
SO_TYPE, TCP_NODELAY,
cmsghdr, in6_addr, in_addr, iovec, ip_mreq, ipv6_mreq, linger, msghdr, size_t, sockaddr,
sockaddr_in, sockaddr_in6, sockaddr_un, socklen_t, AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC,
AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN, AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX,
AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY, AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET,
AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE, AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX,
AF_UNSPEC, AF_WANPIPE, AF_X25, IPPROTO_AH, IPPROTO_BEETPH, IPPROTO_COMP, IPPROTO_DCCP,
IPPROTO_EGP, IPPROTO_ENCAP, IPPROTO_ESP, IPPROTO_ETHERNET, IPPROTO_FRAGMENT, IPPROTO_GRE,
IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_IDP, IPPROTO_IGMP, IPPROTO_IP, IPPROTO_IPIP,
IPPROTO_IPV6, IPPROTO_MH, IPPROTO_MPLS, IPPROTO_MPTCP, IPPROTO_MTP, IPPROTO_PIM, IPPROTO_PUP,
IPPROTO_RAW, IPPROTO_ROUTING, IPPROTO_RSVP, IPPROTO_SCTP, IPPROTO_TCP, IPPROTO_TP, IPPROTO_UDP,
IPPROTO_UDPLITE, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY,
IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL,
MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE, MSG_MORE,
MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, O_CLOEXEC, O_NONBLOCK,
SCM_CREDENTIALS, SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM,
SOCK_SEQPACKET, SOCK_STREAM, SOL_SOCKET, SO_BROADCAST, SO_LINGER, SO_PASSCRED, SO_RCVTIMEO_NEW,
SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD, SO_TYPE, TCP_NODELAY,
};
1 change: 1 addition & 0 deletions src/backend/linux_raw/net/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub(crate) mod addr;
pub(crate) mod msghdr;
pub(crate) mod read_sockaddr;
pub(crate) mod send_recv;
pub(crate) mod syscalls;
Expand Down
Loading

0 comments on commit 42b1fce

Please sign in to comment.