#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "uio")]
use crate::sys::time::TimeSpec;
#[cfg(not(target_os = "redox"))]
#[cfg(feature = "uio")]
use crate::sys::time::TimeVal;
use crate::{errno::Errno, Result};
use cfg_if::cfg_if;
use libc::{self, c_int, c_void, size_t, socklen_t};
#[cfg(all(feature = "uio", not(target_os = "redox")))]
use libc::{
iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE,
};
#[cfg(not(target_os = "redox"))]
use std::io::{IoSlice, IoSliceMut};
#[cfg(feature = "net")]
use std::net;
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, RawFd, OwnedFd};
use std::{mem, ptr};
#[deny(missing_docs)]
mod addr;
#[deny(missing_docs)]
pub mod sockopt;
pub use self::addr::{SockaddrLike, SockaddrStorage};
#[cfg(any(target_os = "illumos", target_os = "solaris"))]
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(any(
target_os = "illumos",
target_os = "solaris",
target_os = "haiku",
target_os = "redox",
)))]
#[cfg(feature = "net")]
pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
#[cfg(any(
target_os = "illumos",
target_os = "solaris",
target_os = "haiku",
target_os = "redox",
))]
#[cfg(feature = "net")]
pub use self::addr::{SockaddrIn, SockaddrIn6};
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use crate::sys::socket::addr::alg::AlgAddr;
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use crate::sys::socket::addr::netlink::NetlinkAddr;
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg(feature = "ioctl")]
pub use crate::sys::socket::addr::sys_control::SysControlAddr;
#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
pub use crate::sys::socket::addr::vsock::VsockAddr;
#[cfg(all(feature = "uio", not(target_os = "redox")))]
pub use libc::{cmsghdr, msghdr};
pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
#[cfg(feature = "net")]
pub use libc::{sockaddr_in, sockaddr_in6};
#[cfg(feature = "net")]
use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
#[non_exhaustive]
pub enum SockType {
Stream = libc::SOCK_STREAM,
Datagram = libc::SOCK_DGRAM,
SeqPacket = libc::SOCK_SEQPACKET,
#[cfg(not(target_os = "redox"))]
Raw = libc::SOCK_RAW,
#[cfg(not(any(target_os = "haiku", target_os = "redox")))]
Rdm = libc::SOCK_RDM,
}
impl TryFrom<i32> for SockType {
type Error = crate::Error;
fn try_from(x: i32) -> Result<Self> {
match x {
libc::SOCK_STREAM => Ok(Self::Stream),
libc::SOCK_DGRAM => Ok(Self::Datagram),
libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
#[cfg(not(target_os = "redox"))]
libc::SOCK_RAW => Ok(Self::Raw),
#[cfg(not(any(target_os = "haiku", target_os = "redox")))]
libc::SOCK_RDM => Ok(Self::Rdm),
_ => Err(Errno::EINVAL),
}
}
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum SockProtocol {
Tcp = libc::IPPROTO_TCP,
Udp = libc::IPPROTO_UDP,
Raw = libc::IPPROTO_RAW,
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
KextEvent = libc::SYSPROTO_EVENT,
#[cfg(any(target_os = "ios", target_os = "macos"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
KextControl = libc::SYSPROTO_CONTROL,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkRoute = libc::NETLINK_ROUTE,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkUserSock = libc::NETLINK_USERSOCK,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkNFLOG = libc::NETLINK_NFLOG,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkSELinux = libc::NETLINK_SELINUX,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkISCSI = libc::NETLINK_ISCSI,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkAudit = libc::NETLINK_AUDIT,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkNetFilter = libc::NETLINK_NETFILTER,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkRDMA = libc::NETLINK_RDMA,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkGeneric = libc::NETLINK_GENERIC,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
NetlinkCrypto = libc::NETLINK_CRYPTO,
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
EthAll = (libc::ETH_P_ALL as u16).to_be() as i32,
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
CanRaw = libc::CAN_RAW,
}
impl SockProtocol {
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[allow(non_upper_case_globals)]
pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; }
#[cfg(any(target_os = "android", target_os = "linux"))]
libc_bitflags! {
pub struct TimestampingFlag: libc::c_uint {
SOF_TIMESTAMPING_SOFTWARE;
SOF_TIMESTAMPING_RAW_HARDWARE;
SOF_TIMESTAMPING_TX_HARDWARE;
SOF_TIMESTAMPING_TX_SOFTWARE;
SOF_TIMESTAMPING_RX_HARDWARE;
SOF_TIMESTAMPING_RX_SOFTWARE;
SOF_TIMESTAMPING_OPT_ID;
SOF_TIMESTAMPING_OPT_TSONLY;
}
}
libc_bitflags! {
pub struct SockFlag: c_int {
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
SOCK_NONBLOCK;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
SOCK_CLOEXEC;
#[cfg(target_os = "netbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
SOCK_NOSIGPIPE;
#[cfg(target_os = "openbsd")]
#[cfg_attr(docsrs, doc(cfg(all())))]
SOCK_DNS;
}
}
libc_bitflags! {
pub struct MsgFlags: c_int {
MSG_OOB;
MSG_PEEK;
MSG_WAITALL;
#[cfg(not(target_os = "aix"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MSG_DONTWAIT;
MSG_CTRUNC;
MSG_TRUNC;
MSG_EOR;
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MSG_ERRQUEUE;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MSG_CMSG_CLOEXEC;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "haiku",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MSG_NOSIGNAL;
#[cfg(any(target_os = "android",
target_os = "fuchsia",
target_os = "linux",
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "solaris"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
MSG_WAITFORONE;
}
}
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UnixCredentials(libc::ucred);
impl UnixCredentials {
pub fn new() -> Self {
unsafe {
UnixCredentials(libc::ucred {
pid: libc::getpid(),
uid: libc::getuid(),
gid: libc::getgid()
})
}
}
pub fn pid(&self) -> libc::pid_t {
self.0.pid
}
pub fn uid(&self) -> libc::uid_t {
self.0.uid
}
pub fn gid(&self) -> libc::gid_t {
self.0.gid
}
}
impl Default for UnixCredentials {
fn default() -> Self {
Self::new()
}
}
impl From<libc::ucred> for UnixCredentials {
fn from(cred: libc::ucred) -> Self {
UnixCredentials(cred)
}
}
impl From<UnixCredentials> for libc::ucred {
fn from(uc: UnixCredentials) -> Self {
uc.0
}
}
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UnixCredentials(libc::cmsgcred);
impl UnixCredentials {
pub fn pid(&self) -> libc::pid_t {
self.0.cmcred_pid
}
pub fn uid(&self) -> libc::uid_t {
self.0.cmcred_uid
}
pub fn euid(&self) -> libc::uid_t {
self.0.cmcred_euid
}
pub fn gid(&self) -> libc::gid_t {
self.0.cmcred_gid
}
pub fn groups(&self) -> &[libc::gid_t] {
unsafe {
std::slice::from_raw_parts(
self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
self.0.cmcred_ngroups as _
)
}
}
}
impl From<libc::cmsgcred> for UnixCredentials {
fn from(cred: libc::cmsgcred) -> Self {
UnixCredentials(cred)
}
}
}
}
cfg_if! {
if #[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "ios"
))] {
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct XuCred(libc::xucred);
impl XuCred {
pub fn version(&self) -> u32 {
self.0.cr_version
}
pub fn uid(&self) -> libc::uid_t {
self.0.cr_uid
}
pub fn groups(&self) -> &[libc::gid_t] {
&self.0.cr_groups
}
}
}
}
feature! {
#![feature = "net"]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IpMembershipRequest(libc::ip_mreq);
impl IpMembershipRequest {
pub fn new(group: net::Ipv4Addr, interface: Option<net::Ipv4Addr>)
-> Self
{
let imr_addr = match interface {
None => net::Ipv4Addr::UNSPECIFIED,
Some(addr) => addr
};
IpMembershipRequest(libc::ip_mreq {
imr_multiaddr: ipv4addr_to_libc(group),
imr_interface: ipv4addr_to_libc(imr_addr)
})
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
impl Ipv6MembershipRequest {
pub const fn new(group: net::Ipv6Addr) -> Self {
Ipv6MembershipRequest(libc::ipv6_mreq {
ipv6mr_multiaddr: ipv6addr_to_libc(&group),
ipv6mr_interface: 0,
})
}
}
}
#[cfg(not(target_os = "redox"))]
feature! {
#![feature = "uio"]
#[macro_export]
macro_rules! cmsg_space {
( $( $x:ty ),* ) => {
{
let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*;
Vec::<u8>::with_capacity(space)
}
}
}
#[inline]
#[doc(hidden)]
pub fn cmsg_space<T>() -> usize {
unsafe { libc::CMSG_SPACE(mem::size_of::<T>() as libc::c_uint) as usize }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RecvMsg<'a, 's, S> {
pub bytes: usize,
cmsghdr: Option<&'a cmsghdr>,
pub address: Option<S>,
pub flags: MsgFlags,
iobufs: std::marker::PhantomData<& 's()>,
mhdr: msghdr,
}
impl<'a, S> RecvMsg<'a, '_, S> {
pub fn cmsgs(&self) -> CmsgIterator {
CmsgIterator {
cmsghdr: self.cmsghdr,
mhdr: &self.mhdr
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CmsgIterator<'a> {
cmsghdr: Option<&'a cmsghdr>,
mhdr: &'a msghdr
}
impl<'a> Iterator for CmsgIterator<'a> {
type Item = ControlMessageOwned;
fn next(&mut self) -> Option<ControlMessageOwned> {
match self.cmsghdr {
None => None, Some(hdr) => {
let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))};
self.cmsghdr = unsafe {
let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _);
p.as_ref()
};
cm
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ControlMessageOwned {
ScmRights(Vec<RawFd>),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ScmCredentials(UnixCredentials),
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ScmCreds(UnixCredentials),
ScmTimestamp(TimeVal),
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmTimestampsns(Timestamps),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ScmTimestampns(TimeSpec),
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4PacketInfo(libc::in_pktinfo),
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "openbsd",
target_os = "netbsd",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6PacketInfo(libc::in6_pktinfo),
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvIf(libc::sockaddr_dl),
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvDstAddr(libc::in_addr),
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4OrigDstAddr(libc::sockaddr_in),
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6OrigDstAddr(libc::sockaddr_in6),
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
UdpGroSegments(u16),
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
RxqOvfl(u32),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
#[doc(hidden)]
Unknown(UnknownCmsg),
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Timestamps {
pub system: TimeSpec,
pub hw_trans: TimeSpec,
pub hw_raw: TimeSpec,
}
impl ControlMessageOwned {
#[allow(clippy::cast_ptr_alignment)]
unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
{
let p = CMSG_DATA(header);
#[allow(clippy::unnecessary_cast)]
let len = header as *const _ as usize + header.cmsg_len as usize
- p as usize;
match (header.cmsg_level, header.cmsg_type) {
(libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
let n = len / mem::size_of::<RawFd>();
let mut fds = Vec::with_capacity(n);
for i in 0..n {
let fdp = (p as *const RawFd).add(i);
fds.push(ptr::read_unaligned(fdp));
}
ControlMessageOwned::ScmRights(fds)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
(libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
let cred: libc::ucred = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmCredentials(cred.into())
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
(libc::SOL_SOCKET, libc::SCM_CREDS) => {
let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmCreds(cred.into())
}
#[cfg(not(any(target_os = "aix", target_os = "haiku")))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
let tv: libc::timeval = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
},
#[cfg(any(target_os = "android", target_os = "linux"))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
let ts: libc::timespec = ptr::read_unaligned(p as *const _);
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
let tp = p as *const libc::timespec;
let ts: libc::timespec = ptr::read_unaligned(tp);
let system = TimeSpec::from(ts);
let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
let hw_trans = TimeSpec::from(ts);
let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
let hw_raw = TimeSpec::from(ts);
let timestamping = Timestamps { system, hw_trans, hw_raw };
ControlMessageOwned::ScmTimestampsns(timestamping)
}
#[cfg(any(
target_os = "android",
target_os = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos"
))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
ControlMessageOwned::Ipv6PacketInfo(info)
}
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_PKTINFO) => {
let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
ControlMessageOwned::Ipv4PacketInfo(info)
}
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVIF) => {
let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
ControlMessageOwned::Ipv4RecvIf(dl)
},
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
let dl = ptr::read_unaligned(p as *const libc::in_addr);
ControlMessageOwned::Ipv4RecvDstAddr(dl)
},
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
ControlMessageOwned::Ipv4OrigDstAddr(dl)
},
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
(libc::SOL_UDP, libc::UDP_GRO) => {
let gso_size: u16 = ptr::read_unaligned(p as *const _);
ControlMessageOwned::UdpGroSegments(gso_size)
},
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
(libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
let drop_counter = ptr::read_unaligned(p as *const u32);
ControlMessageOwned::RxqOvfl(drop_counter)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVERR) => {
let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
ControlMessageOwned::Ipv4RecvErr(err, addr)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
ControlMessageOwned::Ipv6RecvErr(err, addr)
},
#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
ControlMessageOwned::Ipv6OrigDstAddr(dl)
},
(_, _) => {
let sl = std::slice::from_raw_parts(p, len);
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
ControlMessageOwned::Unknown(ucmsg)
}
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg(feature = "net")]
#[allow(clippy::cast_ptr_alignment)] unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
let ee = p as *const libc::sock_extended_err;
let err = ptr::read_unaligned(ee);
let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
if addrp.offset(1) as usize - (p as usize) > len {
(err, None)
} else {
(err, Some(ptr::read_unaligned(addrp)))
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ControlMessage<'a> {
ScmRights(&'a [RawFd]),
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ScmCredentials(&'a UnixCredentials),
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
ScmCreds,
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
AlgSetIv(&'a [u8]),
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
AlgSetOp(&'a libc::c_int),
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
#[cfg_attr(docsrs, doc(cfg(all())))]
AlgSetAeadAssoclen(&'a u32),
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
UdpGsoSegments(&'a u16),
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4PacketInfo(&'a libc::in_pktinfo),
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "freebsd",
target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),
#[cfg(any(
target_os = "netbsd",
target_os = "freebsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4SendSrcAddr(&'a libc::in_addr),
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
RxqOvfl(&'a u32),
#[cfg(target_os = "linux")]
TxTime(&'a u64),
}
#[doc(hidden)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct UnknownCmsg(cmsghdr, Vec<u8>);
impl<'a> ControlMessage<'a> {
fn space(&self) -> usize {
unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize}
}
#[cfg(any(target_os = "android",
all(target_os = "linux", not(target_env = "musl"))))]
fn cmsg_len(&self) -> usize {
unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize}
}
#[cfg(not(any(target_os = "android",
all(target_os = "linux", not(target_env = "musl")))))]
fn cmsg_len(&self) -> libc::c_uint {
unsafe{CMSG_LEN(self.len() as libc::c_uint)}
}
fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) {
let data_ptr = match *self {
ControlMessage::ScmRights(fds) => {
fds as *const _ as *const u8
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(creds) => {
&creds.0 as *const libc::ucred as *const u8
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ControlMessage::ScmCreds => {
unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
return
}
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(iv) => {
#[allow(deprecated)] let af_alg_iv = libc::af_alg_iv {
ivlen: iv.len() as u32,
iv: [0u8; 0],
};
let size = mem::size_of_val(&af_alg_iv);
unsafe {
ptr::copy_nonoverlapping(
&af_alg_iv as *const _ as *const u8,
cmsg_data,
size,
);
ptr::copy_nonoverlapping(
iv.as_ptr(),
cmsg_data.add(size),
iv.len()
);
};
return
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetOp(op) => {
op as *const _ as *const u8
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetAeadAssoclen(len) => {
len as *const _ as *const u8
},
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(gso_size) => {
gso_size as *const _ as *const u8
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(drop_count) => {
drop_count as *const _ as *const u8
},
#[cfg(target_os = "linux")]
ControlMessage::TxTime(tx_time) => {
tx_time as *const _ as *const u8
},
};
unsafe {
ptr::copy_nonoverlapping(
data_ptr,
cmsg_data,
self.len()
)
};
}
fn len(&self) -> usize {
match *self {
ControlMessage::ScmRights(fds) => {
mem::size_of_val(fds)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(creds) => {
mem::size_of_val(creds)
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ControlMessage::ScmCreds => {
mem::size_of::<libc::cmsgcred>()
}
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(iv) => {
mem::size_of::<&[u8]>() + iv.len()
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetOp(op) => {
mem::size_of_val(op)
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetAeadAssoclen(len) => {
mem::size_of_val(len)
},
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(gso_size) => {
mem::size_of_val(gso_size)
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(drop_count) => {
mem::size_of_val(drop_count)
},
#[cfg(target_os = "linux")]
ControlMessage::TxTime(tx_time) => {
mem::size_of_val(tx_time)
},
}
}
fn cmsg_level(&self) -> libc::c_int {
match *self {
ControlMessage::ScmRights(_) => libc::SOL_SOCKET,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ControlMessage::ScmCreds => libc::SOL_SOCKET,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
#[cfg(target_os = "linux")]
ControlMessage::TxTime(_) => libc::SOL_SOCKET,
}
}
fn cmsg_type(&self) -> libc::c_int {
match *self {
ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ControlMessage::ScmCreds => libc::SCM_CREDS,
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetIv(_) => {
libc::ALG_SET_IV
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetOp(_) => {
libc::ALG_SET_OP
},
#[cfg(any(target_os = "android", target_os = "linux"))]
ControlMessage::AlgSetAeadAssoclen(_) => {
libc::ALG_SET_AEAD_ASSOCLEN
},
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(_) => {
libc::UDP_SEGMENT
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "freebsd",
target_os = "android", target_os = "ios",))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
#[cfg(any(target_os = "netbsd", target_os = "freebsd",
target_os = "openbsd", target_os = "dragonfly"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
ControlMessage::RxqOvfl(_) => {
libc::SO_RXQ_OVFL
},
#[cfg(target_os = "linux")]
ControlMessage::TxTime(_) => {
libc::SCM_TXTIME
},
}
}
unsafe fn encode_into(&self, cmsg: *mut cmsghdr) {
(*cmsg).cmsg_level = self.cmsg_level();
(*cmsg).cmsg_type = self.cmsg_type();
(*cmsg).cmsg_len = self.cmsg_len();
self.copy_to_cmsg_data(CMSG_DATA(cmsg));
}
}
pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
flags: MsgFlags, addr: Option<&S>) -> Result<usize>
where S: SockaddrLike
{
let capacity = cmsgs.iter().map(|c| c.space()).sum();
let mut cmsg_buffer = vec![0u8; capacity];
let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn sendmmsg<'a, XS, AS, C, I, S>(
fd: RawFd,
data: &'a mut MultiHeaders<S>,
slices: XS,
addrs: AS,
cmsgs: C,
flags: MsgFlags
) -> crate::Result<MultiResults<'a, S>>
where
XS: IntoIterator<Item = &'a I>,
AS: AsRef<[Option<S>]>,
I: AsRef<[IoSlice<'a>]> + 'a,
C: AsRef<[ControlMessage<'a>]> + 'a,
S: SockaddrLike + 'a
{
let mut count = 0;
for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
let p = &mut mmsghdr.msg_hdr;
p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
p.msg_iovlen = slice.as_ref().len() as _;
p.msg_namelen = addr.as_ref().map_or(0, S::len);
p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
for cmsg in cmsgs.as_ref() {
assert_ne!(pmhdr, ptr::null_mut());
unsafe { cmsg.encode_into(pmhdr) };
pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
}
count = i+1;
}
let sent = Errno::result(unsafe {
libc::sendmmsg(
fd,
data.items.as_mut_ptr(),
count as _,
flags.bits() as _
)
})? as usize;
Ok(MultiResults {
rmm: data,
current_index: 0,
received: sent
})
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
#[derive(Debug)]
pub struct MultiHeaders<S> {
items: Box<[libc::mmsghdr]>,
addresses: Box<[mem::MaybeUninit<S>]>,
_cmsg_buffers: Option<Box<[u8]>>,
msg_controllen: usize,
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
impl<S> MultiHeaders<S> {
pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
where
S: Copy + SockaddrLike,
{
let mut addresses = vec![std::mem::MaybeUninit::<S>::uninit(); num_slices].into_boxed_slice();
let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
let mut cmsg_buffers =
cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
let items = addresses
.iter_mut()
.enumerate()
.map(|(ix, address)| {
let (ptr, cap) = match &mut cmsg_buffers {
Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen),
None => (std::ptr::null_mut(), 0),
};
let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) };
libc::mmsghdr {
msg_hdr,
msg_len: 0,
}
})
.collect::<Vec<_>>();
Self {
items: items.into_boxed_slice(),
addresses,
_cmsg_buffers: cmsg_buffers,
msg_controllen,
}
}
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn recvmmsg<'a, XS, S, I>(
fd: RawFd,
data: &'a mut MultiHeaders<S>,
slices: XS,
flags: MsgFlags,
mut timeout: Option<crate::sys::time::TimeSpec>,
) -> crate::Result<MultiResults<'a, S>>
where
XS: IntoIterator<Item = &'a I>,
I: AsRef<[IoSliceMut<'a>]> + 'a,
{
let mut count = 0;
for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
let p = &mut mmsghdr.msg_hdr;
p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
p.msg_iovlen = slice.as_ref().len() as _;
count = i + 1;
}
let timeout_ptr = timeout
.as_mut()
.map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
let received = Errno::result(unsafe {
libc::recvmmsg(
fd,
data.items.as_mut_ptr(),
count as _,
flags.bits() as _,
timeout_ptr,
)
})? as usize;
Ok(MultiResults {
rmm: data,
current_index: 0,
received,
})
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
#[derive(Debug)]
pub struct MultiResults<'a, S> {
rmm: &'a MultiHeaders<S>,
current_index: usize,
received: usize,
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
impl<'a, S> Iterator for MultiResults<'a, S>
where
S: Copy + SockaddrLike,
{
type Item = RecvMsg<'a, 'a, S>;
#[allow(clippy::unnecessary_cast)]
fn next(&mut self) -> Option<Self::Item> {
if self.current_index >= self.received {
return None;
}
let mmsghdr = self.rmm.items[self.current_index];
let address = unsafe { self.rmm.addresses[self.current_index].assume_init() };
self.current_index += 1;
Some(unsafe {
read_mhdr(
mmsghdr.msg_hdr,
mmsghdr.msg_len as isize,
self.rmm.msg_controllen,
address,
)
})
}
}
impl<'a, S> RecvMsg<'_, 'a, S> {
pub fn iovs(&self) -> IoSliceIterator<'a> {
IoSliceIterator {
index: 0,
remaining: self.bytes,
slices: unsafe {
std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _)
},
}
}
}
#[derive(Debug)]
pub struct IoSliceIterator<'a> {
index: usize,
remaining: usize,
slices: &'a [IoSlice<'a>],
}
impl<'a> Iterator for IoSliceIterator<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.slices.len() {
return None;
}
let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())];
self.remaining -= slice.len();
self.index += 1;
if slice.is_empty() {
return None;
}
Some(slice)
}
}
#[cfg(target_os = "linux")]
#[cfg(test)]
mod test {
use crate::sys::socket::{AddressFamily, ControlMessageOwned};
use crate::*;
use std::str::FromStr;
use std::os::unix::io::AsRawFd;
#[cfg_attr(qemu, ignore)]
#[test]
fn test_recvmm2() -> crate::Result<()> {
use crate::sys::socket::{
sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
SockaddrIn, TimestampingFlag,
};
use std::io::{IoSlice, IoSliceMut};
let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
let ssock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::empty(),
None,
)?;
let rsock = socket(
AddressFamily::Inet,
SockType::Datagram,
SockFlag::SOCK_NONBLOCK,
None,
)?;
crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?;
setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?;
let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
let mut recv_buf = vec![0; 1024];
let mut recv_iovs = Vec::new();
let mut pkt_iovs = Vec::new();
for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
pkt_iovs.push(IoSliceMut::new(chunk));
if ix % 2 == 1 {
recv_iovs.push(pkt_iovs);
pkt_iovs = Vec::new();
}
}
drop(pkt_iovs);
let flags = MsgFlags::empty();
let iov1 = [IoSlice::new(&sbuf)];
let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?;
for rmsg in recv {
#[cfg(not(any(qemu, target_arch = "aarch64")))]
let mut saw_time = false;
let mut recvd = 0;
for cmsg in rmsg.cmsgs() {
if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
let ts = timestamps.system;
let sys_time =
crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
let diff = if ts > sys_time {
ts - sys_time
} else {
sys_time - ts
};
assert!(std::time::Duration::from(diff).as_secs() < 60);
#[cfg(not(any(qemu, target_arch = "aarch64")))]
{
saw_time = true;
}
}
}
#[cfg(not(any(qemu, target_arch = "aarch64")))]
assert!(saw_time);
for iov in rmsg.iovs() {
recvd += iov.len();
}
assert_eq!(recvd, 400);
}
Ok(())
}
}
unsafe fn read_mhdr<'a, 'i, S>(
mhdr: msghdr,
r: isize,
msg_controllen: usize,
mut address: S,
) -> RecvMsg<'a, 'i, S>
where S: SockaddrLike
{
#[allow(clippy::unnecessary_cast)]
let cmsghdr = {
if mhdr.msg_controllen > 0 {
debug_assert!(!mhdr.msg_control.is_null());
debug_assert!(msg_controllen >= mhdr.msg_controllen as usize);
CMSG_FIRSTHDR(&mhdr as *const msghdr)
} else {
ptr::null()
}.as_ref()
};
let _ = address.set_length(mhdr.msg_namelen as usize);
RecvMsg {
bytes: r as usize,
cmsghdr,
address: Some(address),
flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
mhdr,
iobufs: std::marker::PhantomData,
}
}
unsafe fn pack_mhdr_to_receive<S>(
iov_buffer: *mut IoSliceMut,
iov_buffer_len: usize,
cmsg_buffer: *mut u8,
cmsg_capacity: usize,
address: *mut S,
) -> msghdr
where
S: SockaddrLike
{
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
let p = mhdr.as_mut_ptr();
(*p).msg_name = address as *mut c_void;
(*p).msg_namelen = S::size();
(*p).msg_iov = iov_buffer as *mut iovec;
(*p).msg_iovlen = iov_buffer_len as _;
(*p).msg_control = cmsg_buffer as *mut c_void;
(*p).msg_controllen = cmsg_capacity as _;
(*p).msg_flags = 0;
mhdr.assume_init()
}
fn pack_mhdr_to_send<'a, I, C, S>(
cmsg_buffer: &mut [u8],
iov: I,
cmsgs: C,
addr: Option<&S>
) -> msghdr
where
I: AsRef<[IoSlice<'a>]>,
C: AsRef<[ControlMessage<'a>]>,
S: SockaddrLike + 'a
{
let capacity = cmsg_buffer.len();
let cmsg_ptr = if capacity > 0 {
cmsg_buffer.as_mut_ptr() as *mut c_void
} else {
ptr::null_mut()
};
let mhdr = unsafe {
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
let p = mhdr.as_mut_ptr();
(*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _;
(*p).msg_namelen = addr.map(S::len).unwrap_or(0);
(*p).msg_iov = iov.as_ref().as_ptr() as *mut _;
(*p).msg_iovlen = iov.as_ref().len() as _;
(*p).msg_control = cmsg_ptr;
(*p).msg_controllen = capacity as _;
(*p).msg_flags = 0;
mhdr.assume_init()
};
let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) };
for cmsg in cmsgs.as_ref() {
assert_ne!(pmhdr, ptr::null_mut());
unsafe { cmsg.encode_into(pmhdr) };
pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) };
}
mhdr
}
pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> Result<RecvMsg<'a, 'outer, S>>
where S: SockaddrLike + 'a,
'inner: 'outer
{
let mut address = mem::MaybeUninit::uninit();
let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
.map(|v| (v.as_mut_ptr(), v.capacity()))
.unwrap_or((ptr::null_mut(), 0));
let mut mhdr = unsafe {
pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
};
let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
let r = Errno::result(ret)?;
Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
}
}
pub fn socket<T: Into<Option<SockProtocol>>>(
domain: AddressFamily,
ty: SockType,
flags: SockFlag,
protocol: T,
) -> Result<OwnedFd> {
let protocol = match protocol.into() {
None => 0,
Some(p) => p as c_int,
};
let mut ty = ty as c_int;
ty |= flags.bits();
let res = unsafe { libc::socket(domain as c_int, ty, protocol) };
match res {
-1 => Err(Errno::last()),
fd => {
unsafe { Ok(OwnedFd::from_raw_fd(fd)) }
}
}
}
pub fn socketpair<T: Into<Option<SockProtocol>>>(
domain: AddressFamily,
ty: SockType,
protocol: T,
flags: SockFlag,
) -> Result<(OwnedFd, OwnedFd)> {
let protocol = match protocol.into() {
None => 0,
Some(p) => p as c_int,
};
let mut ty = ty as c_int;
ty |= flags.bits();
let mut fds = [-1, -1];
let res = unsafe {
libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr())
};
Errno::result(res)?;
unsafe {
Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])))
}
}
pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
let fd = sock.as_fd().as_raw_fd();
let res = unsafe { libc::listen(fd, backlog as c_int) };
Errno::result(res).map(drop)
}
pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
let res = unsafe { libc::bind(fd, addr.as_ptr(), addr.len()) };
Errno::result(res).map(drop)
}
pub fn accept(sockfd: RawFd) -> Result<RawFd> {
let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
Errno::result(res)
}
#[cfg(any(
all(
target_os = "android",
any(
target_arch = "aarch64",
target_arch = "x86",
target_arch = "x86_64"
)
),
target_os = "dragonfly",
target_os = "emscripten",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
))]
pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
let res = unsafe {
libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits())
};
Errno::result(res)
}
pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
let res = unsafe { libc::connect(fd, addr.as_ptr(), addr.len()) };
Errno::result(res).map(drop)
}
pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
unsafe {
let ret = libc::recv(
sockfd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as size_t,
flags.bits(),
);
Errno::result(ret).map(|r| r as usize)
}
}
pub fn recvfrom<T: SockaddrLike>(
sockfd: RawFd,
buf: &mut [u8],
) -> Result<(usize, Option<T>)> {
unsafe {
let mut addr = mem::MaybeUninit::<T>::uninit();
let mut len = mem::size_of_val(&addr) as socklen_t;
let ret = Errno::result(libc::recvfrom(
sockfd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as size_t,
0,
addr.as_mut_ptr() as *mut sockaddr,
&mut len as *mut socklen_t,
))? as usize;
Ok((
ret,
T::from_raw(
addr.assume_init().as_ptr(),
Some(len),
),
))
}
}
pub fn sendto(
fd: RawFd,
buf: &[u8],
addr: &dyn SockaddrLike,
flags: MsgFlags,
) -> Result<usize> {
let ret = unsafe {
libc::sendto(
fd,
buf.as_ptr() as *const c_void,
buf.len() as size_t,
flags.bits(),
addr.as_ptr(),
addr.len(),
)
};
Errno::result(ret).map(|r| r as usize)
}
pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
let ret = unsafe {
libc::send(
fd,
buf.as_ptr() as *const c_void,
buf.len() as size_t,
flags.bits(),
)
};
Errno::result(ret).map(|r| r as usize)
}
pub trait GetSockOpt: Copy {
type Val;
fn get<F: AsFd>(&self, fd: &F) -> Result<Self::Val>;
}
pub trait SetSockOpt: Clone {
type Val;
fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()>;
}
pub fn getsockopt<F: AsFd, O: GetSockOpt>(fd: &F, opt: O) -> Result<O::Val> {
opt.get(fd)
}
pub fn setsockopt<F: AsFd, O: SetSockOpt>(
fd: &F,
opt: O,
val: &O::Val,
) -> Result<()> {
opt.set(fd, val)
}
pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> {
unsafe {
let mut addr = mem::MaybeUninit::<T>::uninit();
let mut len = T::size();
let ret =
libc::getpeername(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
Errno::result(ret)?;
T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
}
}
pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> {
unsafe {
let mut addr = mem::MaybeUninit::<T>::uninit();
let mut len = T::size();
let ret =
libc::getsockname(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
Errno::result(ret)?;
T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Shutdown {
Read,
Write,
Both,
}
pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
unsafe {
use libc::shutdown;
let how = match how {
Shutdown::Read => libc::SHUT_RD,
Shutdown::Write => libc::SHUT_WR,
Shutdown::Both => libc::SHUT_RDWR,
};
Errno::result(shutdown(df, how)).map(drop)
}
}
#[cfg(test)]
mod tests {
#[cfg(not(target_os = "redox"))]
#[test]
fn can_use_cmsg_space() {
let _ = cmsg_space!(u8);
}
#[cfg(not(any(
target_os = "redox",
target_os = "linux",
target_os = "android"
)))]
#[test]
fn can_open_routing_socket() {
let _ = super::socket(
super::AddressFamily::Route,
super::SockType::Raw,
super::SockFlag::empty(),
None,
)
.expect("Failed to open routing socket");
}
}