Quelle mod.rs
Sprache: unbekannt
|
|
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
//! Socket interface functions
//!
//! [Further reading]( https://man7.org/linux/man-pages/man7/socket.7.html)
#[cfg(any(target_os = "freebsd", linux_android))]
#[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, size_t, socklen_t};
#[cfg(all(feature = "uio", not(target_os = "redox")))]
use libc::{
c_void, iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE,
MSG_CTRUNC,
};
#[cfg(not(target_os = "redox"))]
use std::io::{IoSlice, IoSliceMut};
#[cfg(feature = "net")]
use std::net;
use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd};
use std::{mem, ptr};
#[deny(missing_docs)]
mod addr;
#[deny(missing_docs)]
pub mod sockopt;
/*
*
* ===== Re-exports =====
*
*/
pub use self::addr::{SockaddrLike, SockaddrStorage};
#[cfg(solarish)]
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(solarish))]
pub use self::addr::{AddressFamily, UnixAddr};
#[cfg(not(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox")))]
#[cfg(feature = "net")]
pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
#[cfg(any(solarish, target_os = "haiku", target_os = "hurd", target_os = "redox"))]
#[cfg(feature = "net")]
pub use self::addr::{SockaddrIn, SockaddrIn6};
#[cfg(linux_android)]
pub use crate::sys::socket::addr::alg::AlgAddr;
#[cfg(linux_android)]
pub use crate::sys::socket::addr::netlink::NetlinkAddr;
#[cfg(apple_targets)]
#[cfg(feature = "ioctl")]
pub use crate::sys::socket::addr::sys_control::SysControlAddr;
#[cfg(any(linux_android, apple_targets))]
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 to configure and control a KEXT
/// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
#[cfg(apple_targets)]
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(linux_android)]
NetlinkRoute = libc::NETLINK_ROUTE,
/// Reserved for user-mode socket protocols
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
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(linux_android)]
NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
/// Netfilter/iptables ULOG.
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkNFLOG = libc::NETLINK_NFLOG,
/// SELinux event notifications.
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkSELinux = libc::NETLINK_SELINUX,
/// Open-iSCSI
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkISCSI = libc::NETLINK_ISCSI,
/// Auditing
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkAudit = libc::NETLINK_AUDIT,
/// Access to FIB lookup from user space
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
/// Netfilter subsystem
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkNetFilter = libc::NETLINK_NETFILTER,
/// SCSI Transports
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
/// Infiniband RDMA
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
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(linux_android)]
NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
/// DECnet routing messages
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
/// Kernel messages to user space
/// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
#[cfg(linux_android)]
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(linux_android)]
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(linux_android)]
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(linux_android)]
EthAll = (libc::ETH_P_ALL as u16).to_be() as i32,
/// ICMP protocol ([icmp(7)](https://man7.org/linux/man-pages/man7/icmp.7.html))
Icmp = libc::IPPROTO_ICMP,
/// ICMPv6 protocol (ICMP over IPv6)
IcmpV6 = libc::IPPROTO_ICMPV6,
}
impl SockProtocol {
/// The Controller Area Network raw socket protocol
/// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
#[cfg(target_os = "linux")]
#[allow(non_upper_case_globals)]
pub const CanRaw: SockProtocol = SockProtocol::Icmp; // Matches libc::CAN_RAW
/// The Controller Area Network broadcast manager protocol
/// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
#[cfg(target_os = "linux")]
#[allow(non_upper_case_globals)]
pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; // Matches libc::CAN_BCM
/// 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(apple_targets)]
#[allow(non_upper_case_globals)]
pub const KextEvent: SockProtocol = SockProtocol::Icmp; // Matches libc::SYSPROTO_EVENT
}
#[cfg(linux_android)]
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(linux_android,
freebsdlike,
netbsdlike,
solarish))]
SOCK_NONBLOCK;
/// Set close-on-exec on the new descriptor
#[cfg(any(linux_android,
freebsdlike,
netbsdlike,
solarish))]
SOCK_CLOEXEC;
/// Return `EPIPE` instead of raising `SIGPIPE`
#[cfg(target_os = "netbsd")]
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")]
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"))]
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(linux_android)]
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(linux_android, freebsdlike, netbsdlike))]
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(linux_android,
freebsdlike,
solarish,
netbsdlike,
target_os = "fuchsia",
target_os = "haiku"))]
MSG_NOSIGNAL;
/// Turns on [`MSG_DONTWAIT`] after the first message has been received (only for
/// `recvmmsg()`).
#[cfg(any(linux_android,
netbsdlike,
target_os = "fuchsia",
target_os = "freebsd"))]
MSG_WAITFORONE;
}
}
#[cfg(target_os = "freebsd")]
libc_enum! {
/// A selector for which clock to use when generating packet timestamps.
/// Used when setting [`TsClock`](crate::sys::socket::sockopt::TsClock) on a socket.
/// (For more details, see [setsockopt(2)](https://man.freebsd.org/cgi/man.cgi?setsockopt)).
#[repr(i32)]
#[non_exhaustive]
pub enum SocketTimestamp {
/// Microsecond resolution, realtime. This is the default.
SO_TS_REALTIME_MICRO,
/// Sub-nanosecond resolution, realtime.
SO_TS_BINTIME,
/// Nanosecond resolution, realtime.
SO_TS_REALTIME,
/// Nanosecond resolution, monotonic.
SO_TS_MONOTONIC,
}
}
cfg_if! {
if #[cfg(linux_android)] {
/// 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(freebsdlike)] {
/// 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(),
self.0.cmcred_ngroups as _
)
}
}
}
impl From<libc::cmsgcred> for UnixCredentials {
fn from(cred: libc::cmsgcred) -> Self {
UnixCredentials(cred)
}
}
}
}
cfg_if! {
if #[cfg(any(freebsdlike, apple_targets))] {
/// 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);
/// # }
/// ```
#[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 const 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. If
/// allocated space for CMSGs was too small it is not safe to iterate,
/// instead return an `Error::ENOBUFS` error.
pub fn cmsgs(&self) -> Result<CmsgIterator> {
if self.mhdr.msg_flags & MSG_CTRUNC == MSG_CTRUNC {
return Err(Errno::ENOBUFS);
}
Ok(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`].
///
/// [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(linux_android)]
ScmCredentials(UnixCredentials),
/// Received version of [`ControlMessage::ScmCreds`]
#[cfg(freebsdlike)]
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().unwrap().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(linux_android)]
ScmTimestampsns(Timestamps),
/// Nanoseconds resolution timestamp
///
/// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
#[cfg(linux_android)]
ScmTimestampns(TimeSpec),
/// Realtime clock timestamp
///
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
#[cfg(target_os = "freebsd")]
ScmRealtime(TimeSpec),
/// Monotonic clock timestamp
///
/// [Further reading](https://man.freebsd.org/cgi/man.cgi?setsockopt)
#[cfg(target_os = "freebsd")]
ScmMonotonic(TimeSpec),
#[cfg(any(linux_android, apple_targets, target_os = "netbsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4PacketInfo(libc::in_pktinfo),
#[cfg(any(linux_android, bsd))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6PacketInfo(libc::in6_pktinfo),
#[cfg(bsd)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvIf(libc::sockaddr_dl),
#[cfg(bsd)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4RecvDstAddr(libc::in_addr),
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4OrigDstAddr(libc::sockaddr_in),
#[cfg(any(linux_android, target_os = "freebsd"))]
#[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(i32),
/// 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(linux_android, target_os = "fuchsia"))]
RxqOvfl(u32),
/// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
#[cfg(linux_android)]
#[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(linux_android)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
/// `SOL_TLS` messages of type `TLS_GET_RECORD_TYPE`
#[cfg(any(target_os = "linux"))]
TlsGetRecordType(TlsGetRecordType),
/// Catch-all variant for unimplemented cmsg types.
#[doc(hidden)]
Unknown(UnknownCmsg),
}
/// For representing packet timestamps via `SO_TIMESTAMPING` interface
#[cfg(linux_android)]
#[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,
}
/// These constants correspond to TLS 1.2 message types, as defined in
/// RFC 5246, Appendix A.1
#[cfg(any(target_os = "linux"))]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
#[non_exhaustive]
pub enum TlsGetRecordType {
ChangeCipherSpec ,
Alert,
Handshake,
ApplicationData,
Unknown(u8),
}
#[cfg(any(target_os = "linux"))]
impl From<u8> for TlsGetRecordType {
fn from(x: u8) -> Self {
match x {
20 => TlsGetRecordType::ChangeCipherSpec,
21 => TlsGetRecordType::Alert,
22 => TlsGetRecordType::Handshake,
23 => TlsGetRecordType::ApplicationData,
_ => TlsGetRecordType::Unknown(x),
}
}
}
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 = unsafe { 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 {
unsafe {
let fdp = (p as *const RawFd).add(i);
fds.push(ptr::read_unaligned(fdp));
}
}
ControlMessageOwned::ScmRights(fds)
},
#[cfg(linux_android)]
(libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
let cred: libc::ucred = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmCredentials(cred.into())
}
#[cfg(freebsdlike)]
(libc::SOL_SOCKET, libc::SCM_CREDS) => {
let cred: libc::cmsgcred = unsafe { 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 = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
},
#[cfg(linux_android)]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
}
#[cfg(target_os = "freebsd")]
(libc::SOL_SOCKET, libc::SCM_REALTIME) => {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmRealtime(TimeSpec::from(ts))
}
#[cfg(target_os = "freebsd")]
(libc::SOL_SOCKET, libc::SCM_MONOTONIC) => {
let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::ScmMonotonic(TimeSpec::from(ts))
}
#[cfg(linux_android)]
(libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
let tp = p as *const libc::timespec;
let ts: libc::timespec = unsafe { ptr::read_unaligned(tp) };
let system = TimeSpec::from(ts);
let ts: libc::timespec = unsafe { ptr::read_unaligned(tp.add(1)) };
let hw_trans = TimeSpec::from(ts);
let ts: libc::timespec = unsafe { 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 = "freebsd", linux_android, apple_targets))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
let info = unsafe { ptr::read_unaligned(p as *const libc::in6_pktinfo) };
ControlMessageOwned::Ipv6PacketInfo(info)
}
#[cfg(any(linux_android, apple_targets, target_os = "netbsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_PKTINFO) => {
let info = unsafe { ptr::read_unaligned(p as *const libc::in_pktinfo) };
ControlMessageOwned::Ipv4PacketInfo(info)
}
#[cfg(bsd)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVIF) => {
let dl = unsafe { ptr::read_unaligned(p as *const libc::sockaddr_dl) };
ControlMessageOwned::Ipv4RecvIf(dl)
},
#[cfg(bsd)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
let dl = unsafe { ptr::read_unaligned(p as *const libc::in_addr) };
ControlMessageOwned::Ipv4RecvDstAddr(dl)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
let dl = unsafe { 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: i32 = unsafe { ptr::read_unaligned(p as *const _) };
ControlMessageOwned::UdpGroSegments(gso_size)
},
#[cfg(any(linux_android, target_os = "fuchsia"))]
(libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
let drop_counter = unsafe { ptr::read_unaligned(p as *const u32) };
ControlMessageOwned::RxqOvfl(drop_counter)
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVERR) => {
let (err, addr) = unsafe { Self::recv_err_helper::<sockaddr_in>(p, len) };
ControlMessageOwned::Ipv4RecvErr(err, addr)
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
let (err, addr) = unsafe { Self::recv_err_helper::<sockaddr_in6>(p, len) };
ControlMessageOwned::Ipv6RecvErr(err, addr)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
let dl = unsafe { ptr::read_unaligned(p as *const libc::sockaddr_in6) };
ControlMessageOwned::Ipv6OrigDstAddr(dl)
},
#[cfg(any(target_os = "linux"))]
(libc::SOL_TLS, libc::TLS_GET_RECORD_TYPE) => {
let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::TlsGetRecordType(content_type.into())
},
(_, _) => {
let sl = unsafe { std::slice::from_raw_parts(p, len) };
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
ControlMessageOwned::Unknown(ucmsg)
}
}
}
#[cfg(linux_android)]
#[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 = unsafe { 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 = unsafe { libc::SO_EE_OFFENDER(ee) as *const T };
if unsafe { addrp.offset(1) } as usize - (p as usize) > len {
(err, None)
} else {
(err, Some(unsafe { ptr::read_unaligned(addrp) }))
}
}
}
/// A type-safe zero-copy wrapper around a single control message, as used with
/// [`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(linux_android)]
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(freebsdlike)]
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(linux_android)]
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(linux_android)]
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(linux_android)]
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(linux_android, target_os = "netbsd", apple_targets))]
#[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(linux_android,
target_os = "netbsd",
target_os = "freebsd",
apple_targets))]
#[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(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4SendSrcAddr(&'a libc::in_addr),
/// Configure the hop limit for v6 multicast traffic.
///
/// Set the IPv6 hop limit for this message. The argument is an integer
/// between 0 and 255. A value of -1 will set the hop limit to the route
/// default if possible on the interface. Without this cmsg, packets sent
/// with sendmsg have a hop limit of 1 and will not leave the local network.
/// For further information, please refer to the
/// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6HopLimit(&'a libc::c_int),
/// 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(linux_android, target_os = "fuchsia"))]
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(linux_android)]
ControlMessage::ScmCredentials(creds) => {
&creds.0 as *const libc::ucred as *const u8
}
#[cfg(freebsdlike)]
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(linux_android)]
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(linux_android)]
ControlMessage::AlgSetOp(op) => {
op as *const _ as *const u8
},
#[cfg(linux_android)]
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(linux_android, target_os = "netbsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(linux_android, target_os = "netbsd",
target_os = "freebsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => limit as *const _ as *const u8,
#[cfg(any(linux_android, target_os = "fuchsia"))]
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(linux_android)]
ControlMessage::ScmCredentials(creds) => {
mem::size_of_val(creds)
}
#[cfg(freebsdlike)]
ControlMessage::ScmCreds => {
mem::size_of::<libc::cmsgcred>()
}
#[cfg(linux_android)]
ControlMessage::AlgSetIv(iv) => {
mem::size_of::<&[u8]>() + iv.len()
},
#[cfg(linux_android)]
ControlMessage::AlgSetOp(op) => {
mem::size_of_val(op)
},
#[cfg(linux_android)]
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(linux_android, target_os = "netbsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(linux_android, target_os = "netbsd",
target_os = "freebsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => {
mem::size_of_val(limit)
},
#[cfg(any(linux_android, target_os = "fuchsia"))]
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(linux_android)]
ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
#[cfg(freebsdlike)]
ControlMessage::ScmCreds => libc::SOL_SOCKET,
#[cfg(linux_android)]
ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
#[cfg(any(linux_android, target_os = "netbsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, target_os = "netbsd",
target_os = "freebsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPPROTO_IPV6,
#[cfg(any(linux_android, target_os = "fuchsia"))]
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(linux_android)]
ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
#[cfg(freebsdlike)]
ControlMessage::ScmCreds => libc::SCM_CREDS,
#[cfg(linux_android)]
ControlMessage::AlgSetIv(_) => {
libc::ALG_SET_IV
},
#[cfg(linux_android)]
ControlMessage::AlgSetOp(_) => {
libc::ALG_SET_OP
},
#[cfg(linux_android)]
ControlMessage::AlgSetAeadAssoclen(_) => {
libc::ALG_SET_AEAD_ASSOCLEN
},
#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
ControlMessage::UdpGsoSegments(_) => {
libc::UDP_SEGMENT
},
#[cfg(any(linux_android, target_os = "netbsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
#[cfg(any(linux_android, target_os = "netbsd",
target_os = "freebsd", apple_targets))]
#[cfg(feature = "net")]
ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPV6_HOPLIMIT,
#[cfg(any(linux_android, target_os = "fuchsia"))]
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) {
unsafe {
(*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.as_raw_fd()];
/// 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.as_raw_fd()];
/// 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(linux_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().cast_mut().cast();
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).cast_mut().cast();
// 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) };
}
// Doing an unchecked addition is alright here, as the only way to obtain an instance of `MultiHeaders`
// is through the `preallocate` function, which takes an `usize` as an argument to define its size,
// which also provides an upper bound for the size of this zipped iterator. Thus, `i < usize::MAX` or in
// other words: `count` doesn't overflow
count = i + 1;
}
// SAFETY: all pointers are guaranteed to be valid for the scope of this function. `count` does represent the
// maximum number of messages that can be sent safely (i.e. `count` is the minimum of the sizes of `slices`,
// `data.items` and `addrs`)
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(linux_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(linux_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
--> --------------------
--> maximum size reached
--> --------------------
[ Dauer der Verarbeitung: 0.68 Sekunden
]
|
2026-04-04
|