summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys/socket/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/sys/socket/mod.rs')
-rw-r--r--third_party/rust/nix/src/sys/socket/mod.rs2465
1 files changed, 2465 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/sys/socket/mod.rs b/third_party/rust/nix/src/sys/socket/mod.rs
new file mode 100644
index 0000000000..78dd617c55
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/mod.rs
@@ -0,0 +1,2465 @@
+//! Socket interface functions
+//!
+//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeSpec;
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeVal;
+use crate::{errno::Errno, Result};
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, size_t, socklen_t};
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+use libc::{
+ iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE,
+};
+#[cfg(not(target_os = "redox"))]
+use std::io::{IoSlice, IoSliceMut};
+#[cfg(feature = "net")]
+use std::net;
+use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, RawFd, OwnedFd};
+use std::{mem, ptr};
+
+#[deny(missing_docs)]
+mod addr;
+#[deny(missing_docs)]
+pub mod sockopt;
+
+/*
+ *
+ * ===== 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",
+ target_os = "redox",
+)))]
+#[cfg(feature = "net")]
+pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
+#[cfg(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+))]
+#[cfg(feature = "net")]
+pub use self::addr::{SockaddrIn, SockaddrIn6};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "ioctl")]
+pub use crate::sys::socket::addr::sys_control::SysControlAddr;
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+pub use crate::sys::socket::addr::vsock::VsockAddr;
+
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+pub use libc::{cmsghdr, msghdr};
+pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
+#[cfg(feature = "net")]
+pub use libc::{sockaddr_in, sockaddr_in6};
+
+#[cfg(feature = "net")]
+use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
+
+/// 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.
+ #[cfg(not(target_os = "redox"))]
+ Raw = libc::SOCK_RAW,
+ /// Provides a reliable datagram layer that does not
+ /// guarantee ordering.
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ 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),
+ #[cfg(not(target_os = "redox"))]
+ libc::SOCK_RAW => Ok(Self::Raw),
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ libc::SOCK_RDM => Ok(Self::Rdm),
+ _ => Err(Errno::EINVAL),
+ }
+ }
+}
+
+/// 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,
+ /// Netfilter/iptables ULOG.
+ /// ([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())))]
+ NetlinkNFLOG = libc::NETLINK_NFLOG,
+ /// 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,
+ /// Generic netlink family for simplified netlink usage.
+ /// ([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())))]
+ NetlinkGeneric = libc::NETLINK_GENERIC,
+ /// 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 as u16).to_be() as i32,
+ /// The Controller Area Network raw socket protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CanRaw = libc::CAN_RAW,
+}
+
+impl SockProtocol {
+ /// The Controller Area Network broadcast manager protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(non_upper_case_globals)]
+ pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; // Matches libc::CAN_BCM
+}
+#[cfg(any(target_os = "android", 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: libc::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 transmitting timestamps as reported by hardware
+ SOF_TIMESTAMPING_TX_HARDWARE;
+ /// Collect transmitting 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;
+ /// Generate a unique identifier along with each transmitted packet
+ SOF_TIMESTAMPING_OPT_ID;
+ /// Return transmit timestamps alongside an empty packet instead of the original packet
+ SOF_TIMESTAMPING_OPT_TSONLY;
+ }
+}
+
+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.
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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;
+ /// Turns on [`MSG_DONTWAIT`] after the first message has been received (only for
+ /// `recvmmsg()`).
+ #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_WAITFORONE;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ /// 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 {
+ std::slice::from_raw_parts(
+ self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
+ self.0.cmcred_ngroups as _
+ )
+ }
+ }
+ }
+
+ impl From<libc::cmsgcred> for UnixCredentials {
+ fn from(cred: libc::cmsgcred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// 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,
+ })
+ }
+}
+}
+
+#[cfg(not(target_os = "redox"))]
+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 space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*;
+ Vec::<u8>::with_capacity(space)
+ }
+ }
+}
+
+#[inline]
+#[doc(hidden)]
+pub fn cmsg_space<T>() -> usize {
+ // SAFETY: CMSG_SPACE is always safe
+ unsafe { libc::CMSG_SPACE(mem::size_of::<T>() as libc::c_uint) as usize }
+}
+
+#[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;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # 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.as_raw_fd(), &localhost).unwrap();
+ /// let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).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.as_raw_fd(), &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.as_raw_fd(), &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
+ /// # }
+ /// ```
+ ScmTimestamp(TimeVal),
+ /// A set of nanosecond resolution timestamps
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ScmTimestampsns(Timestamps),
+ /// Nanoseconds resolution timestamp
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmTimestampns(TimeSpec),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(libc::in_pktinfo),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(libc::in6_pktinfo),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvIf(libc::sockaddr_dl),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvDstAddr(libc::in_addr),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4OrigDstAddr(libc::sockaddr_in),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6OrigDstAddr(libc::sockaddr_in6),
+
+ /// 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(any(target_os = "android", 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(any(target_os = "aix", target_os = "haiku")))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
+ let tv: libc::timeval = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
+ let ts: libc::timespec = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
+ let tp = p as *const libc::timespec;
+ let ts: libc::timespec = ptr::read_unaligned(tp);
+ let system = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
+ let hw_trans = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
+ let hw_raw = TimeSpec::from(ts);
+ let timestamping = Timestamps { system, hw_trans, hw_raw };
+ ControlMessageOwned::ScmTimestampsns(timestamping)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
+ ControlMessageOwned::Ipv6PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
+ ControlMessageOwned::Ipv4PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVIF) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
+ ControlMessageOwned::Ipv4RecvIf(dl)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::in_addr);
+ ControlMessageOwned::Ipv4RecvDstAddr(dl)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+ ControlMessageOwned::Ipv4OrigDstAddr(dl)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ (libc::SOL_UDP, libc::UDP_GRO) => {
+ let gso_size: u16 = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::UdpGroSegments(gso_size)
+ },
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
+ let drop_counter = ptr::read_unaligned(p as *const u32);
+ ControlMessageOwned::RxqOvfl(drop_counter)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
+ ControlMessageOwned::Ipv4RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+ ControlMessageOwned::Ipv6RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+ ControlMessageOwned::Ipv6OrigDstAddr(dl)
+ },
+ (_, _) => {
+ let sl = std::slice::from_raw_parts(p, len);
+ let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
+ ControlMessageOwned::Unknown(ucmsg)
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[allow(clippy::cast_ptr_alignment)] // 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::<&[u8]>() + iv.len()
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ mem::size_of_val(op)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ mem::size_of_val(len)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ mem::size_of_val(gso_size)
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ mem::size_of_val(drop_count)
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ mem::size_of_val(tx_time)
+ },
+ }
+ }
+
+ /// 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;
+/// # use std::os::unix::io::AsRawFd;
+/// 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.as_raw_fd(), &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;
+/// # use std::os::unix::io::AsRawFd;
+/// 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.as_raw_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 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::<S>::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 mut cmsg_buffers =
+ cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
+
+ let items = addresses
+ .iter_mut()
+ .enumerate()
+ .map(|(ix, address)| {
+ let (ptr, cap) = match &mut cmsg_buffers {
+ Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen),
+ None => (std::ptr::null_mut(), 0),
+ };
+ let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) };
+ libc::mmsghdr {
+ msg_hdr,
+ msg_len: 0,
+ }
+ })
+ .collect::<Vec<_>>();
+
+ Self {
+ items: items.into_boxed_slice(),
+ addresses,
+ _cmsg_buffers: cmsg_buffers,
+ msg_controllen,
+ }
+ }
+}
+
+/// 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 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;
+ use std::os::unix::io::AsRawFd;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recvmm2() -> crate::Result<()> {
+ use crate::sys::socket::{
+ sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
+ SockaddrIn, TimestampingFlag,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )?;
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::SOCK_NONBLOCK,
+ None,
+ )?;
+
+ crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?;
+
+ setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?;
+
+ let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
+
+ let mut recv_buf = vec![0; 1024];
+
+ let mut recv_iovs = Vec::new();
+ let mut pkt_iovs = Vec::new();
+
+ for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
+ pkt_iovs.push(IoSliceMut::new(chunk));
+ if ix % 2 == 1 {
+ recv_iovs.push(pkt_iovs);
+ pkt_iovs = Vec::new();
+ }
+ }
+ drop(pkt_iovs);
+
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+
+ let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
+ sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
+
+ let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
+
+ let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
+
+ let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?;
+
+ for rmsg in recv {
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ let mut saw_time = false;
+ let mut recvd = 0;
+ for cmsg in rmsg.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
+ let ts = timestamps.system;
+
+ let sys_time =
+ crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ {
+ saw_time = true;
+ }
+ }
+ }
+
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ assert!(saw_time);
+
+ for iov in rmsg.iovs() {
+ recvd += iov.len();
+ }
+ assert_eq!(recvd, 400);
+ }
+
+ Ok(())
+ }
+}
+unsafe fn read_mhdr<'a, 'i, S>(
+ mhdr: msghdr,
+ r: isize,
+ msg_controllen: usize,
+ mut address: S,
+) -> RecvMsg<'a, 'i, S>
+ where S: SockaddrLike
+{
+ // 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()
+ };
+
+ // Ignore errors if this socket address has statically-known length
+ //
+ // This is to ensure that unix socket addresses have their length set appropriately.
+ let _ = address.set_length(mhdr.msg_namelen as usize);
+
+ RecvMsg {
+ bytes: r as usize,
+ cmsghdr,
+ address: Some(address),
+ flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
+ mhdr,
+ iobufs: std::marker::PhantomData,
+ }
+}
+
+/// 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: *mut IoSliceMut,
+ iov_buffer_len: usize,
+ cmsg_buffer: *mut 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 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_mut_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, 'outer, S>>
+ where S: SockaddrLike + 'a,
+ 'inner: 'outer
+{
+ let mut address = mem::MaybeUninit::uninit();
+
+ let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
+ .map(|v| (v.as_mut_ptr(), v.capacity()))
+ .unwrap_or((ptr::null_mut(), 0));
+ let mut mhdr = unsafe {
+ pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
+ };
+
+ let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
+
+ let r = Errno::result(ret)?;
+
+ Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
+}
+}
+
+/// 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<OwnedFd> {
+ 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) };
+
+ match res {
+ -1 => Err(Errno::last()),
+ fd => {
+ // Safe because libc::socket returned success
+ unsafe { Ok(OwnedFd::from_raw_fd(fd)) }
+ }
+ }
+}
+
+/// 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<(OwnedFd, OwnedFd)> {
+ 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)?;
+
+ // Safe because socketpair returned success.
+ unsafe {
+ Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])))
+ }
+}
+
+/// Listen for connections on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
+pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
+ let fd = sock.as_fd().as_raw_fd();
+ let res = unsafe { libc::listen(fd, backlog as c_int) };
+
+ Errno::result(res).map(drop)
+}
+
+/// 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_mut_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_mut_ptr() as *mut c_void,
+ buf.len() as size_t,
+ 0,
+ addr.as_mut_ptr() as *mut sockaddr,
+ &mut len as *mut socklen_t,
+ ))? as usize;
+
+ Ok((
+ ret,
+ T::from_raw(
+ addr.assume_init().as_ptr(),
+ Some(len),
+ ),
+ ))
+ }
+}
+
+/// 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<F: AsFd>(&self, fd: &F) -> 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<F: AsFd>(&self, fd: &F, 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<F: AsFd, O: GetSockOpt>(fd: &F, 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;
+///
+/// let listener = TcpListener::bind("0.0.0.0:0").unwrap();
+/// let fd = listener;
+/// let res = setsockopt(&fd, KeepAlive, &true);
+/// assert!(res.is_ok());
+/// ```
+pub fn setsockopt<F: AsFd, O: SetSockOpt>(
+ fd: &F,
+ 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 {
+ #[cfg(not(target_os = "redox"))]
+ #[test]
+ fn can_use_cmsg_space() {
+ let _ = cmsg_space!(u8);
+ }
+
+ #[cfg(not(any(
+ target_os = "redox",
+ target_os = "linux",
+ target_os = "android"
+ )))]
+ #[test]
+ fn can_open_routing_socket() {
+ let _ = super::socket(
+ super::AddressFamily::Route,
+ super::SockType::Raw,
+ super::SockFlag::empty(),
+ None,
+ )
+ .expect("Failed to open routing socket");
+ }
+}