mirror of
https://github.com/jiangcuo/nix.git
synced 2026-08-11 06:39:08 +00:00
2403 lines
87 KiB
Rust
2403 lines
87 KiB
Rust
//! Socket interface functions
|
|
//!
|
|
//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
|
|
#[cfg(target_os = "linux")]
|
|
#[cfg(feature = "uio")]
|
|
use crate::sys::time::TimeSpec;
|
|
#[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, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
|
|
CMSG_LEN, CMSG_NXTHDR,
|
|
};
|
|
use std::io::{IoSlice, IoSliceMut};
|
|
#[cfg(feature = "net")]
|
|
use std::net;
|
|
use std::os::unix::io::RawFd;
|
|
use std::{mem, ptr, slice};
|
|
|
|
#[deny(missing_docs)]
|
|
mod addr;
|
|
#[deny(missing_docs)]
|
|
pub mod sockopt;
|
|
|
|
/*
|
|
*
|
|
* ===== Re-exports =====
|
|
*
|
|
*/
|
|
|
|
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"
|
|
)))]
|
|
#[cfg(feature = "net")]
|
|
pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
|
|
#[cfg(any(
|
|
target_os = "illumos",
|
|
target_os = "solaris",
|
|
target_os = "haiku"
|
|
))]
|
|
#[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"))]
|
|
pub use crate::sys::socket::addr::vsock::VsockAddr;
|
|
|
|
#[cfg(feature = "uio")]
|
|
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};
|
|
|
|
// Needed by the cmsg_space macro
|
|
#[doc(hidden)]
|
|
pub use libc::{c_uint, CMSG_SPACE};
|
|
|
|
#[cfg(feature = "net")]
|
|
use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
|
|
|
|
/// These constants are used to specify the communication semantics
|
|
/// when creating a socket with [`socket()`](fn.socket.html)
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
#[repr(i32)]
|
|
#[non_exhaustive]
|
|
pub enum SockType {
|
|
/// Provides sequenced, reliable, two-way, connection-
|
|
/// based byte streams. An out-of-band data transmission
|
|
/// mechanism may be supported.
|
|
Stream = libc::SOCK_STREAM,
|
|
/// Supports datagrams (connectionless, unreliable
|
|
/// messages of a fixed maximum length).
|
|
Datagram = libc::SOCK_DGRAM,
|
|
/// Provides a sequenced, reliable, two-way connection-
|
|
/// based data transmission path for datagrams of fixed
|
|
/// maximum length; a consumer is required to read an
|
|
/// entire packet with each input system call.
|
|
SeqPacket = libc::SOCK_SEQPACKET,
|
|
/// Provides raw network protocol access.
|
|
Raw = libc::SOCK_RAW,
|
|
/// Provides a reliable datagram layer that does not
|
|
/// guarantee ordering.
|
|
#[cfg(not(any(target_os = "haiku")))]
|
|
Rdm = libc::SOCK_RDM,
|
|
}
|
|
// The TryFrom impl could've been derived using libc_enum!. But for
|
|
// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
|
|
// keep the old variant names.
|
|
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),
|
|
libc::SOCK_RAW => Ok(Self::Raw),
|
|
#[cfg(not(any(target_os = "haiku")))]
|
|
libc::SOCK_RDM => Ok(Self::Rdm),
|
|
_ => Err(Errno::EINVAL),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
|
|
/// to specify the protocol to use.
|
|
#[repr(i32)]
|
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
|
#[non_exhaustive]
|
|
pub enum SockProtocol {
|
|
/// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
|
|
Tcp = libc::IPPROTO_TCP,
|
|
/// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
|
|
Udp = libc::IPPROTO_UDP,
|
|
/// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html))
|
|
Raw = libc::IPPROTO_RAW,
|
|
/// Allows applications and other KEXTs to be notified when certain kernel events occur
|
|
/// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
|
|
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
KextEvent = libc::SYSPROTO_EVENT,
|
|
/// Allows applications to configure and control a KEXT
|
|
/// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
|
|
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
KextControl = libc::SYSPROTO_CONTROL,
|
|
/// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link
|
|
// parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkRoute = libc::NETLINK_ROUTE,
|
|
/// Reserved for user-mode socket protocols
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkUserSock = libc::NETLINK_USERSOCK,
|
|
/// Query information about sockets of various protocol families from the kernel
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
|
|
/// SELinux event notifications.
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkSELinux = libc::NETLINK_SELINUX,
|
|
/// Open-iSCSI
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkISCSI = libc::NETLINK_ISCSI,
|
|
/// Auditing
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkAudit = libc::NETLINK_AUDIT,
|
|
/// Access to FIB lookup from user space
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
|
|
/// Netfilter subsystem
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkNetFilter = libc::NETLINK_NETFILTER,
|
|
/// SCSI Transports
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
|
|
/// Infiniband RDMA
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkRDMA = libc::NETLINK_RDMA,
|
|
/// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module.
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
|
|
/// DECnet routing messages
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
|
|
/// Kernel messages to user space
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
|
|
/// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow
|
|
/// configuration of the kernel crypto API.
|
|
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
NetlinkCrypto = libc::NETLINK_CRYPTO,
|
|
/// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols
|
|
/// defined in the interface to be received.
|
|
/// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html))
|
|
// The protocol number is fed into the socket syscall in network byte order.
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
EthAll = libc::ETH_P_ALL.to_be(),
|
|
}
|
|
|
|
#[cfg(any(target_os = "linux"))]
|
|
libc_bitflags! {
|
|
/// Configuration flags for `SO_TIMESTAMPING` interface
|
|
///
|
|
/// For use with [`Timestamping`][sockopt::Timestamping].
|
|
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
|
|
pub struct TimestampingFlag: c_uint {
|
|
/// Report any software timestamps when available.
|
|
SOF_TIMESTAMPING_SOFTWARE;
|
|
/// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
|
|
SOF_TIMESTAMPING_RAW_HARDWARE;
|
|
/// Collect transmiting timestamps as reported by hardware
|
|
SOF_TIMESTAMPING_TX_HARDWARE;
|
|
/// Collect transmiting timestamps as reported by software
|
|
SOF_TIMESTAMPING_TX_SOFTWARE;
|
|
/// Collect receiving timestamps as reported by hardware
|
|
SOF_TIMESTAMPING_RX_HARDWARE;
|
|
/// Collect receiving timestamps as reported by software
|
|
SOF_TIMESTAMPING_RX_SOFTWARE;
|
|
}
|
|
}
|
|
|
|
libc_bitflags! {
|
|
/// Additional socket options
|
|
pub struct SockFlag: c_int {
|
|
/// Set non-blocking mode on the new socket
|
|
#[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;
|
|
/// Set close-on-exec on the new descriptor
|
|
#[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;
|
|
/// Return `EPIPE` instead of raising `SIGPIPE`
|
|
#[cfg(target_os = "netbsd")]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
SOCK_NOSIGPIPE;
|
|
/// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)`
|
|
/// to the DNS port (typically 53)
|
|
#[cfg(target_os = "openbsd")]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
SOCK_DNS;
|
|
}
|
|
}
|
|
|
|
libc_bitflags! {
|
|
/// Flags for send/recv and their relatives
|
|
pub struct MsgFlags: c_int {
|
|
/// Sends or requests out-of-band data on sockets that support this notion
|
|
/// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also
|
|
/// support out-of-band data.
|
|
MSG_OOB;
|
|
/// Peeks at an incoming message. The data is treated as unread and the next
|
|
/// [`recv()`](fn.recv.html)
|
|
/// or similar function shall still return this data.
|
|
MSG_PEEK;
|
|
/// Receive operation blocks until the full amount of data can be
|
|
/// returned. The function may return smaller amount of data if a signal
|
|
/// is caught, an error or disconnect occurs.
|
|
MSG_WAITALL;
|
|
/// Enables nonblocking operation; if the operation would block,
|
|
/// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar
|
|
/// behavior to setting the `O_NONBLOCK` flag
|
|
/// (via the [`fcntl`](../../fcntl/fn.fcntl.html)
|
|
/// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per-
|
|
/// call option, whereas `O_NONBLOCK` is a setting on the open file
|
|
/// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)),
|
|
/// which will affect all threads in
|
|
/// the calling process and as well as other processes that hold
|
|
/// file descriptors referring to the same open file description.
|
|
MSG_DONTWAIT;
|
|
/// Receive flags: Control Data was discarded (buffer too small)
|
|
MSG_CTRUNC;
|
|
/// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram
|
|
/// (since Linux 2.4.27/2.6.8),
|
|
/// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4)
|
|
/// sockets: return the real length of the packet or datagram, even
|
|
/// when it was longer than the passed buffer. Not implemented for UNIX
|
|
/// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets.
|
|
///
|
|
/// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp).
|
|
MSG_TRUNC;
|
|
/// Terminates a record (when this notion is supported, as for
|
|
/// sockets of type [`SeqPacket`](enum.SockType.html)).
|
|
MSG_EOR;
|
|
/// This flag specifies that queued errors should be received from
|
|
/// the socket error queue. (For more details, see
|
|
/// [recvfrom(2)](https://linux.die.net/man/2/recvfrom))
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
MSG_ERRQUEUE;
|
|
/// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain
|
|
/// file descriptor using the `SCM_RIGHTS` operation (described in
|
|
/// [unix(7)](https://linux.die.net/man/7/unix)).
|
|
/// This flag is useful for the same reasons as the `O_CLOEXEC` flag of
|
|
/// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html).
|
|
///
|
|
/// Only used in [`recvmsg`](fn.recvmsg.html) function.
|
|
#[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;
|
|
/// Requests not to send `SIGPIPE` errors when the other end breaks the connection.
|
|
/// (For more details, see [send(2)](https://linux.die.net/man/2/send)).
|
|
#[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_if! {
|
|
if #[cfg(any(target_os = "android", target_os = "linux"))] {
|
|
/// Unix credentials of the sending process.
|
|
///
|
|
/// This struct is used with the `SO_PEERCRED` ancillary message
|
|
/// and the `SCM_CREDENTIALS` control message for UNIX sockets.
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct UnixCredentials(libc::ucred);
|
|
|
|
impl UnixCredentials {
|
|
/// Creates a new instance with the credentials of the current process
|
|
pub fn new() -> Self {
|
|
// Safe because these FFI functions are inherently safe
|
|
unsafe {
|
|
UnixCredentials(libc::ucred {
|
|
pid: libc::getpid(),
|
|
uid: libc::getuid(),
|
|
gid: libc::getgid()
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Returns the process identifier
|
|
pub fn pid(&self) -> libc::pid_t {
|
|
self.0.pid
|
|
}
|
|
|
|
/// Returns the user identifier
|
|
pub fn uid(&self) -> libc::uid_t {
|
|
self.0.uid
|
|
}
|
|
|
|
/// Returns the group identifier
|
|
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"))] {
|
|
/// Unix credentials of the sending process.
|
|
///
|
|
/// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct UnixCredentials(libc::cmsgcred);
|
|
|
|
impl UnixCredentials {
|
|
/// Returns the process identifier
|
|
pub fn pid(&self) -> libc::pid_t {
|
|
self.0.cmcred_pid
|
|
}
|
|
|
|
/// Returns the real user identifier
|
|
pub fn uid(&self) -> libc::uid_t {
|
|
self.0.cmcred_uid
|
|
}
|
|
|
|
/// Returns the effective user identifier
|
|
pub fn euid(&self) -> libc::uid_t {
|
|
self.0.cmcred_euid
|
|
}
|
|
|
|
/// Returns the real group identifier
|
|
pub fn gid(&self) -> libc::gid_t {
|
|
self.0.cmcred_gid
|
|
}
|
|
|
|
/// Returns a list group identifiers (the first one being the effective 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cfg_if! {
|
|
if #[cfg(any(
|
|
target_os = "dragonfly",
|
|
target_os = "freebsd",
|
|
target_os = "macos",
|
|
target_os = "ios"
|
|
))] {
|
|
/// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct XuCred(libc::xucred);
|
|
|
|
impl XuCred {
|
|
/// Structure layout version
|
|
pub fn version(&self) -> u32 {
|
|
self.0.cr_version
|
|
}
|
|
|
|
/// Effective user ID
|
|
pub fn uid(&self) -> libc::uid_t {
|
|
self.0.cr_uid
|
|
}
|
|
|
|
/// Returns a list of group identifiers (the first one being the
|
|
/// effective GID)
|
|
pub fn groups(&self) -> &[libc::gid_t] {
|
|
&self.0.cr_groups
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
feature! {
|
|
#![feature = "net"]
|
|
/// Request for multicast socket operations
|
|
///
|
|
/// This is a wrapper type around `ip_mreq`.
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct IpMembershipRequest(libc::ip_mreq);
|
|
|
|
impl IpMembershipRequest {
|
|
/// Instantiate a new `IpMembershipRequest`
|
|
///
|
|
/// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface.
|
|
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)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Request for ipv6 multicast socket operations
|
|
///
|
|
/// This is a wrapper type around `ipv6_mreq`.
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
|
|
|
|
impl Ipv6MembershipRequest {
|
|
/// Instantiate a new `Ipv6MembershipRequest`
|
|
pub const fn new(group: net::Ipv6Addr) -> Self {
|
|
Ipv6MembershipRequest(libc::ipv6_mreq {
|
|
ipv6mr_multiaddr: ipv6addr_to_libc(&group),
|
|
ipv6mr_interface: 0,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
feature! {
|
|
#![feature = "uio"]
|
|
|
|
/// Create a buffer large enough for storing some control messages as returned
|
|
/// by [`recvmsg`](fn.recvmsg.html).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # #[macro_use] extern crate nix;
|
|
/// # use nix::sys::time::TimeVal;
|
|
/// # use std::os::unix::io::RawFd;
|
|
/// # fn main() {
|
|
/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message
|
|
/// let _ = cmsg_space!(TimeVal);
|
|
/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
|
|
/// // with two file descriptors
|
|
/// let _ = cmsg_space!([RawFd; 2]);
|
|
/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
|
|
/// // and a `ControlMessageOwned::ScmTimestamp` message
|
|
/// let _ = cmsg_space!(RawFd, TimeVal);
|
|
/// # }
|
|
/// ```
|
|
// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a
|
|
// stack-allocated array.
|
|
#[macro_export]
|
|
macro_rules! cmsg_space {
|
|
( $( $x:ty ),* ) => {
|
|
{
|
|
let mut space = 0;
|
|
$(
|
|
// CMSG_SPACE is always safe
|
|
space += unsafe {
|
|
$crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint)
|
|
} as usize;
|
|
)*
|
|
Vec::<u8>::with_capacity(space)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
/// Contains outcome of sending or receiving a message
|
|
///
|
|
/// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and
|
|
/// [`iovs`][RecvMsg::iovs`] to access underlying io slices.
|
|
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> {
|
|
/// Iterate over the valid control messages pointed to by this
|
|
/// msghdr.
|
|
pub fn cmsgs(&self) -> CmsgIterator {
|
|
CmsgIterator {
|
|
cmsghdr: self.cmsghdr,
|
|
mhdr: &self.mhdr
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct CmsgIterator<'a> {
|
|
/// Control message buffer to decode from. Must adhere to cmsg alignment.
|
|
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, // No more messages
|
|
Some(hdr) => {
|
|
// Get the data.
|
|
// Safe if cmsghdr points to valid data returned by recvmsg(2)
|
|
let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))};
|
|
// Advance the internal pointer. Safe if mhdr and cmsghdr point
|
|
// to valid data returned by recvmsg(2)
|
|
self.cmsghdr = unsafe {
|
|
let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _);
|
|
p.as_ref()
|
|
};
|
|
cm
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A type-safe wrapper around a single control message, as used with
|
|
/// [`recvmsg`](#fn.recvmsg).
|
|
///
|
|
/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
|
|
// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and
|
|
// sendmsg. However, on some platforms the messages returned by recvmsg may be
|
|
// unaligned. ControlMessageOwned takes those messages by copy, obviating any
|
|
// alignment issues.
|
|
//
|
|
// See https://github.com/nix-rust/nix/issues/999
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
#[non_exhaustive]
|
|
pub enum ControlMessageOwned {
|
|
/// Received version of [`ControlMessage::ScmRights`]
|
|
ScmRights(Vec<RawFd>),
|
|
/// Received version of [`ControlMessage::ScmCredentials`]
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
ScmCredentials(UnixCredentials),
|
|
/// Received version of [`ControlMessage::ScmCreds`]
|
|
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
ScmCreds(UnixCredentials),
|
|
/// A message of type `SCM_TIMESTAMP`, containing the time the
|
|
/// packet was received by the kernel.
|
|
///
|
|
/// See the kernel's explanation in "SO_TIMESTAMP" of
|
|
/// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # #[macro_use] extern crate nix;
|
|
/// # use nix::sys::socket::*;
|
|
/// # use nix::sys::time::*;
|
|
/// # use std::io::{IoSlice, IoSliceMut};
|
|
/// # use std::time::*;
|
|
/// # use std::str::FromStr;
|
|
/// # fn main() {
|
|
/// // Set up
|
|
/// let message = "Ohayō!".as_bytes();
|
|
/// let in_socket = socket(
|
|
/// AddressFamily::Inet,
|
|
/// SockType::Datagram,
|
|
/// SockFlag::empty(),
|
|
/// None).unwrap();
|
|
/// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
|
|
/// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
|
|
/// bind(in_socket, &localhost).unwrap();
|
|
/// let address: SockaddrIn = getsockname(in_socket).unwrap();
|
|
/// // Get initial time
|
|
/// let time0 = SystemTime::now();
|
|
/// // Send the message
|
|
/// let iov = [IoSlice::new(message)];
|
|
/// let flags = MsgFlags::empty();
|
|
/// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
|
|
/// assert_eq!(message.len(), l);
|
|
/// // Receive the message
|
|
/// let mut buffer = vec![0u8; message.len()];
|
|
/// let mut cmsgspace = cmsg_space!(TimeVal);
|
|
/// let mut iov = [IoSliceMut::new(&mut buffer)];
|
|
/// let r = recvmsg::<SockaddrIn>(in_socket, &mut iov, Some(&mut cmsgspace), flags)
|
|
/// .unwrap();
|
|
/// let rtime = match r.cmsgs().next() {
|
|
/// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime,
|
|
/// Some(_) => panic!("Unexpected control message"),
|
|
/// None => panic!("No control message")
|
|
/// };
|
|
/// // Check the final time
|
|
/// let time1 = SystemTime::now();
|
|
/// // the packet's received timestamp should lie in-between the two system
|
|
/// // times, unless the system clock was adjusted in the meantime.
|
|
/// let rduration = Duration::new(rtime.tv_sec() as u64,
|
|
/// rtime.tv_usec() as u32 * 1000);
|
|
/// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
|
|
/// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
|
|
/// // Close socket
|
|
/// nix::unistd::close(in_socket).unwrap();
|
|
/// # }
|
|
/// ```
|
|
ScmTimestamp(TimeVal),
|
|
/// A set of nanosecond resolution timestamps
|
|
///
|
|
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
|
|
#[cfg(all(target_os = "linux"))]
|
|
ScmTimestampsns(Timestamps),
|
|
/// Nanoseconds resolution timestamp
|
|
///
|
|
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
|
|
#[cfg(all(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),
|
|
|
|
/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
|
|
/// packets from a single sender.
|
|
/// Fixed-size payloads are following one by one in a receive buffer.
|
|
/// This Control Message indicates the size of all smaller packets,
|
|
/// except, maybe, the last one.
|
|
///
|
|
/// `UdpGroSegment` socket option should be enabled on a socket
|
|
/// to allow receiving GRO packets.
|
|
#[cfg(target_os = "linux")]
|
|
#[cfg(feature = "net")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
|
|
UdpGroSegments(u16),
|
|
|
|
/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
|
|
/// ancilliary msg (cmsg) should be attached to recieved
|
|
/// skbs indicating the number of packets dropped by the
|
|
/// socket between the last recieved packet and this
|
|
/// received packet.
|
|
///
|
|
/// `RxqOvfl` socket option should be enabled on a socket
|
|
/// to allow receiving the drop counter.
|
|
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
RxqOvfl(u32),
|
|
|
|
/// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
|
|
#[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>),
|
|
/// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
|
|
#[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>),
|
|
|
|
/// Catch-all variant for unimplemented cmsg types.
|
|
#[doc(hidden)]
|
|
Unknown(UnknownCmsg),
|
|
}
|
|
|
|
/// For representing packet timestamps via `SO_TIMESTAMPING` interface
|
|
#[cfg(all(target_os = "linux"))]
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
pub struct Timestamps {
|
|
/// software based timestamp, usually one containing data
|
|
pub system: TimeSpec,
|
|
/// legacy timestamp, usually empty
|
|
pub hw_trans: TimeSpec,
|
|
/// hardware based timestamp
|
|
pub hw_raw: TimeSpec,
|
|
}
|
|
|
|
impl ControlMessageOwned {
|
|
/// Decodes a `ControlMessageOwned` from raw bytes.
|
|
///
|
|
/// This is only safe to call if the data is correct for the message type
|
|
/// specified in the header. Normally, the kernel ensures that this is the
|
|
/// case. "Correct" in this case includes correct length, alignment and
|
|
/// actual content.
|
|
// Clippy complains about the pointer alignment of `p`, not understanding
|
|
// that it's being fed to a function that can handle that.
|
|
#[allow(clippy::cast_ptr_alignment)]
|
|
unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
|
|
{
|
|
let p = CMSG_DATA(header);
|
|
// The cast is not unnecessary on all platforms.
|
|
#[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(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(all(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(all(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 = 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)] // False positive
|
|
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);
|
|
|
|
// For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
|
|
// CMSG_DATA buffer. For local errors, there is no address included in the control
|
|
// message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
|
|
// validate that the address object is in-bounds before we attempt to copy it.
|
|
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)))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A type-safe zero-copy wrapper around a single control message, as used wih
|
|
/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not
|
|
/// exhaustively pattern-match it.
|
|
///
|
|
/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
#[non_exhaustive]
|
|
pub enum ControlMessage<'a> {
|
|
/// A message of type `SCM_RIGHTS`, containing an array of file
|
|
/// descriptors passed between processes.
|
|
///
|
|
/// See the description in the "Ancillary messages" section of the
|
|
/// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html).
|
|
///
|
|
/// Using multiple `ScmRights` messages for a single `sendmsg` call isn't
|
|
/// recommended since it causes platform-dependent behaviour: It might
|
|
/// swallow all but the first `ScmRights` message or fail with `EINVAL`.
|
|
/// Instead, you can put all fds to be passed into a single `ScmRights`
|
|
/// message.
|
|
ScmRights(&'a [RawFd]),
|
|
/// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of
|
|
/// a process connected to the socket.
|
|
///
|
|
/// This is similar to the socket option `SO_PEERCRED`, but requires a
|
|
/// process to explicitly send its credentials. A process running as root is
|
|
/// allowed to specify any credentials, while credentials sent by other
|
|
/// processes are verified by the kernel.
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
ScmCredentials(&'a UnixCredentials),
|
|
/// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
|
|
/// a process connected to the socket.
|
|
///
|
|
/// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
|
|
/// requires a process to explicitly send its credentials.
|
|
///
|
|
/// Credentials are always overwritten by the kernel, so this variant does have
|
|
/// any data, unlike the receive-side
|
|
/// [`ControlMessageOwned::ScmCreds`].
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
|
|
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
ScmCreds,
|
|
|
|
/// Set IV for `AF_ALG` crypto API.
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
|
|
#[cfg(any(
|
|
target_os = "android",
|
|
target_os = "linux",
|
|
))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
AlgSetIv(&'a [u8]),
|
|
/// Set crypto operation for `AF_ALG` crypto API. It may be one of
|
|
/// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT`
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
|
|
#[cfg(any(
|
|
target_os = "android",
|
|
target_os = "linux",
|
|
))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
AlgSetOp(&'a libc::c_int),
|
|
/// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms)
|
|
/// for `AF_ALG` crypto API.
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
|
|
#[cfg(any(
|
|
target_os = "android",
|
|
target_os = "linux",
|
|
))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
AlgSetAeadAssoclen(&'a u32),
|
|
|
|
/// UDP GSO makes it possible for applications to generate network packets
|
|
/// for a virtual MTU much greater than the real one.
|
|
/// The length of the send data no longer matches the expected length on
|
|
/// the wire.
|
|
/// The size of the datagram payload as it should appear on the wire may be
|
|
/// passed through this control message.
|
|
/// Send buffer should consist of multiple fixed-size wire payloads
|
|
/// following one by one, and the last, possibly smaller one.
|
|
#[cfg(target_os = "linux")]
|
|
#[cfg(feature = "net")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
|
|
UdpGsoSegments(&'a u16),
|
|
|
|
/// Configure the sending addressing and interface for v4
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page.
|
|
#[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),
|
|
|
|
/// Configure the sending addressing and interface for v6
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
|
|
#[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),
|
|
|
|
/// Configure the IPv4 source address with `IP_SENDSRCADDR`.
|
|
#[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),
|
|
|
|
/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
|
|
/// ancilliary msg (cmsg) should be attached to recieved
|
|
/// skbs indicating the number of packets dropped by the
|
|
/// socket between the last recieved packet and this
|
|
/// received packet.
|
|
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
|
|
#[cfg_attr(docsrs, doc(cfg(all())))]
|
|
RxqOvfl(&'a u32),
|
|
|
|
/// Configure the transmission time of packets.
|
|
///
|
|
/// For further information, please refer to the
|
|
/// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man
|
|
/// page.
|
|
#[cfg(target_os = "linux")]
|
|
TxTime(&'a u64),
|
|
}
|
|
|
|
// An opaque structure used to prevent cmsghdr from being a public type
|
|
#[doc(hidden)]
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct UnknownCmsg(cmsghdr, Vec<u8>);
|
|
|
|
impl<'a> ControlMessage<'a> {
|
|
/// The value of CMSG_SPACE on this message.
|
|
/// Safe because CMSG_SPACE is always safe
|
|
fn space(&self) -> usize {
|
|
unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize}
|
|
}
|
|
|
|
/// The value of CMSG_LEN on this message.
|
|
/// Safe because CMSG_LEN is always safe
|
|
#[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)}
|
|
}
|
|
|
|
/// Return a reference to the payload data as a byte pointer
|
|
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 => {
|
|
// The kernel overwrites the data, we just zero it
|
|
// to make sure it's not uninitialized memory
|
|
unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
|
|
return
|
|
}
|
|
#[cfg(any(target_os = "android", target_os = "linux"))]
|
|
ControlMessage::AlgSetIv(iv) => {
|
|
#[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501
|
|
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()
|
|
)
|
|
};
|
|
}
|
|
|
|
/// The size of the payload, excluding its cmsghdr
|
|
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_val(&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")]
|
|
#[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)
|
|
},
|
|
}
|
|
}
|
|
|
|
/// Returns the value to put into the `cmsg_level` field of the header.
|
|
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,
|
|
}
|
|
}
|
|
|
|
/// Returns the value to put into the `cmsg_type` field of the header.
|
|
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: cmsg must point to a valid cmsghdr with enough space to
|
|
// encode self.
|
|
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));
|
|
}
|
|
}
|
|
|
|
|
|
/// Send data in scatter-gather vectors to a socket, possibly accompanied
|
|
/// by ancillary data. Optionally direct the message at the given address,
|
|
/// as with sendto.
|
|
///
|
|
/// Allocates if cmsgs is nonempty.
|
|
///
|
|
/// # Examples
|
|
/// When not directing to any specific address, use `()` for the generic type
|
|
/// ```
|
|
/// # use nix::sys::socket::*;
|
|
/// # use nix::unistd::pipe;
|
|
/// # use std::io::IoSlice;
|
|
/// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
|
|
/// SockFlag::empty())
|
|
/// .unwrap();
|
|
/// let (r, w) = pipe().unwrap();
|
|
///
|
|
/// let iov = [IoSlice::new(b"hello")];
|
|
/// let fds = [r];
|
|
/// let cmsg = ControlMessage::ScmRights(&fds);
|
|
/// sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
|
|
/// ```
|
|
/// When directing to a specific address, the generic type will be inferred.
|
|
/// ```
|
|
/// # use nix::sys::socket::*;
|
|
/// # use nix::unistd::pipe;
|
|
/// # use std::io::IoSlice;
|
|
/// # use std::str::FromStr;
|
|
/// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap();
|
|
/// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(),
|
|
/// None).unwrap();
|
|
/// let (r, w) = pipe().unwrap();
|
|
///
|
|
/// let iov = [IoSlice::new(b"hello")];
|
|
/// let fds = [r];
|
|
/// let cmsg = ControlMessage::ScmRights(&fds);
|
|
/// sendmsg(fd, &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
|
|
/// ```
|
|
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();
|
|
|
|
// First size the buffer needed to hold the cmsgs. It must be zeroed,
|
|
// because subsequent code will not clear the padding bytes.
|
|
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)
|
|
}
|
|
|
|
|
|
/// An extension of `sendmsg` that allows the caller to transmit multiple
|
|
/// messages on a socket using a single system call. This has performance
|
|
/// benefits for some applications.
|
|
///
|
|
/// Allocations are performed for cmsgs and to build `msghdr` buffer
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `fd`: Socket file descriptor
|
|
/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items
|
|
/// * `flags`: Optional flags passed directly to the operating system.
|
|
///
|
|
/// # Returns
|
|
/// `Vec` with numbers of sent bytes on each sent message.
|
|
///
|
|
/// # References
|
|
/// [`sendmsg`](fn.sendmsg.html)
|
|
#[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,
|
|
// one address per group of slices
|
|
addrs: AS,
|
|
// shared across all the messages
|
|
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 mut 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 _;
|
|
|
|
// Encode each cmsg. This must happen after initializing the header because
|
|
// CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
|
|
// CMSG_FIRSTHDR is always safe
|
|
let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
|
|
for cmsg in cmsgs.as_ref() {
|
|
assert_ne!(pmhdr, ptr::null_mut());
|
|
// Safe because we know that pmhdr is valid, and we initialized it with
|
|
// sufficient space
|
|
unsafe { cmsg.encode_into(pmhdr) };
|
|
// Safe because mhdr is valid
|
|
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)]
|
|
/// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions
|
|
pub struct MultiHeaders<S> {
|
|
// preallocated boxed slice of mmsghdr
|
|
items: Box<[libc::mmsghdr]>,
|
|
addresses: Box<[mem::MaybeUninit<S>]>,
|
|
// while we are not using it directly - this is used to store control messages
|
|
// and we retain pointers to them inside items array
|
|
_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> {
|
|
/// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate
|
|
///
|
|
/// `cmsg_buffer` should be created with [`cmsg_space!`] if needed
|
|
pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
|
|
where
|
|
S: Copy + SockaddrLike,
|
|
{
|
|
// we will be storing pointers to addresses inside mhdr - convert it into boxed
|
|
// slice so it can'be changed later by pushing anything into self.addresses
|
|
let mut addresses = vec![std::mem::MaybeUninit::uninit(); num_slices].into_boxed_slice();
|
|
|
|
let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
|
|
|
|
// we'll need a cmsg_buffer for each slice, we preallocate a vector and split
|
|
// it into "slices" parts
|
|
let 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 &cmsg_buffers {
|
|
Some(v) => ((&v[ix * msg_controllen] as *const u8), msg_controllen),
|
|
None => (std::ptr::null(), 0),
|
|
};
|
|
let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null(), 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,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call.
|
|
///
|
|
/// This has performance benefits for some applications.
|
|
///
|
|
/// This method performs no allocations.
|
|
///
|
|
/// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce
|
|
/// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and
|
|
/// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs].
|
|
///
|
|
/// # Bugs (in underlying implementation, at least in Linux)
|
|
/// The timeout argument does not work as intended. The timeout is checked only after the receipt
|
|
/// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires,
|
|
/// but then no further datagrams are received, the call will block forever.
|
|
///
|
|
/// If an error occurs after at least one message has been received, the call succeeds, and returns
|
|
/// the number of messages received. The error code is expected to be returned on a subsequent
|
|
/// call to recvmmsg(). In the current implementation, however, the error code can be
|
|
/// overwritten in the meantime by an unrelated network event on a socket, for example an
|
|
/// incoming ICMP packet.
|
|
|
|
// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
|
|
// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
|
|
// details
|
|
|
|
#[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 mut 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)]
|
|
/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
|
|
///
|
|
///
|
|
pub struct MultiResults<'a, S> {
|
|
// preallocated structures
|
|
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>;
|
|
|
|
// The cast is not unnecessary on all platforms.
|
|
#[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];
|
|
|
|
// as long as we are not reading past the index writen by recvmmsg - address
|
|
// will be initialized
|
|
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> {
|
|
/// Iterate over the filled io slices pointed by this msghdr
|
|
pub fn iovs(&self) -> IoSliceIterator<'a> {
|
|
IoSliceIterator {
|
|
index: 0,
|
|
remaining: self.bytes,
|
|
slices: unsafe {
|
|
// safe for as long as mgdr is properly initialized and references are valid.
|
|
// for multi messages API we initialize it with an empty
|
|
// slice and replace with a concrete buffer
|
|
// for single message API we hold a lifetime reference to ioslices
|
|
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)
|
|
}
|
|
}
|
|
|
|
// test contains both recvmmsg and timestaping which is linux only
|
|
// there are existing tests for recvmmsg only in tests/
|
|
#[cfg(target_os = "linux")]
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::sys::socket::{AddressFamily, ControlMessageOwned};
|
|
use crate::*;
|
|
use std::str::FromStr;
|
|
|
|
#[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, &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, &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, &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,
|
|
address: S,
|
|
) -> RecvMsg<'a, 'i, S>
|
|
where S: SockaddrLike
|
|
{
|
|
// The cast is not unnecessary on all platforms.
|
|
#[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()
|
|
};
|
|
|
|
RecvMsg {
|
|
bytes: r as usize,
|
|
cmsghdr,
|
|
address: Some(address),
|
|
flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
|
|
mhdr,
|
|
iobufs: std::marker::PhantomData,
|
|
}
|
|
}
|
|
|
|
/// Pack pointers to various structures into into msghdr
|
|
///
|
|
/// # Safety
|
|
/// `iov_buffer` and `iov_buffer_len` must point to a slice
|
|
/// of `IoSliceMut` and number of available elements or be a null pointer and 0
|
|
///
|
|
/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used
|
|
/// to store control headers later or be a null pointer and 0 if control
|
|
/// headers are not used
|
|
///
|
|
/// Buffers must remain valid for the whole lifetime of msghdr
|
|
unsafe fn pack_mhdr_to_receive<S>(
|
|
iov_buffer: *const IoSliceMut,
|
|
iov_buffer_len: usize,
|
|
cmsg_buffer: *const u8,
|
|
cmsg_capacity: usize,
|
|
address: *mut S,
|
|
) -> msghdr
|
|
where
|
|
S: SockaddrLike
|
|
{
|
|
// Musl's msghdr has private fields, so this is the only way to
|
|
// initialize it.
|
|
let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
|
|
let p = mhdr.as_mut_ptr();
|
|
(*p).msg_name = (*address).as_mut_ptr() 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();
|
|
|
|
// The message header must be initialized before the individual cmsgs.
|
|
let cmsg_ptr = if capacity > 0 {
|
|
cmsg_buffer.as_ptr() as *mut c_void
|
|
} else {
|
|
ptr::null_mut()
|
|
};
|
|
|
|
let mhdr = unsafe {
|
|
// Musl's msghdr has private fields, so this is the only way to
|
|
// initialize it.
|
|
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);
|
|
// transmute iov into a mutable pointer. sendmsg doesn't really mutate
|
|
// the buffer, but the standard says that it takes a mutable pointer
|
|
(*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()
|
|
};
|
|
|
|
// Encode each cmsg. This must happen after initializing the header because
|
|
// CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
|
|
// CMSG_FIRSTHDR is always safe
|
|
let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) };
|
|
for cmsg in cmsgs.as_ref() {
|
|
assert_ne!(pmhdr, ptr::null_mut());
|
|
// Safe because we know that pmhdr is valid, and we initialized it with
|
|
// sufficient space
|
|
unsafe { cmsg.encode_into(pmhdr) };
|
|
// Safe because mhdr is valid
|
|
pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) };
|
|
}
|
|
|
|
mhdr
|
|
}
|
|
|
|
/// Receive message in scatter-gather vectors from a socket, and
|
|
/// optionally receive ancillary data into the provided buffer.
|
|
/// If no ancillary data is desired, use () as the type parameter.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// * `fd`: Socket file descriptor
|
|
/// * `iov`: Scatter-gather list of buffers to receive the message
|
|
/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by
|
|
/// [`cmsg_space!`](../../macro.cmsg_space.html)
|
|
/// * `flags`: Optional flags passed directly to the operating system.
|
|
///
|
|
/// # References
|
|
/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
|
|
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, 'inner, 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_ref().as_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()) })
|
|
}
|
|
}
|
|
|
|
/// Create an endpoint for communication
|
|
///
|
|
/// The `protocol` specifies a particular protocol to be used with the
|
|
/// socket. Normally only a single protocol exists to support a
|
|
/// particular socket type within a given protocol family, in which case
|
|
/// protocol can be specified as `None`. However, it is possible that many
|
|
/// protocols may exist, in which case a particular protocol must be
|
|
/// specified in this manner.
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
|
|
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,
|
|
};
|
|
|
|
// SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
|
|
// little easier to understand by separating it out. So we have to merge these bitfields
|
|
// here.
|
|
let mut ty = ty as c_int;
|
|
ty |= flags.bits();
|
|
|
|
let res = unsafe { libc::socket(domain as c_int, ty, protocol) };
|
|
|
|
Errno::result(res)
|
|
}
|
|
|
|
/// Create a pair of connected sockets
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
|
|
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,
|
|
};
|
|
|
|
// SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
|
|
// little easier to understand by separating it out. So we have to merge these bitfields
|
|
// here.
|
|
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]))
|
|
}
|
|
|
|
/// Listen for connections on a socket
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
|
|
pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
|
|
let res = unsafe { libc::listen(sockfd, backlog as c_int) };
|
|
|
|
Errno::result(res).map(drop)
|
|
}
|
|
|
|
/// Bind a name to a socket
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
|
|
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)
|
|
}
|
|
|
|
/// Accept a connection on a socket
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html)
|
|
pub fn accept(sockfd: RawFd) -> Result<RawFd> {
|
|
let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
|
|
|
|
Errno::result(res)
|
|
}
|
|
|
|
/// Accept a connection on a socket
|
|
///
|
|
/// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html)
|
|
#[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)
|
|
}
|
|
|
|
/// Initiate a connection on a socket
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
|
|
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)
|
|
}
|
|
|
|
/// Receive data from a connection-oriented socket. Returns the number of
|
|
/// bytes read
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
|
|
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)
|
|
}
|
|
}
|
|
|
|
/// Receive data from a connectionless or connection-oriented socket. Returns
|
|
/// the number of bytes read and, for connectionless sockets, the socket
|
|
/// address of the sender.
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
|
|
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_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() as *const sockaddr,
|
|
Some(len),
|
|
),
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Send a message to a socket
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
|
|
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)
|
|
}
|
|
|
|
/// Send data to a connection-oriented socket. Returns the number of bytes read
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
|
|
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)
|
|
}
|
|
|
|
/*
|
|
*
|
|
* ===== Socket Options =====
|
|
*
|
|
*/
|
|
|
|
/// Represents a socket option that can be retrieved.
|
|
pub trait GetSockOpt: Copy {
|
|
type Val;
|
|
|
|
/// Look up the value of this socket option on the given socket.
|
|
fn get(&self, fd: RawFd) -> Result<Self::Val>;
|
|
}
|
|
|
|
/// Represents a socket option that can be set.
|
|
pub trait SetSockOpt: Clone {
|
|
type Val;
|
|
|
|
/// Set the value of this socket option on the given socket.
|
|
fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
|
|
}
|
|
|
|
/// Get the current value for the requested socket option
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
|
|
pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> {
|
|
opt.get(fd)
|
|
}
|
|
|
|
/// Sets the value for the requested socket option
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html)
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use nix::sys::socket::setsockopt;
|
|
/// use nix::sys::socket::sockopt::KeepAlive;
|
|
/// use std::net::TcpListener;
|
|
/// use std::os::unix::io::AsRawFd;
|
|
///
|
|
/// let listener = TcpListener::bind("0.0.0.0:0").unwrap();
|
|
/// let fd = listener.as_raw_fd();
|
|
/// let res = setsockopt(fd, KeepAlive, &true);
|
|
/// assert!(res.is_ok());
|
|
/// ```
|
|
pub fn setsockopt<O: SetSockOpt>(
|
|
fd: RawFd,
|
|
opt: O,
|
|
val: &O::Val,
|
|
) -> Result<()> {
|
|
opt.set(fd, val)
|
|
}
|
|
|
|
/// Get the address of the peer connected to the socket `fd`.
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
|
|
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)
|
|
}
|
|
}
|
|
|
|
/// Get the current address to which the socket `fd` is bound.
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
|
|
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 {
|
|
/// Further receptions will be disallowed.
|
|
Read,
|
|
/// Further transmissions will be disallowed.
|
|
Write,
|
|
/// Further receptions and transmissions will be disallowed.
|
|
Both,
|
|
}
|
|
|
|
/// Shut down part of a full-duplex connection.
|
|
///
|
|
/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html)
|
|
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 {
|
|
#[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");
|
|
}
|
|
}
|