//! 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::convert::{TryFrom, TryInto}; 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(not(any(target_os = "illumos", target_os = "solaris")))] #[allow(deprecated)] pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; #[cfg(any(target_os = "illumos", target_os = "solaris"))] #[allow(deprecated)] pub use self::addr::{AddressFamily, SockAddr, UnixAddr}; #[allow(deprecated)] #[cfg(not(any( target_os = "illumos", target_os = "solaris", target_os = "haiku" )))] #[cfg(feature = "net")] pub use self::addr::{ InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, LinkAddr, SockaddrIn, SockaddrIn6, }; #[allow(deprecated)] #[cfg(any( target_os = "illumos", target_os = "solaris", target_os = "haiku" ))] #[cfg(feature = "net")] pub use self::addr::{ InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, 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 for SockType { type Error = crate::Error; fn try_from(x: i32) -> Result { 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. #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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. #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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. #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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. #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_DONTWAIT; /// Receive flags: Control Data was discarded (buffer too small) #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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). #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 MSG_TRUNC; /// Terminates a record (when this notion is supported, as for /// sockets of type [`SeqPacket`](enum.SockType.html)). #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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())))] #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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())))] #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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())))] #[allow(deprecated)] // Suppress useless warnings from libc PR 2963 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 for UnixCredentials { fn from(cred: libc::ucred) -> Self { UnixCredentials(cred) } } impl From 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 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) -> 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::::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, 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 { 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), /// 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::(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), /// 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), /// 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::(); 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::(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::(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::::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(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option) { 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); 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::() } #[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; /// 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(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage], flags: MsgFlags, addr: Option<&S>) -> Result 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, slices: XS, // one address per group of slices addrs: AS, // shared across all the messages cmsgs: C, flags: MsgFlags ) -> crate::Result> where XS: IntoIterator, AS: AsRef<[Option]>, 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 { // preallocated boxed slice of mmsghdr items: Box<[libc::mmsghdr]>, addresses: Box<[mem::MaybeUninit]>, // while we are not using it directly - this is used to store control messages // and we retain pointers to them inside items array #[allow(dead_code)] cmsg_buffers: Option>, msg_controllen: usize, } #[cfg(any( target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", ))] impl MultiHeaders { /// 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>) -> 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::>(); Self { items: items.into_boxed_slice(), addresses, 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, slices: XS, flags: MsgFlags, mut timeout: Option, ) -> crate::Result> where XS: IntoIterator, 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, 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 { 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 { 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::>(); 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( 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::::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::::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>, flags: MsgFlags) -> Result> 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>>( domain: AddressFamily, ty: SockType, flags: SockFlag, protocol: T, ) -> Result { 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>>( 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 { 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 { 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 { 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( sockfd: RawFd, buf: &mut [u8], ) -> Result<(usize, Option)> { unsafe { let mut addr = mem::MaybeUninit::::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 libc::sockaddr, &mut len as *mut socklen_t, ))? as usize; Ok(( ret, T::from_raw( addr.assume_init().as_ptr() as *const libc::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 { 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 { 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; } /// 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(fd: RawFd, opt: O) -> Result { 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( 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(fd: RawFd) -> Result { unsafe { let mut addr = mem::MaybeUninit::::uninit(); let mut len = T::size(); let ret = libc::getpeername( fd, addr.as_mut_ptr() as *mut libc::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(fd: RawFd) -> Result { unsafe { let mut addr = mem::MaybeUninit::::uninit(); let mut len = T::size(); let ret = libc::getsockname( fd, addr.as_mut_ptr() as *mut libc::sockaddr, &mut len, ); Errno::result(ret)?; T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL) } } /// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a /// certain size. /// /// In C this would usually be done by casting. The `len` argument /// should be the number of bytes in the `sockaddr_storage` that are actually /// allocated and valid. It must be at least as large as all the useful parts /// of the structure. Note that in the case of a `sockaddr_un`, `len` need not /// include the terminating null. #[deprecated( since = "0.24.0", note = "use SockaddrLike or SockaddrStorage instead" )] #[allow(deprecated)] pub fn sockaddr_storage_to_addr( addr: &sockaddr_storage, len: usize, ) -> Result { assert!(len <= mem::size_of::()); if len < mem::size_of_val(&addr.ss_family) { return Err(Errno::ENOTCONN); } match c_int::from(addr.ss_family) { #[cfg(feature = "net")] libc::AF_INET => { assert!(len >= mem::size_of::()); let sin = unsafe { *(addr as *const sockaddr_storage as *const sockaddr_in) }; Ok(SockAddr::Inet(InetAddr::V4(sin))) } #[cfg(feature = "net")] libc::AF_INET6 => { assert!(len >= mem::size_of::()); let sin6 = unsafe { *(addr as *const _ as *const sockaddr_in6) }; Ok(SockAddr::Inet(InetAddr::V6(sin6))) } libc::AF_UNIX => unsafe { let sun = *(addr as *const _ as *const sockaddr_un); let sun_len = len.try_into().unwrap(); Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len))) }, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg(feature = "net")] libc::AF_PACKET => { use libc::sockaddr_ll; // Don't assert anything about the size. // Apparently the Linux kernel can return smaller sizes when // the value in the last element of sockaddr_ll (`sll_addr`) is // smaller than the declared size of that field let sll = unsafe { *(addr as *const _ as *const sockaddr_ll) }; Ok(SockAddr::Link(LinkAddr(sll))) } #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_NETLINK => { use libc::sockaddr_nl; let snl = unsafe { *(addr as *const _ as *const sockaddr_nl) }; Ok(SockAddr::Netlink(NetlinkAddr(snl))) } #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_ALG => { use libc::sockaddr_alg; let salg = unsafe { *(addr as *const _ as *const sockaddr_alg) }; Ok(SockAddr::Alg(AlgAddr(salg))) } #[cfg(any(target_os = "android", target_os = "linux"))] libc::AF_VSOCK => { use libc::sockaddr_vm; let svm = unsafe { *(addr as *const _ as *const sockaddr_vm) }; Ok(SockAddr::Vsock(VsockAddr(svm))) } af => panic!("unexpected address family {}", af), } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum Shutdown { /// 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); } }