use cfg_if::cfg_if;
use crate::{Error, Result, errno::Errno};
use libc::{self, c_void, c_int, iovec, socklen_t, size_t,
CMSG_FIRSTHDR, CMSG_NXTHDR, CMSG_DATA, CMSG_LEN};
use std::{mem, ptr, slice};
use std::os::unix::io::RawFd;
use crate::sys::time::TimeVal;
use crate::sys::uio::IoVec;
mod addr;
pub mod sockopt;
pub use self::addr::{
AddressFamily,
SockAddr,
InetAddr,
UnixAddr,
IpAddr,
Ipv4Addr,
Ipv6Addr,
LinkAddr,
};
#[cfg(any(target_os = "android", target_os = "linux"))]
pub use crate::sys::socket::addr::netlink::NetlinkAddr;
#[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::vsock::VsockAddr;
pub use libc::{
cmsghdr,
msghdr,
sa_family_t,
sockaddr,
sockaddr_in,
sockaddr_in6,
sockaddr_storage,
sockaddr_un,
};
#[doc(hidden)]
pub use libc::{c_uint, CMSG_SPACE};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(i32)]
pub enum SockType {
Stream = libc::SOCK_STREAM,
Datagram = libc::SOCK_DGRAM,
SeqPacket = libc::SOCK_SEQPACKET,
Raw = libc::SOCK_RAW,
Rdm = libc::SOCK_RDM,
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SockProtocol {
Tcp = libc::IPPROTO_TCP,
Udp = libc::IPPROTO_UDP,
#[cfg(any(target_os = "ios", target_os = "macos"))]
KextEvent = libc::SYSPROTO_EVENT,
#[cfg(any(target_os = "ios", target_os = "macos"))]
KextControl = libc::SYSPROTO_CONTROL,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkRoute = libc::NETLINK_ROUTE,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkUserSock = libc::NETLINK_USERSOCK,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkSELinux = libc::NETLINK_SELINUX,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkISCSI = libc::NETLINK_ISCSI,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkAudit = libc::NETLINK_AUDIT,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkNetFilter = libc::NETLINK_NETFILTER,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkRDMA = libc::NETLINK_RDMA,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
#[cfg(any(target_os = "android", target_os = "linux"))]
NetlinkCrypto = libc::NETLINK_CRYPTO,
}
libc_bitflags!{
pub struct SockFlag: c_int {
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
SOCK_NONBLOCK;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
SOCK_CLOEXEC;
#[cfg(target_os = "netbsd")]
SOCK_NOSIGPIPE;
#[cfg(target_os = "openbsd")]
SOCK_DNS;
}
}
libc_bitflags!{
pub struct MsgFlags: c_int {
MSG_OOB;
MSG_PEEK;
MSG_WAITALL;
MSG_DONTWAIT;
MSG_CTRUNC;
MSG_TRUNC;
MSG_EOR;
#[cfg(any(target_os = "android", target_os = "linux"))]
MSG_ERRQUEUE;
#[cfg(any(target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"))]
MSG_CMSG_CLOEXEC;
}
}
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 {
UnixCredentials(libc::ucred {
pid: crate::unistd::getpid().as_raw(),
uid: crate::unistd::getuid().as_raw(),
gid: crate::unistd::getgid().as_raw(),
})
}
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 Into<libc::ucred> for UnixCredentials {
fn into(self) -> libc::ucred {
self.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 { 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)
}
}
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct IpMembershipRequest(libc::ip_mreq);
impl IpMembershipRequest {
pub fn new(group: Ipv4Addr, interface: Option<Ipv4Addr>) -> Self {
IpMembershipRequest(libc::ip_mreq {
imr_multiaddr: group.0,
imr_interface: interface.unwrap_or_else(Ipv4Addr::any).0,
})
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
impl Ipv6MembershipRequest {
pub fn new(group: Ipv6Addr) -> Self {
Ipv6MembershipRequest(libc::ipv6_mreq {
ipv6mr_multiaddr: group.0,
ipv6mr_interface: 0,
})
}
}
#[macro_export]
macro_rules! cmsg_space {
( $( $x:ty ),* ) => {
{
use nix::sys::socket::{c_uint, CMSG_SPACE};
use std::mem;
let mut space = 0;
$(
space += unsafe {
CMSG_SPACE(mem::size_of::<$x>() as c_uint)
} as usize;
)*
Vec::<u8>::with_capacity(space)
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RecvMsg<'a> {
pub bytes: usize,
cmsghdr: Option<&'a cmsghdr>,
pub address: Option<SockAddr>,
pub flags: MsgFlags,
mhdr: msghdr,
}
impl<'a> RecvMsg<'a> {
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)]
pub enum ControlMessageOwned {
ScmRights(Vec<RawFd>),
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(UnixCredentials),
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ScmCreds(UnixCredentials),
ScmTimestamp(TimeVal),
#[cfg(any(
target_os = "android",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
))]
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",
))]
Ipv6PacketInfo(libc::in6_pktinfo),
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
Ipv4RecvIf(libc::sockaddr_dl),
#[cfg(any(
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
Ipv4RecvDstAddr(libc::in_addr),
#[cfg(target_os = "linux")]
UdpGroSegments(u16),
#[doc(hidden)]
Unknown(UnknownCmsg),
}
impl ControlMessageOwned {
#[allow(clippy::cast_ptr_alignment)]
unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
{
let p = CMSG_DATA(header);
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())
}
(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 = "freebsd",
target_os = "ios",
target_os = "linux",
target_os = "macos"
))]
(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",
))]
(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",
))]
(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",
))]
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
let dl = ptr::read_unaligned(p as *const libc::in_addr);
ControlMessageOwned::Ipv4RecvDstAddr(dl)
},
#[cfg(target_os = "linux")]
(libc::SOL_UDP, libc::UDP_GRO) => {
let gso_size: u16 = ptr::read_unaligned(p as *const _);
ControlMessageOwned::UdpGroSegments(gso_size)
},
(_, _) => {
let sl = slice::from_raw_parts(p, len);
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(&sl[..]));
ControlMessageOwned::Unknown(ucmsg)
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ControlMessage<'a> {
ScmRights(&'a [RawFd]),
#[cfg(any(target_os = "android", target_os = "linux"))]
ScmCredentials(&'a UnixCredentials),
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
ScmCreds,
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
AlgSetIv(&'a [u8]),
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
AlgSetOp(&'a libc::c_int),
#[cfg(any(
target_os = "android",
target_os = "linux",
))]
AlgSetAeadAssoclen(&'a u32),
#[cfg(target_os = "linux")]
UdpGsoSegments(&'a u16),
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "android",
target_os = "ios",))]
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",))]
Ipv6PacketInfo(&'a libc::in6_pktinfo),
}
#[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) => {
let af_alg_iv = libc::af_alg_iv {
ivlen: iv.len() as u32,
iv: [0u8; 0],
};
let size = mem::size_of::<libc::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")]
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",))]
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",))]
ControlMessage::Ipv6PacketInfo(info) => info 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::<libc::af_alg_iv>() + 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")]
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",))]
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",))]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
}
}
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")]
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
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",))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
}
}
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")]
ControlMessage::UdpGsoSegments(_) => {
libc::UDP_SEGMENT
},
#[cfg(any(target_os = "linux", target_os = "macos",
target_os = "netbsd", target_os = "android",
target_os = "ios",))]
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",))]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
}
}
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(fd: RawFd, iov: &[IoVec<&[u8]>], cmsgs: &[ControlMessage],
flags: MsgFlags, addr: Option<&SockAddr>) -> Result<usize>
{
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",
))]
#[derive(Debug)]
pub struct SendMmsgData<'a, I, C>
where
I: AsRef<[IoVec<&'a [u8]>]>,
C: AsRef<[ControlMessage<'a>]>
{
pub iov: I,
pub cmsgs: C,
pub addr: Option<SockAddr>,
pub _lt: std::marker::PhantomData<&'a I>,
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn sendmmsg<'a, I, C>(
fd: RawFd,
data: impl std::iter::IntoIterator<Item=&'a SendMmsgData<'a, I, C>>,
flags: MsgFlags
) -> Result<Vec<usize>>
where
I: AsRef<[IoVec<&'a [u8]>]> + 'a,
C: AsRef<[ControlMessage<'a>]> + 'a,
{
let iter = data.into_iter();
let size_hint = iter.size_hint();
let reserve_items = size_hint.1.unwrap_or(size_hint.0);
let mut output = Vec::<libc::mmsghdr>::with_capacity(reserve_items);
let mut cmsgs_buffer = vec![0u8; 0];
for d in iter {
let cmsgs_start = cmsgs_buffer.len();
let cmsgs_required_capacity: usize = d.cmsgs.as_ref().iter().map(|c| c.space()).sum();
let cmsgs_buffer_need_capacity = cmsgs_start + cmsgs_required_capacity;
cmsgs_buffer.resize(cmsgs_buffer_need_capacity, 0);
output.push(libc::mmsghdr {
msg_hdr: pack_mhdr_to_send(
&mut cmsgs_buffer[cmsgs_start..],
&d.iov,
&d.cmsgs,
d.addr.as_ref()
),
msg_len: 0,
});
};
let ret = unsafe { libc::sendmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _) };
let sent_messages = Errno::result(ret)? as usize;
let mut sent_bytes = Vec::with_capacity(sent_messages);
for item in &output {
sent_bytes.push(item.msg_len as usize);
}
Ok(sent_bytes)
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
#[derive(Debug)]
pub struct RecvMmsgData<'a, I>
where
I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
{
pub iov: I,
pub cmsg_buffer: Option<&'a mut Vec<u8>>,
}
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn recvmmsg<'a, I>(
fd: RawFd,
data: impl std::iter::IntoIterator<Item=&'a mut RecvMmsgData<'a, I>,
IntoIter=impl ExactSizeIterator + Iterator<Item=&'a mut RecvMmsgData<'a, I>>>,
flags: MsgFlags,
timeout: Option<crate::sys::time::TimeSpec>
) -> Result<Vec<RecvMsg<'a>>>
where
I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
{
let iter = data.into_iter();
let num_messages = iter.len();
let mut output: Vec<libc::mmsghdr> = Vec::with_capacity(num_messages);
let mut addresses = vec![mem::MaybeUninit::uninit(); num_messages]
.into_boxed_slice();
let results: Vec<_> = iter.enumerate().map(|(i, d)| {
let (msg_controllen, mhdr) = unsafe {
pack_mhdr_to_receive(
d.iov.as_ref(),
&mut d.cmsg_buffer,
addresses[i].as_mut_ptr(),
)
};
output.push(
libc::mmsghdr {
msg_hdr: mhdr,
msg_len: 0,
}
);
(msg_controllen as usize, &mut d.cmsg_buffer)
}).collect();
let timeout = if let Some(mut t) = timeout {
t.as_mut() as *mut libc::timespec
} else {
ptr::null_mut()
};
let ret = unsafe { libc::recvmmsg(fd, output.as_mut_ptr(), output.len() as _, flags.bits() as _, timeout) };
let r = Errno::result(ret)?;
Ok(output
.into_iter()
.zip(addresses.iter().map(|addr| unsafe{addr.assume_init()}))
.zip(results.into_iter())
.map(|((mmsghdr, address), (msg_controllen, cmsg_buffer))| {
unsafe {
read_mhdr(
mmsghdr.msg_hdr,
r as isize,
msg_controllen,
address,
cmsg_buffer
)
}
})
.collect())
}
unsafe fn read_mhdr<'a, 'b>(
mhdr: msghdr,
r: isize,
msg_controllen: usize,
address: sockaddr_storage,
cmsg_buffer: &'a mut Option<&'b mut Vec<u8>>
) -> RecvMsg<'b> {
let cmsghdr = {
if mhdr.msg_controllen > 0 {
cmsg_buffer
.as_mut()
.unwrap()
.set_len(mhdr.msg_controllen as usize);
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 = sockaddr_storage_to_addr(
&address ,
mhdr.msg_namelen as usize
).ok();
RecvMsg {
bytes: r as usize,
cmsghdr,
address,
flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
mhdr,
}
}
unsafe fn pack_mhdr_to_receive<'a, I>(
iov: I,
cmsg_buffer: &mut Option<&mut Vec<u8>>,
address: *mut sockaddr_storage,
) -> (usize, msghdr)
where
I: AsRef<[IoVec<&'a mut [u8]>]> + 'a,
{
let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
.map(|v| (v.as_mut_ptr(), v.capacity()))
.unwrap_or((ptr::null_mut(), 0));
let mhdr = {
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
let p = mhdr.as_mut_ptr();
(*p).msg_name = address as *mut c_void;
(*p).msg_namelen = mem::size_of::<sockaddr_storage>() as socklen_t;
(*p).msg_iov = iov.as_ref().as_ptr() as *mut iovec;
(*p).msg_iovlen = iov.as_ref().len() as _;
(*p).msg_control = msg_control as *mut c_void;
(*p).msg_controllen = msg_controllen as _;
(*p).msg_flags = 0;
mhdr.assume_init()
};
(msg_controllen, mhdr)
}
fn pack_mhdr_to_send<'a, I, C>(
cmsg_buffer: &mut [u8],
iov: I,
cmsgs: C,
addr: Option<&SockAddr>
) -> msghdr
where
I: AsRef<[IoVec<&'a [u8]>]>,
C: AsRef<[ControlMessage<'a>]>
{
let capacity = cmsg_buffer.len();
let (name, namelen) = match addr {
Some(addr) => {
let (x, y) = addr.as_ffi_pair();
(x as *const _, y)
},
None => (ptr::null(), 0),
};
let cmsg_ptr = if capacity > 0 {
cmsg_buffer.as_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 = name as *mut _;
(*p).msg_namelen = namelen;
(*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>(fd: RawFd, iov: &[IoVec<&mut [u8]>],
mut cmsg_buffer: Option<&'a mut Vec<u8>>,
flags: MsgFlags) -> Result<RecvMsg<'a>>
{
let mut address = mem::MaybeUninit::uninit();
let (msg_controllen, mut mhdr) = unsafe {
pack_mhdr_to_receive(&iov, &mut cmsg_buffer, 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(), &mut cmsg_buffer) })
}
pub fn socket<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T) -> Result<RawFd> {
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) };
Errno::result(res)
}
pub fn socketpair<T: Into<Option<SockProtocol>>>(domain: AddressFamily, ty: SockType, protocol: T,
flags: SockFlag) -> Result<(RawFd, RawFd)> {
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)?;
Ok((fds[0], fds[1]))
}
pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
let res = unsafe { libc::listen(sockfd, backlog as c_int) };
Errno::result(res).map(drop)
}
pub fn bind(fd: RawFd, addr: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
libc::bind(fd, ptr, 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(target_os = "android",
target_os = "freebsd",
target_os = "linux",
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: &SockAddr) -> Result<()> {
let res = unsafe {
let (ptr, len) = addr.as_ffi_pair();
libc::connect(fd, ptr, 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_ptr() as *mut c_void,
buf.len() as size_t,
flags.bits());
Errno::result(ret).map(|r| r as usize)
}
}
pub fn recvfrom(sockfd: RawFd, buf: &mut [u8])
-> Result<(usize, Option<SockAddr>)>
{
unsafe {
let mut addr: sockaddr_storage = mem::zeroed();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = Errno::result(libc::recvfrom(
sockfd,
buf.as_ptr() as *mut c_void,
buf.len() as size_t,
0,
&mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
&mut len as *mut socklen_t))? as usize;
match sockaddr_storage_to_addr(&addr, len as usize) {
Err(Error::Sys(Errno::ENOTCONN)) => Ok((ret, None)),
Ok(addr) => Ok((ret, Some(addr))),
Err(e) => Err(e)
}
}
}
pub fn sendto(fd: RawFd, buf: &[u8], addr: &SockAddr, flags: MsgFlags) -> Result<usize> {
let ret = unsafe {
let (ptr, len) = addr.as_ffi_pair();
libc::sendto(fd, buf.as_ptr() as *const c_void, buf.len() as size_t, flags.bits(), ptr, 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)
}
#[repr(i32)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum SockLevel {
Socket = libc::SOL_SOCKET,
Tcp = libc::IPPROTO_TCP,
Ip = libc::IPPROTO_IP,
Ipv6 = libc::IPPROTO_IPV6,
Udp = libc::IPPROTO_UDP,
#[cfg(any(target_os = "android", target_os = "linux"))]
Netlink = libc::SOL_NETLINK,
#[cfg(any(target_os = "android", target_os = "linux"))]
Alg = libc::SOL_ALG,
}
pub trait GetSockOpt : Copy {
type Val;
#[doc(hidden)]
fn get(&self, fd: RawFd) -> Result<Self::Val>;
}
pub trait SetSockOpt : Clone {
type Val;
#[doc(hidden)]
fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
}
pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> {
opt.get(fd)
}
pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> {
opt.set(fd, val)
}
pub fn getpeername(fd: RawFd) -> Result<SockAddr> {
unsafe {
let mut addr = mem::MaybeUninit::uninit();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = libc::getpeername(
fd,
addr.as_mut_ptr() as *mut libc::sockaddr,
&mut len
);
Errno::result(ret)?;
sockaddr_storage_to_addr(&addr.assume_init(), len as usize)
}
}
pub fn getsockname(fd: RawFd) -> Result<SockAddr> {
unsafe {
let mut addr = mem::MaybeUninit::uninit();
let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
let ret = libc::getsockname(
fd,
addr.as_mut_ptr() as *mut libc::sockaddr,
&mut len
);
Errno::result(ret)?;
sockaddr_storage_to_addr(&addr.assume_init(), len as usize)
}
}
pub fn sockaddr_storage_to_addr(
addr: &sockaddr_storage,
len: usize) -> Result<SockAddr> {
assert!(len <= mem::size_of::<sockaddr_un>());
if len < mem::size_of_val(&addr.ss_family) {
return Err(Error::Sys(Errno::ENOTCONN));
}
match c_int::from(addr.ss_family) {
libc::AF_INET => {
assert_eq!(len as usize, mem::size_of::<sockaddr_in>());
let sin = unsafe {
*(addr as *const sockaddr_storage as *const sockaddr_in)
};
Ok(SockAddr::Inet(InetAddr::V4(sin)))
}
libc::AF_INET6 => {
assert_eq!(len as usize, mem::size_of::<sockaddr_in6>());
let sin6 = unsafe {
*(addr as *const _ as *const sockaddr_in6)
};
Ok(SockAddr::Inet(InetAddr::V6(sin6)))
}
libc::AF_UNIX => {
let pathlen = len - offset_of!(sockaddr_un, sun_path);
let sun = unsafe {
*(addr as *const _ as *const sockaddr_un)
};
Ok(SockAddr::Unix(UnixAddr(sun, pathlen)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_NETLINK => {
use libc::sockaddr_nl;
let snl = unsafe {
*(addr as *const _ as *const sockaddr_nl)
};
Ok(SockAddr::Netlink(NetlinkAddr(snl)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_ALG => {
use libc::sockaddr_alg;
let salg = unsafe {
*(addr as *const _ as *const sockaddr_alg)
};
Ok(SockAddr::Alg(AlgAddr(salg)))
}
#[cfg(any(target_os = "android", target_os = "linux"))]
libc::AF_VSOCK => {
use libc::sockaddr_vm;
let svm = unsafe {
*(addr as *const _ as *const sockaddr_vm)
};
Ok(SockAddr::Vsock(VsockAddr(svm)))
}
af => panic!("unexpected address family {}", af),
}
}
#[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)
}
}