From 07316cd6ea15b344ba6672478800ca1460779a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ad=C3=A4?= Date: Tue, 14 Nov 2023 18:24:36 +0100 Subject: [PATCH] socket api --- src/ifaddrs.rs | 128 +- src/sys/socket/addr.rs | 4249 +++++++++++++++++++++++-------------- src/sys/socket/mod.rs | 2393 +++++++++++++++------ src/sys/socket/sockopt.rs | 43 +- test/sys/test_socket.rs | 1350 +++++++----- test/sys/test_sockopt.rs | 80 +- 6 files changed, 5321 insertions(+), 2922 deletions(-) diff --git a/src/ifaddrs.rs b/src/ifaddrs.rs index e90b26d012..a5f70918ad 100644 --- a/src/ifaddrs.rs +++ b/src/ifaddrs.rs @@ -2,34 +2,32 @@ //! //! Uses the Linux and/or BSD specific function `getifaddrs` to query the list //! of interfaces and their associated addresses. - use cfg_if::cfg_if; -#[cfg(apple_targets)] -use std::convert::TryFrom; use std::ffi; use std::iter::Iterator; +use std::marker::PhantomData; use std::mem; use std::option::Option; use crate::net::if_::*; -use crate::sys::socket::{SockaddrLike, SockaddrStorage}; +use crate::sys::socket::RawAddr; use crate::{Errno, Result}; /// Describes a single address for an interface as returned by `getifaddrs`. -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub struct InterfaceAddress { +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct InterfaceAddress<'a> { /// Name of the network interface pub interface_name: String, /// Flags as from `SIOCGIFFLAGS` ioctl pub flags: InterfaceFlags, /// Network address of this interface - pub address: Option, + pub address: Option>, /// Netmask of this interface - pub netmask: Option, + pub netmask: Option>, /// Broadcast address of this interface, if applicable - pub broadcast: Option, + pub broadcast: Option>, /// Point-to-point destination address - pub destination: Option, + pub destination: Option>, } cfg_if! { @@ -44,54 +42,12 @@ cfg_if! { } } -/// Workaround a bug in XNU where netmasks will always have the wrong size in -/// the sa_len field due to the kernel ignoring trailing zeroes in the structure -/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470 -/// -/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and -/// memcpy sa_len of the netmask to that new storage. Finally, we reset the -/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all -/// members of the sockaddr_storage are "ok" with being zeroed out (there are -/// no pointers). -#[cfg(apple_targets)] -unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option { - let src_sock = info.ifa_netmask; - if src_sock.is_null() { - return None; - } - - let mut dst_sock = mem::MaybeUninit::::zeroed(); - - let dst_sock = unsafe { - // memcpy only sa_len bytes, assume the rest is zero - std::ptr::copy_nonoverlapping( - src_sock as *const u8, - dst_sock.as_mut_ptr().cast(), - (*src_sock).sa_len.into(), - ); - - // Initialize ss_len to sizeof(libc::sockaddr_storage). - (*dst_sock.as_mut_ptr()).ss_len = - u8::try_from(mem::size_of::()).unwrap(); - dst_sock.assume_init() - }; - - let dst_sock_ptr = - &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr; - - unsafe { SockaddrStorage::from_raw(dst_sock_ptr, None) } -} - -impl InterfaceAddress { +impl<'a> InterfaceAddress<'a> { /// Create an `InterfaceAddress` from the libc struct. fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress { let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) }; - let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) }; - #[cfg(apple_targets)] - let netmask = unsafe { workaround_xnu_bug(info) }; - #[cfg(not(apple_targets))] - let netmask = - unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) }; + let address = unsafe { RawAddr::new(&*info.ifa_addr) }; + let netmask = unsafe { RawAddr::new(&*info.ifa_netmask) }; let mut addr = InterfaceAddress { interface_name: ifname.to_string_lossy().to_string(), flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32), @@ -103,9 +59,9 @@ impl InterfaceAddress { let ifu = get_ifu_from_sockaddr(info); if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { - addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) }; + addr.destination = unsafe { RawAddr::new(&*ifu) }; } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) { - addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) }; + addr.broadcast = unsafe { RawAddr::new(&*ifu) }; } addr @@ -114,23 +70,43 @@ impl InterfaceAddress { /// Holds the results of `getifaddrs`. /// -/// Use the function `getifaddrs` to create this Iterator. Note that the -/// actual list of interfaces can be iterated once and will be freed as -/// soon as the Iterator goes out of scope. -#[derive(Debug, Eq, Hash, PartialEq)] -pub struct InterfaceAddressIterator { +/// Use the function `getifaddrs` to create this struct and [`Self::iter`] +/// to create the iterator. Note that the actual list of interfaces can be +/// iterated once and will be freed as soon as the Iterator goes out of scope. +#[derive(Debug)] +pub struct InterfaceAddresses { base: *mut libc::ifaddrs, - next: *mut libc::ifaddrs, } -impl Drop for InterfaceAddressIterator { +impl InterfaceAddresses { + /// Create an iterator over the list of interfaces. + pub fn iter(&self) -> InterfaceAddressIterator<'_> { + InterfaceAddressIterator { + next: self.base, + _a: PhantomData, + } + } +} + +impl Drop for InterfaceAddresses { fn drop(&mut self) { unsafe { libc::freeifaddrs(self.base) }; } } -impl Iterator for InterfaceAddressIterator { - type Item = InterfaceAddress; +/// Holds the results of `getifaddrs`. +/// +/// Use the function `getifaddrs` to create this Iterator. Note that the +/// actual list of interfaces can be iterated once and will be freed as +/// soon as the Iterator goes out of scope. +#[derive(Debug, Eq, Hash, PartialEq)] +pub struct InterfaceAddressIterator<'a> { + next: *mut libc::ifaddrs, + _a: PhantomData<&'a ()>, +} + +impl<'a> Iterator for InterfaceAddressIterator<'a> { + type Item = InterfaceAddress<'a>; fn next(&mut self) -> Option<::Item> { match unsafe { self.next.as_ref() } { Some(ifaddr) => { @@ -154,7 +130,7 @@ impl Iterator for InterfaceAddressIterator { /// # Example /// ``` /// let addrs = nix::ifaddrs::getifaddrs().unwrap(); -/// for ifaddr in addrs { +/// for ifaddr in addrs.iter() { /// match ifaddr.address { /// Some(address) => { /// println!("interface {} address {}", @@ -167,13 +143,12 @@ impl Iterator for InterfaceAddressIterator { /// } /// } /// ``` -pub fn getifaddrs() -> Result { +pub fn getifaddrs() -> Result { let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit(); unsafe { Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| { - InterfaceAddressIterator { + InterfaceAddresses { base: addrs.assume_init(), - next: addrs.assume_init(), } }) } @@ -182,6 +157,7 @@ pub fn getifaddrs() -> Result { #[cfg(test)] mod tests { use super::*; + use crate::sys::socket::AddressFamily; // Only checks if `getifaddrs` can be invoked without panicking. #[test] @@ -194,19 +170,17 @@ mod tests { #[test] fn test_getifaddrs_netmask_correct() { let addrs = getifaddrs().unwrap(); - for iface in addrs { + for iface in addrs.iter() { let sock = if let Some(sock) = iface.netmask { sock } else { continue; }; - if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) { - let _ = sock.as_sockaddr_in().unwrap(); + if sock.family() == AddressFamily::INET { + let _ = sock.to_ipv4().unwrap(); return; - } else if sock.family() - == Some(crate::sys::socket::AddressFamily::Inet6) - { - let _ = sock.as_sockaddr_in6().unwrap(); + } else if sock.family() == AddressFamily::INET6 { + let _ = sock.to_ipv6().unwrap(); return; } } diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index eb39d97f88..51102a5262 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -1,41 +1,46 @@ +#[cfg(not(any( + target_os = "solaris", + target_os = "redox", +)))] +#[cfg(feature = "net")] +pub use self::datalink::LinkAddress; #[cfg(any( - target_os = "android", target_os = "dragonfly", target_os = "freebsd", apple_targets, - target_os = "linux", target_os = "illumos", target_os = "netbsd", - target_os = "openbsd", target_os = "haiku", - target_os = "fuchsia", target_os = "aix", + target_os = "openbsd" ))] #[cfg(feature = "net")] pub use self::datalink::LinkAddr; -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" -))] -pub use self::vsock::VsockAddr; +#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] +pub use self::vsock::VsockAddress; use super::sa_family_t; use crate::errno::Errno; #[cfg(any(target_os = "android", target_os = "linux"))] -use crate::sys::socket::addr::alg::AlgAddr; +use crate::sys::socket::addr::alg::AlgAddress; #[cfg(any(target_os = "android", target_os = "linux"))] -use crate::sys::socket::addr::netlink::NetlinkAddr; -#[cfg(all(feature = "ioctl", apple_targets))] -use crate::sys::socket::addr::sys_control::SysControlAddr; +use crate::sys::socket::addr::netlink::NetlinkAddress; +#[cfg(all( + feature = "ioctl", + any(target_os = "ios", target_os = "macos") +))] +use crate::sys::socket::addr::sys_control::SysControlAddress; use crate::{NixPath, Result}; use cfg_if::cfg_if; use memoffset::offset_of; -use std::convert::TryInto; +use std::borrow::{Borrow, BorrowMut}; use std::ffi::OsStr; use std::hash::{Hash, Hasher}; -use std::net::{Ipv4Addr, Ipv6Addr}; +use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; use std::path::Path; +use std::ptr::addr_of; +#[allow(unused)] +use std::ptr::addr_of_mut; use std::{fmt, mem, net, ptr, slice}; /// Convert a std::net::Ipv4Addr into the libc form. @@ -50,368 +55,1029 @@ pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr { #[cfg(feature = "net")] pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr { libc::in6_addr { - s6_addr: addr.octets(), + s6_addr: addr.octets() } } -/// These constants specify the protocol family to be used -/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html) -/// -/// # References -/// -/// [address_families(7)](https://man7.org/linux/man-pages/man7/address_families.7.html) -// Should this be u8? -#[repr(i32)] -#[non_exhaustive] -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -pub enum AddressFamily { - /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html)) - Unix = libc::AF_UNIX, - /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html)) - Inet = libc::AF_INET, - /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html)) - Inet6 = libc::AF_INET6, - /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html)) - #[cfg(any(target_os = "android", target_os = "linux"))] - Netlink = libc::AF_NETLINK, - /// Kernel interface for interacting with the routing table - #[cfg(not(any( - target_os = "redox", - target_os = "linux", - target_os = "android" - )))] - Route = libc::PF_ROUTE, - /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html)) +/// A possible error when converting a c_int to [`AddressFamily`]. +#[derive(Debug, Clone, Copy)] +pub struct InvalidAddressFamilyError; + +/// Address families, corresponding to `AF_*` constants in libc. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AddressFamily(libc::c_int); + +impl AddressFamily { + /// Converts a c_int to an address family. + /// + /// The argument must fit into `sa_family_t`. + pub const fn new(family: libc::c_int) -> std::result::Result { + if family > libc::sa_family_t::MAX as _ { + return Err(InvalidAddressFamilyError); + } + + Ok(Self(family)) + } + + /// Returns the c_int representation of the address family. + pub const fn family(&self) -> libc::c_int { + self.0 + } + + const fn of(addr: &libc::sockaddr) -> Self { + Self(addr.sa_family as _) + } +} + +impl AddressFamily { + /// Represents `AF_802`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const _802: Self = Self(libc::AF_802); + /// Represents `AF_ALG`. #[cfg(any( target_os = "android", + target_os = "fuchsia", target_os = "linux", + ))] + pub const ALG: Self = Self(libc::AF_ALG); + /// Represents `AF_APPLETALK`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const APPLETALK: Self = Self(libc::AF_APPLETALK); + /// Represents `AF_ARP`. + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + ))] + pub const ARP: Self = Self(libc::AF_ARP); + /// Represents `AF_ASH`. + #[cfg(any( + target_os = "android", target_os = "fuchsia", - target_os = "solaris" + target_os = "linux", ))] - Packet = libc::AF_PACKET, - /// KEXT Controls and Notifications - #[cfg(apple_targets)] - System = libc::AF_SYSTEM, - /// Amateur radio AX.25 protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Ax25 = libc::AF_AX25, - /// IPX - Novell protocols - #[cfg(not(any(target_os = "aix", target_os = "redox")))] - Ipx = libc::AF_IPX, - /// AppleTalk - #[cfg(not(target_os = "redox"))] - AppleTalk = libc::AF_APPLETALK, - /// AX.25 packet layer protocol. - /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/)) - #[cfg(any(target_os = "android", target_os = "linux"))] - NetRom = libc::AF_NETROM, - /// Can't be used for creating sockets; mostly used for bridge - /// links in - /// [rtnetlink(7)](https://man7.org/linux/man-pages/man7/rtnetlink.7.html) - /// protocol commands. - #[cfg(any(target_os = "android", target_os = "linux"))] - Bridge = libc::AF_BRIDGE, - /// Access to raw ATM PVCs - #[cfg(any(target_os = "android", target_os = "linux"))] - AtmPvc = libc::AF_ATMPVC, - /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html)) - #[cfg(any(target_os = "android", target_os = "linux"))] - X25 = libc::AF_X25, - /// RATS (Radio Amateur Telecommunications Society) Open - /// Systems environment (ROSE) AX.25 packet layer protocol. - /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/)) - #[cfg(any(target_os = "android", target_os = "linux"))] - Rose = libc::AF_ROSE, - /// DECet protocol sockets. - #[cfg(not(any(target_os = "haiku", target_os = "redox")))] - Decnet = libc::AF_DECnet, - /// Reserved for "802.2LLC project"; never used. - #[cfg(any(target_os = "android", target_os = "linux"))] - NetBeui = libc::AF_NETBEUI, - /// This was a short-lived (between Linux 2.1.30 and - /// 2.1.99pre2) protocol family for firewall upcalls. - #[cfg(any(target_os = "android", target_os = "linux"))] - Security = libc::AF_SECURITY, - /// Key management protocol. - #[cfg(any(target_os = "android", target_os = "linux"))] - Key = libc::AF_KEY, - #[allow(missing_docs)] // Not documented anywhere that I can find - #[cfg(any(target_os = "android", target_os = "linux"))] - Ash = libc::AF_ASH, - /// Acorn Econet protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Econet = libc::AF_ECONET, - /// Access to ATM Switched Virtual Circuits - #[cfg(any(target_os = "android", target_os = "linux"))] - AtmSvc = libc::AF_ATMSVC, - /// Reliable Datagram Sockets (RDS) protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Rds = libc::AF_RDS, - /// IBM SNA - #[cfg(not(any(target_os = "haiku", target_os = "redox")))] - Sna = libc::AF_SNA, - /// Socket interface over IrDA - #[cfg(any(target_os = "android", target_os = "linux"))] - Irda = libc::AF_IRDA, - /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE) - #[cfg(any(target_os = "android", target_os = "linux"))] - Pppox = libc::AF_PPPOX, - /// Legacy protocol for wide area network (WAN) connectivity that was used - /// by Sangoma WAN cards - #[cfg(any(target_os = "android", target_os = "linux"))] - Wanpipe = libc::AF_WANPIPE, - /// Logical link control (IEEE 802.2 LLC) protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Llc = libc::AF_LLC, - /// InfiniBand native addressing - #[cfg(all(target_os = "linux", not(target_env = "uclibc")))] - Ib = libc::AF_IB, - /// Multiprotocol Label Switching - #[cfg(all(target_os = "linux", not(target_env = "uclibc")))] - Mpls = libc::AF_MPLS, - /// Controller Area Network automotive bus protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Can = libc::AF_CAN, - /// TIPC, "cluster domain sockets" protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Tipc = libc::AF_TIPC, - /// Bluetooth low-level socket protocol - #[cfg(not(any( - target_os = "aix", + pub const ASH: Self = Self(libc::AF_ASH); + /// Represents `AF_ATM`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + ))] + pub const ATM: Self = Self(libc::AF_ATM); + /// Represents `AF_ATMPVC`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const ATMPVC: Self = Self(libc::AF_ATMPVC); + /// Represents `AF_ATMSVC`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const ATMSVC: Self = Self(libc::AF_ATMSVC); + /// Represents `AF_AX25`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const AX25: Self = Self(libc::AF_AX25); + /// Represents `AF_BLUETOOTH`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ))] + pub const BLUETOOTH: Self = Self(libc::AF_BLUETOOTH); + /// Represents `AF_BRIDGE`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const BRIDGE: Self = Self(libc::AF_BRIDGE); + /// Represents `AF_CAIF`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const CAIF: Self = Self(libc::AF_CAIF); + /// Represents `AF_CAN`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const CAN: Self = Self(libc::AF_CAN); + /// Represents `AF_CCITT`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", target_os = "illumos", - apple_targets, + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", target_os = "solaris", - target_os = "redox", - )))] - Bluetooth = libc::AF_BLUETOOTH, - /// IUCV (inter-user communication vehicle) z/VM protocol for - /// hypervisor-guest interaction - #[cfg(any(target_os = "android", target_os = "linux"))] - Iucv = libc::AF_IUCV, - /// Rx, Andrew File System remote procedure call protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - RxRpc = libc::AF_RXRPC, - /// New "modular ISDN" driver interface protocol - #[cfg(not(any( - target_os = "aix", + ))] + pub const CCITT: Self = Self(libc::AF_CCITT); + /// Represents `AF_CHAOS`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", target_os = "solaris", - target_os = "haiku", - target_os = "redox", - )))] - Isdn = libc::AF_ISDN, - /// Nokia cellular modem IPC/RPC interface - #[cfg(any(target_os = "android", target_os = "linux"))] - Phonet = libc::AF_PHONET, - /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol - #[cfg(any(target_os = "android", target_os = "linux"))] - Ieee802154 = libc::AF_IEEE802154, - /// Ericsson's Communication CPU to Application CPU interface (CAIF) - /// protocol. - #[cfg(any(target_os = "android", target_os = "linux"))] - Caif = libc::AF_CAIF, - /// Interface to kernel crypto API - #[cfg(any(target_os = "android", target_os = "linux"))] - Alg = libc::AF_ALG, - /// Near field communication - #[cfg(target_os = "linux")] - Nfc = libc::AF_NFC, - /// VMWare VSockets protocol for hypervisor-guest interaction. + ))] + pub const CHAOS: Self = Self(libc::AF_CHAOS); + /// Represents `AF_CNT`. #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", ))] - Vsock = libc::AF_VSOCK, - /// ARPANet IMP addresses + pub const CNT: Self = Self(libc::AF_CNT); + /// Represents `AF_COIP`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", ))] - ImpLink = libc::AF_IMPLINK, - /// PUP protocols, e.g. BSP + pub const COIP: Self = Self(libc::AF_COIP); + /// Represents `AF_DATAKIT`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "illumos", + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", ))] - Pup = libc::AF_PUP, - /// MIT CHAOS protocols + pub const DATAKIT: Self = Self(libc::AF_DATAKIT); + /// Represents `AF_DECnet`. #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", ))] - Chaos = libc::AF_CHAOS, - /// Novell and Xerox protocol - #[cfg(any(apple_targets, target_os = "netbsd", target_os = "openbsd"))] - Ns = libc::AF_NS, - #[allow(missing_docs)] // Not documented anywhere that I can find + pub const DEC_NET: Self = Self(libc::AF_DECnet); + /// Represents `AF_DLI`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", ))] - Iso = libc::AF_ISO, - /// Bell Labs virtual circuit switch ? + pub const DLI: Self = Self(libc::AF_DLI); + /// Represents `AF_E164`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + ))] + pub const E164: Self = Self(libc::AF_E164); + /// Represents `AF_ECMA`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const ECMA: Self = Self(libc::AF_ECMA); + /// Represents `AF_ECONET`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const ECONET: Self = Self(libc::AF_ECONET); + /// Represents `AF_ENCAP`. + #[cfg(target_os = "openbsd")] + pub const ENCAP: Self = Self(libc::AF_ENCAP); + /// Represents `AF_FILE`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", ))] - Datakit = libc::AF_DATAKIT, - /// CCITT protocols, X.25 etc + pub const FILE: Self = Self(libc::AF_FILE); + /// Represents `AF_GOSIP`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const GOSIP: Self = Self(libc::AF_GOSIP); + /// Represents `AF_HYLINK`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "illumos", + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", ))] - Ccitt = libc::AF_CCITT, - /// DEC Direct data link interface + pub const HYLINK: Self = Self(libc::AF_HYLINK); + /// Represents `AF_IB`. + #[cfg(all(target_os = "linux", not(target_env = "uclibc")))] + pub const IB: Self = Self(libc::AF_IB); + /// Represents `AF_IEEE80211`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" ))] - Dli = libc::AF_DLI, - #[allow(missing_docs)] // Not documented anywhere that I can find + pub const IEEE80211: Self = Self(libc::AF_IEEE80211); + /// Represents `AF_IEEE802154`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const IEEE802154: Self = Self(libc::AF_IEEE802154); + /// Represents `AF_IMPLINK`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "illumos", + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", ))] - Lat = libc::AF_LAT, - /// NSC Hyperchannel + pub const IMPLINK: Self = Self(libc::AF_IMPLINK); + /// Represents `AF_INET`. #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "redox", + target_os = "solaris", ))] - Hylink = libc::AF_HYLINK, - /// Link layer interface + pub const INET: Self = Self(libc::AF_INET); + /// Represents `AF_INET6`. #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "fuchsia", + target_os = "haiku", target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "redox", + target_os = "solaris", + ))] + pub const INET6: Self = Self(libc::AF_INET6); + /// Represents `AF_INET6_SDP`. + #[cfg(target_os = "freebsd")] + pub const INET6_SDP: Self = Self(libc::AF_INET6_SDP); + /// Represents `AF_INET_OFFLOAD`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", ))] - Link = libc::AF_LINK, - /// connection-oriented IP, aka ST II + pub const INET_OFFLOAD: Self = Self(libc::AF_INET_OFFLOAD); + /// Represents `AF_INET_SDP`. + #[cfg(target_os = "freebsd")] + pub const INET_SDP: Self = Self(libc::AF_INET_SDP); + /// Represents `AF_IPX`. #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "solaris", + ))] + pub const IPX: Self = Self(libc::AF_IPX); + /// Represents `AF_IRDA`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", ))] - Coip = libc::AF_COIP, - /// Computer Network Technology + pub const IRDA: Self = Self(libc::AF_IRDA); + /// Represents `AF_ISDN`. #[cfg(any( + target_os = "android", target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", ))] - Cnt = libc::AF_CNT, - /// Native ATM access + pub const ISDN: Self = Self(libc::AF_ISDN); + /// Represents `AF_ISO`. #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", ))] - Natm = libc::AF_NATM, - /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html)) - #[cfg(any(target_os = "android", target_os = "linux"))] - Unspec = libc::AF_UNSPEC, -} - -impl AddressFamily { - /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from - /// the `sa_family` field of a `sockaddr`. - /// - /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet - /// and System. Returns None for unsupported or unknown address families. - pub const fn from_i32(family: i32) -> Option { - match family { - libc::AF_UNIX => Some(AddressFamily::Unix), - libc::AF_INET => Some(AddressFamily::Inet), - libc::AF_INET6 => Some(AddressFamily::Inet6), - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_NETLINK => Some(AddressFamily::Netlink), - #[cfg(any(target_os = "macos", target_os = "macos"))] - libc::AF_SYSTEM => Some(AddressFamily::System), - #[cfg(not(any( - target_os = "redox", - target_os = "linux", - target_os = "android" - )))] - libc::PF_ROUTE => Some(AddressFamily::Route), - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_PACKET => Some(AddressFamily::Packet), - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd" - ))] - libc::AF_LINK => Some(AddressFamily::Link), - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" - ))] - libc::AF_VSOCK => Some(AddressFamily::Vsock), - _ => None, - } - } -} - -/// A wrapper around `sockaddr_un`. -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct UnixAddr { - // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts - sun: libc::sockaddr_un, - /// The length of the valid part of `sun`, including the sun_family field - /// but excluding any trailing nul. - // On the BSDs, this field is built into sun + pub const ISO: Self = Self(libc::AF_ISO); + /// Represents `AF_IUCV`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const IUCV: Self = Self(libc::AF_IUCV); + /// Represents `AF_KEY`. #[cfg(any( target_os = "android", target_os = "fuchsia", target_os = "illumos", target_os = "linux", - target_os = "redox", + target_os = "openbsd", + target_os = "solaris", ))] - sun_len: u8, -} - + pub const KEY: Self = Self(libc::AF_KEY); + /// Represents `AF_LAT`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const LAT: Self = Self(libc::AF_LAT); + /// Represents `AF_LINK`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const LINK: Self = Self(libc::AF_LINK); + /// Represents `AF_LLC`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const LLC: Self = Self(libc::AF_LLC); + /// Represents `AF_LOCAL`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const LOCAL: Self = Self(libc::AF_LOCAL); + /// Represents `AF_MPLS`. + #[cfg(all( + any( + target_os = "dragonfly", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + ), + not(target_env = "uclibc"), + ))] + pub const MPLS: Self = Self(libc::AF_MPLS); + /// Represents `AF_NATM`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + pub const NATM: Self = Self(libc::AF_NATM); + /// Represents `AF_NBS`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const NBS: Self = Self(libc::AF_NBS); + /// Represents `AF_NCA`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const NCA: Self = Self(libc::AF_NCA); + /// Represents `AF_NDRV`. + #[cfg(any( + target_os = "ios", + target_os = "macos", + ))] + pub const NDRV: Self = Self(libc::AF_NDRV); + /// Represents `AF_NETBEUI`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const NETBEUI: Self = Self(libc::AF_NETBEUI); + /// Represents `AF_NETBIOS`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + ))] + pub const NETBIOS: Self = Self(libc::AF_NETBIOS); + /// Represents `AF_NETGRAPH`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + ))] + pub const NETGRAPH: Self = Self(libc::AF_NETGRAPH); + /// Represents `AF_NETLINK`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const NETLINK: Self = Self(libc::AF_NETLINK); + /// Represents `AF_NETROM`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const NETROM: Self = Self(libc::AF_NETROM); + /// Represents `AF_NFC`. + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + pub const NFC: Self = Self(libc::AF_NFC); + /// Represents `AF_NIT`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const NIT: Self = Self(libc::AF_NIT); + /// Represents `AF_NOTIFY`. + #[cfg(target_os = "haiku")] + pub const NOTIFY: Self = Self(libc::AF_NOTIFY); + /// Represents `AF_NS`. + #[cfg(any( + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const NS: Self = Self(libc::AF_NS); + /// Represents `AF_OROUTE`. + #[cfg(target_os = "netbsd")] + pub const OROUTE: Self = Self(libc::AF_OROUTE); + /// Represents `AF_OSI`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const OSI: Self = Self(libc::AF_OSI); + /// Represents `AF_OSINET`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const OSINET: Self = Self(libc::AF_OSINET); + /// Represents `AF_PACKET`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris", + ))] + pub const PACKET: Self = Self(libc::AF_PACKET); + /// Represents `AF_PHONET`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const PHONET: Self = Self(libc::AF_PHONET); + /// Represents `AF_POLICY`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const POLICY: Self = Self(libc::AF_POLICY); + /// Represents `AF_PPP`. + #[cfg(any( + target_os = "ios", + target_os = "macos", + ))] + pub const PPP: Self = Self(libc::AF_PPP); + /// Represents `AF_PPPOX`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const PPPOX: Self = Self(libc::AF_PPPOX); + /// Represents `AF_PUP`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const PUP: Self = Self(libc::AF_PUP); + /// Represents `AF_RDS`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const RDS: Self = Self(libc::AF_RDS); + /// Represents `AF_ROSE`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const ROSE: Self = Self(libc::AF_ROSE); + /// Represents `AF_ROUTE`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const ROUTE: Self = Self(libc::AF_ROUTE); + /// Represents `AF_RXRPC`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const RXRPC: Self = Self(libc::AF_RXRPC); + /// Represents `AF_SCLUSTER`. + #[cfg(target_os = "freebsd")] + pub const SCLUSTER: Self = Self(libc::AF_SCLUSTER); + /// Represents `AF_SECURITY`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const SECURITY: Self = Self(libc::AF_SECURITY); + /// Represents `AF_SIP`. + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "openbsd", + ))] + pub const SIP: Self = Self(libc::AF_SIP); + /// Represents `AF_SLOW`. + #[cfg(target_os = "freebsd")] + pub const SLOW: Self = Self(libc::AF_SLOW); + /// Represents `AF_SNA`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + ))] + pub const SNA: Self = Self(libc::AF_SNA); + /// Represents `AF_SYSTEM`. + #[cfg(any( + target_os = "ios", + target_os = "macos", + ))] + pub const SYSTEM: Self = Self(libc::AF_SYSTEM); + /// Represents `AF_SYS_CONTROL`. + #[cfg(any( + target_os = "ios", + target_os = "macos", + ))] + pub const SYS_CONTROL: Self = Self(libc::AF_SYS_CONTROL); + /// Represents `AF_TIPC`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const TIPC: Self = Self(libc::AF_TIPC); + /// Represents `AF_TRILL`. + #[cfg(any( + target_os = "illumos", + target_os = "solaris", + ))] + pub const TRILL: Self = Self(libc::AF_TRILL); + /// Represents `AF_UNIX`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris", + ))] + pub const UNIX: Self = Self(libc::AF_UNIX); + /// Represents `AF_UNSPEC`. + #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", + target_os = "illumos", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "redox", + target_os = "solaris", + ))] + pub const UNSPEC: Self = Self(libc::AF_UNSPEC); + /// Represents `AF_UTUN`. + #[cfg(any( + target_os = "ios", + target_os = "macos", + ))] + pub const UTUN: Self = Self(libc::AF_UTUN); + /// Represents `AF_VSOCK`. + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + ))] + pub const VSOCK: Self = Self(libc::AF_VSOCK); + /// Represents `AF_WANPIPE`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ))] + pub const WANPIPE: Self = Self(libc::AF_WANPIPE); + /// Represents `AF_X25`. + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "solaris", + ))] + pub const X25: Self = Self(libc::AF_X25); + /// Represents `AF_XDP`. + #[cfg(all(target_os = "linux", not(target_env = "uclibc")))] + pub const XDP: Self = Self(libc::AF_XDP); +} + +macro_rules! sockaddr_family { + () => { + #[doc="Returns the address family of this socket address."] + pub const fn family(&self) -> AddressFamily { + unsafe { AddressFamily::of(&*self.as_ptr().cast()) } + } + } +} + +macro_rules! sockaddr_len_static { + () => { + #[doc="Returns the length of this socket address.\n\nThe returned length is guaranteed + to be in bounds of `[2, SIZE]` with `SIZE` being the size of the underlying libc type."] + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + mem::size_of::() + } + }; +} + +/// UNIX socket address. +/// +/// Dyn-sized variant of [`UnixAddress`]. +#[derive(Debug, PartialEq, Eq, Hash)] +#[cfg_attr(not(doc), repr(C, align(2)))] +pub struct UnixAddr { + slice: [u8], +} + +#[allow(clippy::len_without_is_empty)] +impl UnixAddr { + const _ALIGN: () = assert!(mem::align_of::() == 2); + + // SAFETY: When dereferencing, `data` must be valid for reads of at least `len` bytes + // and must point to a valid UNIX address. + #[allow(clippy::cast_ptr_alignment)] + const fn from_raw_parts(data: *const libc::sockaddr_un, len: usize) -> *const Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts(data_ptr, len); + + slice_ptr as *const Self + } + + // SAFETY: When dereferencing, `data` must be valid for reads and writes of at least `len` bytes + // and must point to a valid UNIX address. + #[allow(clippy::cast_ptr_alignment)] + fn from_raw_parts_mut(data: *mut libc::sockaddr_un, len: usize) -> *mut Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts_mut(data_ptr, len); + + slice_ptr as *mut Self + } + + /// Returns the length of this socket address. + /// + /// The returned length is guaranteed to be in bounds of `[2, MAX]` with `MAX` being the + /// maximum length of a socket address (i.e. the size of [`libc::sockaddr_storage`]). + pub fn len(&self) -> usize { + self.slice.len() + } + + fn kind(&self) -> UnixAddrKind<'_> { + // SAFETY: our sockaddr is always valid because of the invariant on the struct + unsafe { UnixAddrKind::get(&*self.as_ptr(), self.len() as _) } + } + + /// If this address represents a filesystem path, return that path. + pub fn path(&self) -> Option<&Path> { + match self.kind() { + UnixAddrKind::Pathname(path) => Some(path), + _ => None, + } + } + + /// If this address represents an abstract socket, return its name. + /// + /// For abstract sockets only the bare name is returned, without the + /// leading NUL byte. `None` is returned for unnamed or path-backed sockets. + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + pub fn as_abstract(&self) -> Option<&[u8]> { + match self.kind() { + UnixAddrKind::Abstract(name) => Some(name), + _ => None, + } + } + + /// Check if this address is an "unnamed" UNIX socket address. + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg_attr(docsrs, doc(cfg(all())))] + #[inline] + pub fn is_unnamed(&self) -> bool { + matches!(self.kind(), UnixAddrKind::Unnamed) + } + + /// Returns the length of the path of this address. + #[inline] + pub fn path_len(&self) -> usize { + self.len() - offset_of!(libc::sockaddr_un, sun_path) + } + + /// Converts to a boxed [`UnixAddr`]. + /// + /// # Panics + /// + /// This method panics if the allocation fails. + pub fn to_boxed(&self) -> Box { + let layout = std::alloc::Layout::for_value(self); + + unsafe { + let box_ptr = std::alloc::alloc(layout); + assert_ne!(box_ptr, std::ptr::null_mut()); + + ptr::copy_nonoverlapping(self.as_ptr().cast(), box_ptr, self.len()); + + Box::from_raw(Self::from_raw_parts_mut(box_ptr.cast(), self.len())) + } + } + + /// Converts to an owned [`UnixAddress`] if the address fits into it. + /// + /// If you are unsure whether it fits into [`UnixAddress`], consider using + /// [`Self::to_boxed`] instead. + pub fn to_owned(&self) -> Option { + if self.len() > mem::size_of::() { + return None; + } + + let mut addr = mem::MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping(self.slice.as_ptr(), addr.as_mut_ptr().cast(), self.len()); + } + + Some(UnixAddress { + sun: unsafe { addr.assume_init() }, + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + sun_len: self.len() as _, + }) + } + + /// Returns a raw pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of!`]. + pub const fn as_ptr(&self) -> *const libc::sockaddr_un { + self.slice.as_ptr().cast() + } + + /// Returns a raw mutable pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of_mut!`]. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un { + self.slice.as_mut_ptr().cast() + } +} + +impl PartialEq for UnixAddr { + fn eq(&self, other: &UnixAddress) -> bool { + *self == **other + } +} + +impl fmt::Display for UnixAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.kind() { + UnixAddrKind::Pathname(path) => path.display().fmt(f), + UnixAddrKind::Unnamed => f.pad(""), + #[cfg(any(target_os = "android", target_os = "linux"))] + UnixAddrKind::Abstract(name) => fmt_abstract(name, f), + } + } +} + +impl AsRef for UnixAddr { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(self.as_ptr().cast(), self.len()) } + } +} + +impl Borrow for UnixAddr { + fn borrow(&self) -> &Addr { + self.as_ref() + } +} + +/// UNIX socket address. +/// +/// This struct is an abstraction for socket addresses of type `AF_UNIX`. +#[derive(Clone, Copy, Debug, Eq)] +#[repr(C)] +pub struct UnixAddress { + // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts + sun: libc::sockaddr_un, + /// The length of the valid part of `sun`, including the sun_family field + /// but excluding any trailing nul. + // On the BSDs, this field is built into sun + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + sun_len: usize, +} + // linux man page unix(7) says there are 3 kinds of unix socket: // pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1 // unnamed: addrlen = sizeof(sa_family_t) @@ -427,7 +1093,7 @@ enum UnixAddrKind<'a> { } impl<'a> UnixAddrKind<'a> { /// Safety: sun & sun_len must be valid - #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self { assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path)); let path_len = @@ -465,13 +1131,13 @@ impl<'a> UnixAddrKind<'a> { } } -impl UnixAddr { - /// Create a new sockaddr_un representing a filesystem path. - #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms - pub fn new(path: &P) -> Result { +impl UnixAddress { + /// Create a new UNIX address representing a filesystem path. + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms + pub fn new(path: &P) -> Result { path.with_nix_path(|cstr| unsafe { let mut ret = libc::sockaddr_un { - sun_family: AddressFamily::Unix as sa_family_t, + sun_family: libc::AF_UNIX as sa_family_t, ..mem::zeroed() }; @@ -481,20 +1147,18 @@ impl UnixAddr { return Err(Errno::ENAMETOOLONG); } - let sun_len = (bytes.len() - + offset_of!(libc::sockaddr_un, sun_path)) - .try_into() - .unwrap(); + // We add 1 for the trailing NUL + let sun_len = bytes.len() + offset_of!(libc::sockaddr_un, sun_path) + 1; - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "netbsd", - target_os = "openbsd" - ))] + #[cfg(not(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + )))] { - ret.sun_len = sun_len; + ret.sun_len = sun_len as _; } ptr::copy_nonoverlapping( bytes.as_ptr(), @@ -502,32 +1166,42 @@ impl UnixAddr { bytes.len(), ); - Ok(UnixAddr::from_raw_parts(ret, sun_len)) + Ok(UnixAddress::from_raw_parts(ret, sun_len)) })? } - /// Create a new `sockaddr_un` representing an address in the "abstract namespace". + /// Create a new UNIX address representing an address in the "abstract namespace". /// /// The leading nul byte for the abstract namespace is automatically added; /// thus the input `path` is expected to be the bare name, not NUL-prefixed. /// This is a Linux-specific extension, primarily used to allow chrooted /// processes to communicate with processes having a different filesystem view. #[cfg(any(target_os = "android", target_os = "linux"))] - #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms - pub fn new_abstract(path: &[u8]) -> Result { + #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms + pub fn new_abstract(path: &[u8]) -> Result { unsafe { let mut ret = libc::sockaddr_un { - sun_family: AddressFamily::Unix as sa_family_t, + sun_family: libc::AF_UNIX as sa_family_t, ..mem::zeroed() }; if path.len() >= ret.sun_path.len() { return Err(Errno::ENAMETOOLONG); } - let sun_len = - (path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path)) - .try_into() - .unwrap(); + + // We add 1 for the leading NUL + let sun_len = path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path); + + #[cfg(not(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + )))] + { + ret.sun_len = sun_len as _; + } // Abstract addresses are represented by sun_path[0] == // b'\0', so copy starting one byte in. @@ -537,104 +1211,83 @@ impl UnixAddr { path.len(), ); - Ok(UnixAddr::from_raw_parts(ret, sun_len)) + Ok(UnixAddress::from_raw_parts(ret, sun_len)) } } - /// Create a new `sockaddr_un` representing an "unnamed" unix socket address. + /// Create an unnamed UNIX address. #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn new_unnamed() -> UnixAddr { + pub fn new_unnamed() -> UnixAddress { let ret = libc::sockaddr_un { - sun_family: AddressFamily::Unix as sa_family_t, + sun_family: libc::AF_UNIX as sa_family_t, ..unsafe { mem::zeroed() } }; - let sun_len: u8 = - offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap(); + let sun_len = offset_of!(libc::sockaddr_un, sun_path); + + #[cfg(not(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + )))] + { + ret.sun_len = sun_len as _; + } - unsafe { UnixAddr::from_raw_parts(ret, sun_len) } + unsafe { UnixAddress::from_raw_parts(ret, sun_len) } } - /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len` - /// is the size of the valid portion of the struct, excluding any trailing - /// NUL. + sockaddr_family!(); + + /// Returns the length of this socket address. /// - /// # Safety - /// This pair of sockaddr_un & sun_len must be a valid unix addr, which - /// means: - /// - sun_len >= offset_of(sockaddr_un, sun_path) - /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path) - /// - if this is a unix addr with a pathname, sun.sun_path is a - /// fs path, not necessarily nul-terminated. - pub(crate) unsafe fn from_raw_parts( - sun: libc::sockaddr_un, - sun_len: u8, - ) -> UnixAddr { + /// The returned length is guaranteed to be in bounds of `[2, SIZE]` with `SIZE` being the + /// size of the underlying libc type. + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "redox", - ))] + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { - UnixAddr { sun, sun_len } + self.sun_len } else { - assert_eq!(sun_len, sun.sun_len); - UnixAddr {sun} + self.sun.sun_len as _ } } } - fn kind(&self) -> UnixAddrKind<'_> { - // SAFETY: our sockaddr is always valid because of the invariant on the struct - unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) } - } - - /// If this address represents a filesystem path, return that path. - pub fn path(&self) -> Option<&Path> { - match self.kind() { - UnixAddrKind::Pathname(path) => Some(path), - _ => None, - } - } - - /// If this address represents an abstract socket, return its name. - /// - /// For abstract sockets only the bare name is returned, without the - /// leading NUL byte. `None` is returned for unnamed or path-backed sockets. - #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn as_abstract(&self) -> Option<&[u8]> { - match self.kind() { - UnixAddrKind::Abstract(name) => Some(name), - _ => None, - } - } - - /// Check if this address is an "unnamed" unix socket address. - #[cfg(any(target_os = "android", target_os = "linux"))] - #[inline] - pub fn is_unnamed(&self) -> bool { - matches!(self.kind(), UnixAddrKind::Unnamed) - } - - /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)` - #[inline] - pub fn path_len(&self) -> usize { - self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path) - } - /// Returns a pointer to the raw `sockaddr_un` struct - #[inline] - pub fn as_ptr(&self) -> *const libc::sockaddr_un { + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_un { &self.sun } - /// Returns a mutable pointer to the raw `sockaddr_un` struct - #[inline] + + /// Returns a raw mutable pointer to the libc type. pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un { &mut self.sun } - fn sun_len(&self) -> u8 { + /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len` + /// is the size of the valid portion of the struct, excluding any trailing + /// NUL. + /// + /// # Safety + /// This pair of sockaddr_un & sun_len must be a valid unix addr, which + /// means: + /// - sun_len >= offset_of(sockaddr_un, sun_path) + /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path) + /// - if this is a unix addr with a pathname, sun.sun_path is a + /// fs path, not necessarily nul-terminated. + pub(crate) unsafe fn from_raw_parts( + sun: libc::sockaddr_un, + sun_len: usize, + ) -> UnixAddress { cfg_if! { if #[cfg(any(target_os = "android", target_os = "fuchsia", @@ -643,315 +1296,154 @@ impl UnixAddr { target_os = "redox", ))] { - self.sun_len + UnixAddress { sun, sun_len } } else { - self.sun.sun_len + assert_eq!(sun_len, sun.sun_len as usize); + UnixAddress {sun} } } } } -impl private::SockaddrLikePriv for UnixAddr {} -impl SockaddrLike for UnixAddr { - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux" - ))] - fn len(&self) -> libc::socklen_t { - self.sun_len.into() - } - - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = len { - if (l as usize) < offset_of!(libc::sockaddr_un, sun_path) - || l > u8::MAX as libc::socklen_t - { - return None; - } - } - if unsafe { (*addr).sa_family as i32 != libc::AF_UNIX } { - return None; - } - let mut su: libc::sockaddr_un = unsafe { mem::zeroed() }; - let sup = &mut su as *mut libc::sockaddr_un as *mut u8; - cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "redox", - ))] { - let su_len = len.unwrap_or( - mem::size_of::() as libc::socklen_t - ); - } else { - let su_len = unsafe { len.unwrap_or((*addr).sa_len as libc::socklen_t) }; - } - } - unsafe { ptr::copy(addr as *const u8, sup, su_len as usize) }; - Some(unsafe { Self::from_raw_parts(su, su_len as u8) }) - } - - fn size() -> libc::socklen_t - where - Self: Sized, - { - mem::size_of::() as libc::socklen_t - } +impl std::ops::Deref for UnixAddress { + type Target = UnixAddr; - unsafe fn set_length( - &mut self, - new_length: usize, - ) -> std::result::Result<(), SocketAddressLengthNotDynamic> { - // `new_length` is only used on some platforms, so it must be provided even when not used - #![allow(unused_variables)] - cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "redox", - ))] { - self.sun_len = new_length as u8; - } - }; - Ok(()) + fn deref(&self) -> &Self::Target { + // SAFETY: The length cannot exceed the size of the struct. + unsafe { + &*UnixAddr::from_raw_parts(&self.sun, self.len()) + } } } -impl AsRef for UnixAddr { - fn as_ref(&self) -> &libc::sockaddr_un { - &self.sun - } -} +impl std::ops::DerefMut for UnixAddress { + fn deref_mut(&mut self) -> &mut Self::Target { + let len = self.len(); -#[cfg(any(target_os = "android", target_os = "linux"))] -fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result { - use fmt::Write; - f.write_str("@\"")?; - for &b in abs { - use fmt::Display; - char::from(b).escape_default().fmt(f)?; + // SAFETY: The length cannot exceed the size of the struct. + unsafe { + &mut *UnixAddr::from_raw_parts_mut(&mut self.sun, len) + } } - f.write_char('"')?; - Ok(()) } -impl fmt::Display for UnixAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.kind() { - UnixAddrKind::Pathname(path) => path.display().fmt(f), - UnixAddrKind::Unnamed => f.pad(""), - #[cfg(any(target_os = "android", target_os = "linux"))] - UnixAddrKind::Abstract(name) => fmt_abstract(name, f), - } +impl AsRef for UnixAddress { + fn as_ref(&self) -> &Addr { + (**self).as_ref() } } -impl PartialEq for UnixAddr { - fn eq(&self, other: &UnixAddr) -> bool { - self.kind() == other.kind() +impl Borrow for UnixAddress { + fn borrow(&self) -> &Addr { + (**self).borrow() } } -impl Eq for UnixAddr {} +#[allow(clippy::cast_ptr_alignment)] +impl From for Address { + fn from(addr: UnixAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); -impl Hash for UnixAddr { - fn hash(&self, s: &mut H) { - self.kind().hash(s) - } -} + unsafe { + ptr::copy_nonoverlapping( + &addr.sun as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); -/// Anything that, in C, can be cast back and forth to `sockaddr`. -/// -/// Most implementors also implement `AsRef` to access their -/// inner type read-only. -#[allow(clippy::len_without_is_empty)] -pub trait SockaddrLike: private::SockaddrLikePriv { - /// Returns a raw pointer to the inner structure. Useful for FFI. - fn as_ptr(&self) -> *const libc::sockaddr { - self as *const Self as *const libc::sockaddr + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, + } + } } +} - /// Unsafe constructor from a variable length source - /// - /// Some C APIs from provide `len`, and others do not. If it's provided it - /// will be validated. If not, it will be guessed based on the family. - /// - /// # Arguments - /// - /// - `addr`: raw pointer to something that can be cast to a - /// `libc::sockaddr`. For example, `libc::sockaddr_in`, - /// `libc::sockaddr_in6`, etc. - /// - `len`: For fixed-width types like `sockaddr_in`, it will be - /// validated if present and ignored if not. For variable-width - /// types it is required and must be the total length of valid - /// data. For example, if `addr` points to a - /// named `sockaddr_un`, then `len` must be the length of the - /// structure up to but not including the trailing NUL. - /// - /// # Safety - /// - /// `addr` must be valid for the specific type of sockaddr. `len`, if - /// present, must not exceed the length of valid data in `addr`. - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized; - - /// Return the address family of this socket - /// - /// # Examples - /// One common use is to match on the family of a union type, like this: - /// ``` - /// # use nix::sys::socket::*; - /// # use std::os::unix::io::AsRawFd; - /// let fd = socket(AddressFamily::Inet, SockType::Stream, - /// SockFlag::empty(), None).unwrap(); - /// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).unwrap(); - /// match ss.family().unwrap() { - /// AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()), - /// AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()), - /// _ => println!("Unexpected address family") - /// } - /// ``` - fn family(&self) -> Option { - // Safe since all implementors have a sa_family field at the same - // address, and they're all repr(C) - AddressFamily::from_i32(unsafe { - (*(self as *const Self as *const libc::sockaddr)).sa_family as i32 - }) +impl AsRef for UnixAddress { + fn as_ref(&self) -> &UnixAddr { + self } +} - cfg_if! { - if #[cfg(any(target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "netbsd", - target_os = "openbsd"))] { - /// Return the length of valid data in the sockaddr structure. - /// - /// For fixed-size sockaddrs, this should be the size of the - /// structure. But for variable-sized types like [`UnixAddr`] it - /// may be less. - fn len(&self) -> libc::socklen_t { - // Safe since all implementors have a sa_len field at the same - // address, and they're all repr(transparent). - // Robust for all implementors. - unsafe { - (*(self as *const Self as *const libc::sockaddr)).sa_len - }.into() - } - } else { - /// Return the length of valid data in the sockaddr structure. - /// - /// For fixed-size sockaddrs, this should be the size of the - /// structure. But for variable-sized types like [`UnixAddr`] it - /// may be less. - fn len(&self) -> libc::socklen_t { - // No robust default implementation is possible without an - // sa_len field. Implementors with a variable size must - // override this method. - mem::size_of_val(self) as libc::socklen_t - } - } +impl AsMut for UnixAddress { + fn as_mut(&mut self) -> &mut UnixAddr { + self } +} - /// Return the available space in the structure - fn size() -> libc::socklen_t - where - Self: Sized, - { - mem::size_of::() as libc::socklen_t +impl Borrow for UnixAddress { + fn borrow(&self) -> &UnixAddr { + self } +} - /// Set the length of this socket address - /// - /// This method may only be called on socket addresses whose lengths are dynamic, and it - /// returns an error if called on a type whose length is static. - /// - /// # Safety - /// - /// `new_length` must be a valid length for this type of address. Specifically, reads of that - /// length from `self` must be valid. - #[doc(hidden)] - unsafe fn set_length( - &mut self, - _new_length: usize, - ) -> std::result::Result<(), SocketAddressLengthNotDynamic> { - Err(SocketAddressLengthNotDynamic) +impl BorrowMut for UnixAddress { + fn borrow_mut(&mut self) -> &mut UnixAddr { + self } } -/// The error returned by [`SockaddrLike::set_length`] on an address whose length is statically -/// fixed. -#[derive(Copy, Clone, Debug)] -pub struct SocketAddressLengthNotDynamic; -impl fmt::Display for SocketAddressLengthNotDynamic { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Attempted to set length on socket whose length is statically fixed") +impl PartialEq for UnixAddress { + fn eq(&self, other: &UnixAddress) -> bool { + **self == **other } } -impl std::error::Error for SocketAddressLengthNotDynamic {} -impl private::SockaddrLikePriv for () { - fn as_mut_ptr(&mut self) -> *mut libc::sockaddr { - ptr::null_mut() +impl PartialEq for UnixAddress { + fn eq(&self, other: &UnixAddr) -> bool { + **self == *other } } -/// `()` can be used in place of a real Sockaddr when no address is expected, -/// for example for a field of `Option where S: SockaddrLike`. -// If this RFC ever stabilizes, then ! will be a better choice. -// https://github.com/rust-lang/rust/issues/35121 -impl SockaddrLike for () { - fn as_ptr(&self) -> *const libc::sockaddr { - ptr::null() +impl Hash for UnixAddress { + fn hash(&self, state: &mut H) { + (**self).hash(state) } +} - unsafe fn from_raw( - _: *const libc::sockaddr, - _: Option, - ) -> Option - where - Self: Sized, - { - None +impl std::fmt::Display for UnixAddress { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + (**self).fmt(f) } +} - fn family(&self) -> Option { - None +impl AsRef for UnixAddress { + fn as_ref(&self) -> &libc::sockaddr_un { + &self.sun } +} - fn len(&self) -> libc::socklen_t { - 0 +#[cfg(any(target_os = "android", target_os = "linux"))] +fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result { + use fmt::Write; + f.write_str("@\"")?; + for &b in abs { + use fmt::Display; + char::from(b).escape_default().fmt(f)?; } + f.write_char('"')?; + Ok(()) } /// An IPv4 socket address #[cfg(feature = "net")] #[repr(transparent)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct SockaddrIn(libc::sockaddr_in); +pub struct Ipv4Address(libc::sockaddr_in); #[cfg(feature = "net")] -impl SockaddrIn { +impl Ipv4Address { /// Returns the IP address associated with this socket address, in native /// endian. - pub const fn ip(&self) -> net::Ipv4Addr { - let bytes = self.0.sin_addr.s_addr.to_ne_bytes(); - let (a, b, c, d) = (bytes[0], bytes[1], bytes[2], bytes[3]); - Ipv4Addr::new(a, b, c, d) + pub const fn ip(&self) -> libc::in_addr_t { + u32::from_be(self.0.sin_addr.s_addr) } /// Creates a new socket address from IPv4 octets and a port number. @@ -960,14 +1452,15 @@ impl SockaddrIn { #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "aix", target_os = "haiku", target_os = "openbsd" ))] - sin_len: Self::size() as u8, - sin_family: AddressFamily::Inet as sa_family_t, + sin_len: mem::size_of::() as u8, + sin_family: libc::AF_INET as sa_family_t, sin_port: u16::to_be(port), sin_addr: libc::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]), @@ -976,45 +1469,78 @@ impl SockaddrIn { }) } + sockaddr_family!(); + sockaddr_len_static!(); + /// Returns the port number associated with this socket address, in native /// endian. pub const fn port(&self) -> u16 { u16::from_be(self.0.sin_port) } + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_in { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_in { + &mut self.0 + } } #[cfg(feature = "net")] -impl private::SockaddrLikePriv for SockaddrIn {} +#[allow(clippy::cast_ptr_alignment)] +impl AsRef for Ipv4Address { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } +} + #[cfg(feature = "net")] -impl SockaddrLike for SockaddrIn { - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; +impl Borrow for Ipv4Address { + fn borrow(&self) -> &Addr { + self.as_ref() + } +} + +#[cfg(feature = "net")] +#[allow(clippy::cast_ptr_alignment)] +impl From for Address { + fn from(addr: Ipv4Address) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_INET } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } #[cfg(feature = "net")] -impl AsRef for SockaddrIn { +impl AsRef for Ipv4Address { fn as_ref(&self) -> &libc::sockaddr_in { &self.0 } } #[cfg(feature = "net")] -impl fmt::Display for SockaddrIn { +impl fmt::Display for Ipv4Address { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let ne = u32::from_be(self.0.sin_addr.s_addr); let port = u16::from_be(self.0.sin_port); @@ -1031,7 +1557,7 @@ impl fmt::Display for SockaddrIn { } #[cfg(feature = "net")] -impl From for SockaddrIn { +impl From for Ipv4Address { fn from(addr: net::SocketAddrV4) -> Self { Self(libc::sockaddr_in { #[cfg(any( @@ -1039,12 +1565,13 @@ impl From for SockaddrIn { target_os = "freebsd", target_os = "haiku", target_os = "hermit", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd" ))] sin_len: mem::size_of::() as u8, - sin_family: AddressFamily::Inet as sa_family_t, + sin_family: libc::AF_INET as sa_family_t, sin_port: addr.port().to_be(), // network byte order sin_addr: ipv4addr_to_libc(*addr.ip()), ..unsafe { mem::zeroed() } @@ -1053,8 +1580,8 @@ impl From for SockaddrIn { } #[cfg(feature = "net")] -impl From for net::SocketAddrV4 { - fn from(addr: SockaddrIn) -> Self { +impl From for net::SocketAddrV4 { + fn from(addr: Ipv4Address) -> Self { net::SocketAddrV4::new( net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()), u16::from_be(addr.0.sin_port), @@ -1063,541 +1590,603 @@ impl From for net::SocketAddrV4 { } #[cfg(feature = "net")] -impl std::str::FromStr for SockaddrIn { +impl std::str::FromStr for Ipv4Address { + type Err = net::AddrParseError; + + fn from_str(s: &str) -> std::result::Result { + net::SocketAddrV4::from_str(s).map(Ipv4Address::from) + } +} + +/// An IPv6 socket address +#[cfg(feature = "net")] +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Ipv6Address(libc::sockaddr_in6); + +#[cfg(feature = "net")] +impl Ipv6Address { + sockaddr_family!(); + sockaddr_len_static!(); + + /// Returns the flow information associated with this address. + pub const fn flowinfo(&self) -> u32 { + self.0.sin6_flowinfo + } + + /// Returns the IP address associated with this socket address. + pub fn ip(&self) -> net::Ipv6Addr { + net::Ipv6Addr::from(self.0.sin6_addr.s6_addr) + } + + /// Returns the port number associated with this socket address, in native + /// endian. + pub const fn port(&self) -> u16 { + u16::from_be(self.0.sin6_port) + } + + /// Returns the scope ID associated with this address. + pub const fn scope_id(&self) -> u32 { + self.0.sin6_scope_id + } + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_in6 { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_in6 { + &mut self.0 + } +} + +#[cfg(feature = "net")] +#[allow(clippy::cast_ptr_alignment)] +impl AsRef for Ipv6Address { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } +} + +#[cfg(feature = "net")] +impl Borrow for Ipv6Address { + fn borrow(&self) -> &Addr { + self.as_ref() + } +} + +#[cfg(feature = "net")] +#[cfg(feature = "net")] +#[allow(clippy::cast_ptr_alignment)] +impl From for Address { + fn from(addr: Ipv6Address) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, + } + } + } +} + +#[cfg(feature = "net")] +impl AsRef for Ipv6Address { + fn as_ref(&self) -> &libc::sockaddr_in6 { + &self.0 + } +} + +#[cfg(feature = "net")] +impl fmt::Display for Ipv6Address { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // These things are really hard to display properly. Easier to let std + // do it. + let std = net::SocketAddrV6::new( + self.ip(), + self.port(), + self.flowinfo(), + self.scope_id(), + ); + std.fmt(f) + } +} + +#[cfg(feature = "net")] +impl From for Ipv6Address { + fn from(addr: net::SocketAddrV6) -> Self { + #[allow(clippy::needless_update)] // It isn't needless on Illumos + Self(libc::sockaddr_in6 { + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "hermit", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + sin6_len: mem::size_of::() as u8, + sin6_family: libc::AF_INET6 as sa_family_t, + sin6_port: addr.port().to_be(), // network byte order + sin6_addr: ipv6addr_to_libc(addr.ip()), + sin6_flowinfo: addr.flowinfo(), // host byte order + sin6_scope_id: addr.scope_id(), // host byte order + ..unsafe { mem::zeroed() } + }) + } +} + +#[cfg(feature = "net")] +impl From for net::SocketAddrV6 { + fn from(addr: Ipv6Address) -> Self { + net::SocketAddrV6::new( + net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr), + u16::from_be(addr.0.sin6_port), + addr.0.sin6_flowinfo, + addr.0.sin6_scope_id, + ) + } +} + +#[cfg(feature = "net")] +impl std::str::FromStr for Ipv6Address { type Err = net::AddrParseError; - fn from_str(s: &str) -> std::result::Result { - net::SocketAddrV4::from_str(s).map(SockaddrIn::from) - } -} + fn from_str(s: &str) -> std::result::Result { + net::SocketAddrV6::from_str(s).map(Ipv6Address::from) + } +} + +macro_rules! sockaddr_storage_conv { + ($fname:ident, $nixty:ty, $cty:ident, $af:ident, $doc:tt) => { + #[doc = $doc] + pub fn $fname(&self) -> Option<&$nixty> { + if self.family() != AddressFamily::$af { + return None; + } + + let addr = self.as_ptr().cast(); + + unsafe { + Some(&*addr) + } + } + }; +} + +/// General purpose socket address with arbitrary address family. +/// +/// Dyn-sized variant of [`Address`]. +#[derive(Debug, PartialEq, Eq, Hash)] +#[cfg_attr(not(doc), repr(C, align(2)))] +pub struct Addr { + slice: [u8], +} + +#[allow(clippy::len_without_is_empty)] +impl Addr { + const _ALIGN: () = assert!(mem::align_of::() == 2); + + // SAFETY: When dereferencing, `data` must be valid for reads of at least `len` bytes. + #[allow(clippy::cast_ptr_alignment)] + const fn from_raw_parts(data: *const libc::sockaddr_storage, len: usize) -> *const Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts(data_ptr, len); + + slice_ptr as *const Self + } + + // SAFETY: When dereferencing, `data` must be valid for reads and writes of at least `len` bytes. + #[allow(clippy::cast_ptr_alignment)] + fn from_raw_parts_mut(data: *mut libc::sockaddr_storage, len: usize) -> *mut Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts_mut(data_ptr, len); + + slice_ptr as *mut Self + } + + /// Returns an empty socket address. + pub const fn empty() -> &'static Self { + let p = ptr::NonNull::::dangling().as_ptr().cast_const(); + + unsafe { + &*Self::from_raw_parts(p, 0) + } + } + + /// Returns the length of this socket address. + /// + /// The returned length is guaranteed to be in bounds of `[2, MAX]` with `MAX` being the + /// maximum length of a socket address (i.e. the size of [`libc::sockaddr_storage`]). + /// + /// The length can also be zero, in which case the address hasn't been initialized yet. + pub const fn len(&self) -> usize { + self.slice.len() + } + + /// Returns the address family associated with this socket address. + /// + /// If the length of this address is zero, [`AddressFamily::UNSPEC`] is returned. + #[allow(clippy::cast_ptr_alignment)] + pub const fn family(&self) -> AddressFamily { + if self.len() == 0 { + return AddressFamily::UNSPEC; + } + + unsafe { + AddressFamily(*addr_of!((*self.as_ptr()).ss_family) as _) + } + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + sockaddr_storage_conv!(to_alg, AlgAddress, sockaddr_alg, ALG, "Converts to [`AlgAddress`], if the address family matches."); + + #[cfg(feature = "net")] + sockaddr_storage_conv!(to_ipv4, Ipv4Address, sockaddr_in, INET, "Converts to [`Ipv4Address`], if the address family matches."); + + #[cfg(feature = "net")] + sockaddr_storage_conv!(to_ipv6, Ipv6Address, sockaddr_in6, INET6, "Converts to [`Ipv6Address`], if the address family matches."); + + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] + sockaddr_storage_conv!(to_vsock, VsockAddress, sockaddr_vm, VSOCK, "Converts to [`VsockAddress`], if the address family matches."); + + #[cfg(all( + feature = "net", + any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "haiku", + target_os = "aix", + target_os = "openbsd", + ), + ))] + /// Converts to [`LinkAddr`], if the address family matches. + pub fn to_link(&self) -> Option<&LinkAddr> { + if self.family() != AddressFamily::LINK { + return None; + } + + unsafe { + Some(&*LinkAddr::from_raw_parts(self.as_ptr().cast(), self.len() as _)) + } + } + + #[cfg(all( + feature = "net", + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ), + ))] + sockaddr_storage_conv!(to_link, LinkAddress, sockaddr_ll, PACKET, "Converts to [`LinkAddress`], if the address family matches."); + + #[cfg(any(target_os = "android", target_os = "linux"))] + sockaddr_storage_conv!(to_netlink, NetlinkAddress, sockaddr_nl, NETLINK, "Converts to [`NetlinkAddress`], if the address family matches."); + + #[cfg(all( + feature = "ioctl", + any(target_os = "ios", target_os = "macos") + ))] + sockaddr_storage_conv!(to_sys_control, SysControlAddress, sockaddr_ctl, SYSTEM, "Converts to [`SysControlAddress`], if the address family matches."); + + /// Converts to [`UnixAddr`], if the address family matches. + pub fn to_unix(&self) -> Option<&UnixAddr> { + if self.family() != AddressFamily::UNIX { + return None; + } + + unsafe { + Some(&*UnixAddr::from_raw_parts(self.as_ptr().cast(), self.len() as _)) + } + } + + /// Converts to a boxed [`Addr`]. + /// + /// # Panics + /// + /// This method panics if the allocation fails. + pub fn to_boxed(&self) -> Box { + let layout = std::alloc::Layout::for_value(self); + + unsafe { + let box_ptr = if layout.size() != 0 { + std::alloc::alloc(layout) + } else { + ptr::NonNull::::dangling().as_ptr().cast() + }; -/// An IPv6 socket address -#[cfg(feature = "net")] -#[repr(transparent)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct SockaddrIn6(libc::sockaddr_in6); + assert_ne!(box_ptr, std::ptr::null_mut()); -#[cfg(feature = "net")] -impl SockaddrIn6 { - /// Returns the flow information associated with this address. - pub const fn flowinfo(&self) -> u32 { - self.0.sin6_flowinfo + ptr::copy_nonoverlapping(self.as_ptr().cast(), box_ptr, self.len()); + + Box::from_raw(Self::from_raw_parts_mut(box_ptr.cast(), self.len())) + } } - /// Returns the IP address associated with this socket address. - pub const fn ip(&self) -> net::Ipv6Addr { - let bytes = self.0.sin6_addr.s6_addr; - let (a, b, c, d, e, f, g, h) = ( - ((bytes[0] as u16) << 8) | bytes[1] as u16, - ((bytes[2] as u16) << 8) | bytes[3] as u16, - ((bytes[4] as u16) << 8) | bytes[5] as u16, - ((bytes[6] as u16) << 8) | bytes[7] as u16, - ((bytes[8] as u16) << 8) | bytes[9] as u16, - ((bytes[10] as u16) << 8) | bytes[11] as u16, - ((bytes[12] as u16) << 8) | bytes[13] as u16, - ((bytes[14] as u16) << 8) | bytes[15] as u16, - ); - Ipv6Addr::new(a, b, c, d, e, f, g, h) + /// Returns a raw pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of!`]. + pub const fn as_ptr(&self) -> *const libc::sockaddr_storage { + self.slice.as_ptr().cast() } - /// Returns the port number associated with this socket address, in native - /// endian. - pub const fn port(&self) -> u16 { - u16::from_be(self.0.sin6_port) + /// Returns a raw mutable pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of_mut!`]. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_storage { + self.slice.as_mut_ptr().cast() } +} - /// Returns the scope ID associated with this address. - pub const fn scope_id(&self) -> u32 { - self.0.sin6_scope_id +impl AsRef for Addr { + fn as_ref(&self) -> &Addr { + self } } -#[cfg(feature = "net")] -impl private::SockaddrLikePriv for SockaddrIn6 {} -#[cfg(feature = "net")] -impl SockaddrLike for SockaddrIn6 { - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; - } +impl ToOwned for Addr { + type Owned = Address; + + fn to_owned(&self) -> Self::Owned { + debug_assert!(self.len() <= mem::size_of::()); + + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + self.as_ptr().cast::(), + storage.as_mut_ptr().cast(), + self.len(), + ); } - if unsafe { (*addr).sa_family as i32 != libc::AF_INET6 } { - return None; + + Address { + storage: unsafe { storage.assume_init() }, + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: self.len() as _, } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } -#[cfg(feature = "net")] -impl AsRef for SockaddrIn6 { - fn as_ref(&self) -> &libc::sockaddr_in6 { - &self.0 +impl PartialEq
for Addr { + fn eq(&self, other: &Address) -> bool { + *self == **other } } -#[cfg(feature = "net")] -impl fmt::Display for SockaddrIn6 { +impl fmt::Display for Addr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // These things are really hard to display properly. Easier to let std - // do it. - let std = net::SocketAddrV6::new( - self.ip(), - self.port(), - self.flowinfo(), - self.scope_id(), - ); - std.fmt(f) - } -} - -#[cfg(feature = "net")] -impl From for SockaddrIn6 { - fn from(addr: net::SocketAddrV6) -> Self { - #[allow(clippy::needless_update)] // It isn't needless on Illumos - Self(libc::sockaddr_in6 { + match self.family() { + #[cfg(any(target_os = "android", target_os = "linux"))] + AddressFamily::ALG => self.to_alg().unwrap().fmt(f), + #[cfg(feature = "net")] + AddressFamily::INET => self.to_ipv4().unwrap().fmt(f), + #[cfg(feature = "net")] + AddressFamily::INET6 => self.to_ipv6().unwrap().fmt(f), #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "haiku", - target_os = "hermit", - apple_targets, + target_os = "ios", + target_os = "macos", + target_os = "illumos", target_os = "netbsd", target_os = "openbsd" ))] - sin6_len: mem::size_of::() as u8, - sin6_family: AddressFamily::Inet6 as sa_family_t, - sin6_port: addr.port().to_be(), // network byte order - sin6_addr: ipv6addr_to_libc(addr.ip()), - sin6_flowinfo: addr.flowinfo(), // host byte order - sin6_scope_id: addr.scope_id(), // host byte order - ..unsafe { mem::zeroed() } - }) - } -} - -#[cfg(feature = "net")] -impl From for net::SocketAddrV6 { - fn from(addr: SockaddrIn6) -> Self { - net::SocketAddrV6::new( - net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr), - u16::from_be(addr.0.sin6_port), - addr.0.sin6_flowinfo, - addr.0.sin6_scope_id, - ) + #[cfg(feature = "net")] + AddressFamily::LINK => self.to_link().unwrap().fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + AddressFamily::NETLINK => self.to_netlink().unwrap().fmt(f), + #[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "fuchsia" + ))] + #[cfg(feature = "net")] + AddressFamily::PACKET => self.to_link().unwrap().fmt(f), + #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(feature = "ioctl")] + AddressFamily::SYSTEM => self.to_sys_control().unwrap().fmt(f), + AddressFamily::UNIX => self.to_unix().unwrap().fmt(f), + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] + AddressFamily::VSOCK => self.to_vsock().unwrap().fmt(f), + AddressFamily::UNSPEC => "
".fmt(f), + _ => "
".fmt(f), + } } } -#[cfg(feature = "net")] -impl std::str::FromStr for SockaddrIn6 { - type Err = net::AddrParseError; - - fn from_str(s: &str) -> std::result::Result { - net::SocketAddrV6::from_str(s).map(SockaddrIn6::from) +impl Default for &Addr { + fn default() -> Self { + Addr::empty() } } -/// A container for any sockaddr type +/// General purpose socket address with arbitrary address family. /// /// Just like C's `sockaddr_storage`, this type is large enough to hold any type /// of sockaddr. It can be used as an argument with functions like -/// [`bind`](super::bind) and [`getsockname`](super::getsockname). Though it is -/// a union, it can be safely accessed through the `as_*` methods. +/// [`bind`](super::bind) and [`getsockname`](super::getsockname). +/// +/// Can be converted to a more specific type using the `to_*` methods. /// /// # Example +/// /// ``` /// # use nix::sys::socket::*; /// # use std::str::FromStr; /// # use std::os::unix::io::AsRawFd; -/// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap(); -/// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), +/// let localhost = Ipv4Address::from_str("127.0.0.1:8081").unwrap(); +/// let fd = socket(AddressFamily::INET, SockType::Stream, SockFlag::empty(), /// None).unwrap(); /// bind(fd.as_raw_fd(), &localhost).expect("bind"); -/// let ss: SockaddrStorage = getsockname(fd.as_raw_fd()).expect("getsockname"); -/// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap()); +/// let ss: Address = getsockname(fd.as_raw_fd()).expect("getsockname"); +/// assert_eq!(&localhost, ss.to_ipv4().unwrap()); /// ``` -#[derive(Clone, Copy, Eq)] +#[derive(Clone, Copy, Debug, Eq)] #[repr(C)] -pub union SockaddrStorage { - #[cfg(any(target_os = "android", target_os = "linux"))] - alg: AlgAddr, - #[cfg(all(feature = "net", not(target_os = "redox")))] - #[cfg_attr(docsrs, doc(cfg(feature = "net")))] - dl: LinkAddr, - #[cfg(any(target_os = "android", target_os = "linux"))] - nl: NetlinkAddr, - #[cfg(all(feature = "ioctl", apple_targets))] - #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))] - sctl: SysControlAddr, - #[cfg(feature = "net")] - sin: SockaddrIn, - #[cfg(feature = "net")] - sin6: SockaddrIn6, - ss: libc::sockaddr_storage, - su: UnixAddr, - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" - ))] - vsock: VsockAddr, -} -impl private::SockaddrLikePriv for SockaddrStorage {} -impl SockaddrLike for SockaddrStorage { - unsafe fn from_raw( - addr: *const libc::sockaddr, - l: Option, - ) -> Option - where - Self: Sized, - { - if addr.is_null() { - return None; - } - if let Some(len) = l { - let ulen = len as usize; - if ulen < offset_of!(libc::sockaddr, sa_data) - || ulen > mem::size_of::() - { - None - } else { - let mut ss: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8; - unsafe { ptr::copy(addr as *const u8, ssp, len as usize) }; - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux" - ))] - if i32::from(ss.ss_family) == libc::AF_UNIX { - // Safe because we UnixAddr is strictly smaller than - // SockaddrStorage, and we just initialized the structure. - unsafe { - (*(&mut ss as *mut libc::sockaddr_storage - as *mut UnixAddr)) - .sun_len = len as u8; - } - } - Some(Self { ss }) - } - } else { - // If length is not available and addr is of a fixed-length type, - // copy it. If addr is of a variable length type and len is not - // available, then there's nothing we can do. - match unsafe { (*addr).sa_family as i32 } { - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_ALG => unsafe { - AlgAddr::from_raw(addr, l).map(|alg| Self { alg }) - }, - #[cfg(feature = "net")] - libc::AF_INET => unsafe { - SockaddrIn::from_raw(addr, l).map(|sin| Self { sin }) - }, - #[cfg(feature = "net")] - libc::AF_INET6 => unsafe { - SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 }) - }, - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "illumos", - target_os = "netbsd", - target_os = "haiku", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - libc::AF_LINK => unsafe { - LinkAddr::from_raw(addr, l).map(|dl| Self { dl }) - } - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_NETLINK => unsafe { - NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl }) - }, - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux" - ))] - #[cfg(feature = "net")] - libc::AF_PACKET => unsafe { - LinkAddr::from_raw(addr, l).map(|dl| Self { dl }) - } - #[cfg(all(feature = "ioctl", apple_targets))] - libc::AF_SYSTEM => unsafe { - SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl }) - } - #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))] - libc::AF_VSOCK => unsafe { - VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock }) - }, - _ => None, - } - } - } - +pub struct Address { + storage: libc::sockaddr_storage, #[cfg(any( target_os = "android", target_os = "fuchsia", target_os = "illumos", - target_os = "linux" + target_os = "linux", + target_os = "redox", ))] - fn len(&self) -> libc::socklen_t { - match self.as_unix_addr() { - // The UnixAddr type knows its own length - Some(ua) => ua.len(), - // For all else, we're just a boring SockaddrStorage - None => mem::size_of_val(self) as libc::socklen_t, - } - } - - unsafe fn set_length( - &mut self, - new_length: usize, - ) -> std::result::Result<(), SocketAddressLengthNotDynamic> { - match self.as_unix_addr_mut() { - Some(addr) => unsafe { addr.set_length(new_length) }, - None => Err(SocketAddressLengthNotDynamic), - } - } + pub(super) len: usize, } -macro_rules! accessors { - ( - $fname:ident, - $fname_mut:ident, - $sockty:ty, - $family:expr, - $libc_ty:ty, - $field:ident) => { - /// Safely and falliably downcast to an immutable reference - pub fn $fname(&self) -> Option<&$sockty> { - if self.family() == Some($family) - && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t - { - // Safe because family and len are validated - Some(unsafe { &self.$field }) - } else { - None - } - } - - /// Safely and falliably downcast to a mutable reference - pub fn $fname_mut(&mut self) -> Option<&mut $sockty> { - if self.family() == Some($family) - && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t - { - // Safe because family and len are validated - Some(unsafe { &mut self.$field }) - } else { - None - } - } - }; -} +#[allow(clippy::len_without_is_empty)] +impl Address { + sockaddr_family!(); -impl SockaddrStorage { - /// Downcast to an immutable `[UnixAddr]` reference. - pub fn as_unix_addr(&self) -> Option<&UnixAddr> { + /// Returns the length of this socket address. + /// + /// The returned length is guaranteed to be in bounds of `[2, SIZE]` with `SIZE` being the size + /// of the underlying libc type. + /// + /// The length can also be zero, in which case the address hasn't been initialized yet. + pub const fn len(&self) -> usize { cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux" - ))] - { - let p = unsafe{ &self.ss as *const libc::sockaddr_storage }; - // Safe because UnixAddr is strictly smaller than - // sockaddr_storage, and we're fully initialized - let len = unsafe { - (*(p as *const UnixAddr )).sun_len as usize - }; + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + self.len } else { - let len = self.len() as usize; + self.storage.ss_len as usize } } - // Sanity checks - if self.family() != Some(AddressFamily::Unix) - || len < offset_of!(libc::sockaddr_un, sun_path) - || len > mem::size_of::() - { - None - } else { - Some(unsafe { &self.su }) - } } - /// Downcast to a mutable `[UnixAddr]` reference. - pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> { - cfg_if! { - if #[cfg(any(target_os = "android", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux" - ))] - { - let p = unsafe{ &self.ss as *const libc::sockaddr_storage }; - // Safe because UnixAddr is strictly smaller than - // sockaddr_storage, and we're fully initialized - let len = unsafe { - (*(p as *const UnixAddr )).sun_len as usize - }; - } else { - let len = self.len() as usize; - } - } - // Sanity checks - if self.family() != Some(AddressFamily::Unix) - || len < offset_of!(libc::sockaddr_un, sun_path) - || len > mem::size_of::() - { - None - } else { - Some(unsafe { &mut self.su }) - } + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_storage { + &self.storage } - #[cfg(any(target_os = "android", target_os = "linux"))] - accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr, - AddressFamily::Alg, libc::sockaddr_alg, alg} - - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux" - ))] - #[cfg(feature = "net")] - accessors! { - as_link_addr, as_link_addr_mut, LinkAddr, - AddressFamily::Packet, libc::sockaddr_ll, dl} - - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - accessors! { - as_link_addr, as_link_addr_mut, LinkAddr, - AddressFamily::Link, libc::sockaddr_dl, dl} - - #[cfg(feature = "net")] - accessors! { - as_sockaddr_in, as_sockaddr_in_mut, SockaddrIn, - AddressFamily::Inet, libc::sockaddr_in, sin} - - #[cfg(feature = "net")] - accessors! { - as_sockaddr_in6, as_sockaddr_in6_mut, SockaddrIn6, - AddressFamily::Inet6, libc::sockaddr_in6, sin6} - - #[cfg(any(target_os = "android", target_os = "linux"))] - accessors! {as_netlink_addr, as_netlink_addr_mut, NetlinkAddr, - AddressFamily::Netlink, libc::sockaddr_nl, nl} - - #[cfg(all(feature = "ioctl", apple_targets))] - #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))] - accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr, - AddressFamily::System, libc::sockaddr_ctl, sctl} + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_storage { + &mut self.storage + } +} - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" - ))] - accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr, - AddressFamily::Vsock, libc::sockaddr_vm, vsock} +impl Default for Address { + fn default() -> Self { + Self { + storage: unsafe { mem::zeroed() }, + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: 0, + } + } } -impl fmt::Debug for SockaddrStorage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SockaddrStorage") - // Safe because sockaddr_storage has the least specific - // field types - .field("ss", unsafe { &self.ss }) - .finish() +impl AsRef for Address { + fn as_ref(&self) -> &libc::sockaddr_storage { + &self.storage } } -impl fmt::Display for SockaddrStorage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +#[cfg(feature = "net")] +impl From for Address { + fn from(s: net::SocketAddrV4) -> Self { unsafe { - match self.ss.ss_family as i32 { - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_ALG => self.alg.fmt(f), - #[cfg(feature = "net")] - libc::AF_INET => self.sin.fmt(f), - #[cfg(feature = "net")] - libc::AF_INET6 => self.sin6.fmt(f), - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - libc::AF_LINK => self.dl.fmt(f), - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_NETLINK => self.nl.fmt(f), - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "fuchsia" - ))] - #[cfg(feature = "net")] - libc::AF_PACKET => self.dl.fmt(f), - #[cfg(apple_targets)] - #[cfg(feature = "ioctl")] - libc::AF_SYSTEM => self.sctl.fmt(f), - libc::AF_UNIX => self.su.fmt(f), + let mut ss = MaybeUninit::::zeroed(); + + let sin = Ipv4Address::from(s); + + ptr::copy_nonoverlapping(&sin.0, ss.as_mut_ptr().cast(), 1); + + Self { + storage: ss.assume_init(), #[cfg(any( target_os = "android", + target_os = "fuchsia", + target_os = "illumos", target_os = "linux", - target_os = "macos" + target_os = "redox", ))] - libc::AF_VSOCK => self.vsock.fmt(f), - _ => "
".fmt(f), + len: mem::size_of::() as _, } } } } #[cfg(feature = "net")] -impl From for SockaddrStorage { - fn from(s: net::SocketAddrV4) -> Self { - unsafe { - let mut ss: Self = mem::zeroed(); - ss.sin = SockaddrIn::from(s); - ss - } - } -} - -#[cfg(feature = "net")] -impl From for SockaddrStorage { +impl From for Address { fn from(s: net::SocketAddrV6) -> Self { unsafe { - let mut ss: Self = mem::zeroed(); - ss.sin6 = SockaddrIn6::from(s); - ss + let mut ss = MaybeUninit::::zeroed(); + + let sin = Ipv6Address::from(s); + + ptr::copy_nonoverlapping(&sin.0, ss.as_mut_ptr().cast(), 1); + + Self { + storage: ss.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: mem::size_of::() as _, + } } } } #[cfg(feature = "net")] -impl From for SockaddrStorage { +impl From for Address { fn from(s: net::SocketAddr) -> Self { match s { net::SocketAddr::V4(sa4) => Self::from(sa4), @@ -1606,116 +2195,77 @@ impl From for SockaddrStorage { } } -impl Hash for SockaddrStorage { - fn hash(&self, s: &mut H) { +impl std::ops::Deref for Address { + type Target = Addr; + + fn deref(&self) -> &Self::Target { unsafe { - match self.ss.ss_family as i32 { - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_ALG => self.alg.hash(s), - #[cfg(feature = "net")] - libc::AF_INET => self.sin.hash(s), - #[cfg(feature = "net")] - libc::AF_INET6 => self.sin6.hash(s), - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - libc::AF_LINK => self.dl.hash(s), - #[cfg(any(target_os = "android", target_os = "linux"))] - libc::AF_NETLINK => self.nl.hash(s), - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "fuchsia" - ))] - #[cfg(feature = "net")] - libc::AF_PACKET => self.dl.hash(s), - #[cfg(apple_targets)] - #[cfg(feature = "ioctl")] - libc::AF_SYSTEM => self.sctl.hash(s), - libc::AF_UNIX => self.su.hash(s), - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" - ))] - libc::AF_VSOCK => self.vsock.hash(s), - _ => self.ss.hash(s), - } + &*Addr::from_raw_parts(&self.storage, self.len()) } } } -impl PartialEq for SockaddrStorage { - fn eq(&self, other: &Self) -> bool { +impl std::ops::DerefMut for Address { + fn deref_mut(&mut self) -> &mut Self::Target { + let len = self.len(); + unsafe { - match (self.ss.ss_family as i32, other.ss.ss_family as i32) { - #[cfg(any(target_os = "android", target_os = "linux"))] - (libc::AF_ALG, libc::AF_ALG) => self.alg == other.alg, - #[cfg(feature = "net")] - (libc::AF_INET, libc::AF_INET) => self.sin == other.sin, - #[cfg(feature = "net")] - (libc::AF_INET6, libc::AF_INET6) => self.sin6 == other.sin6, - #[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd" - ))] - #[cfg(feature = "net")] - (libc::AF_LINK, libc::AF_LINK) => self.dl == other.dl, - #[cfg(any(target_os = "android", target_os = "linux"))] - (libc::AF_NETLINK, libc::AF_NETLINK) => self.nl == other.nl, - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux" - ))] - #[cfg(feature = "net")] - (libc::AF_PACKET, libc::AF_PACKET) => self.dl == other.dl, - #[cfg(apple_targets)] - #[cfg(feature = "ioctl")] - (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl, - (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su, - #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos" - ))] - (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock, - _ => false, - } + &mut *Addr::from_raw_parts_mut(&mut self.storage, len) } } } -pub(super) mod private { - pub trait SockaddrLikePriv { - /// Returns a mutable raw pointer to the inner structure. - /// - /// # Safety - /// - /// This method is technically safe, but modifying the inner structure's - /// `family` or `len` fields may result in violating Nix's invariants. - /// It is best to use this method only with foreign functions that do - /// not change the sockaddr type. - fn as_mut_ptr(&mut self) -> *mut libc::sockaddr { - self as *mut Self as *mut libc::sockaddr - } +impl AsRef for Address { + fn as_ref(&self) -> &Addr { + self + } +} + +impl AsMut for Address { + fn as_mut(&mut self) -> &mut Addr { + self + } +} + +impl Borrow for Address { + fn borrow(&self) -> &Addr { + self + } +} + +impl BorrowMut for Address { + fn borrow_mut(&mut self) -> &mut Addr { + self + } +} + +impl PartialEq
for Address { + fn eq(&self, other: &Address) -> bool { + **self == **other + } +} + +impl PartialEq for Address { + fn eq(&self, other: &Addr) -> bool { + **self == *other + } +} + +impl Hash for Address { + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + +impl std::fmt::Display for Address { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + (**self).fmt(f) } } #[cfg(any(target_os = "android", target_os = "linux"))] pub mod netlink { use super::*; - use crate::sys::socket::addr::AddressFamily; use libc::{sa_family_t, sockaddr_nl}; use std::{fmt, mem}; @@ -1726,20 +2276,23 @@ pub mod netlink { /// [netlink(7)](https://man7.org/linux/man-pages/man7/netlink.7.html) #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] - pub struct NetlinkAddr(pub(in super::super) sockaddr_nl); + pub struct NetlinkAddress(pub(in super::super) sockaddr_nl); - impl NetlinkAddr { + impl NetlinkAddress { /// Construct a new socket address from its port ID and multicast groups /// mask. - pub fn new(pid: u32, groups: u32) -> NetlinkAddr { + pub fn new(pid: u32, groups: u32) -> NetlinkAddress { let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; - addr.nl_family = AddressFamily::Netlink as sa_family_t; + addr.nl_family = libc::AF_NETLINK as sa_family_t; addr.nl_pid = pid; addr.nl_groups = groups; - NetlinkAddr(addr) + NetlinkAddress(addr) } + sockaddr_family!(); + sockaddr_len_static!(); + /// Return the socket's port ID. pub const fn pid(&self) -> u32 { self.0.nl_pid @@ -1749,36 +2302,65 @@ pub mod netlink { pub const fn groups(&self) -> u32 { self.0.nl_groups } + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_nl { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_nl { + &mut self.0 + } } - impl private::SockaddrLikePriv for NetlinkAddr {} - impl SockaddrLike for NetlinkAddr { - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for NetlinkAddress { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } + } + + impl Borrow for NetlinkAddress { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: NetlinkAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_NETLINK } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } - impl AsRef for NetlinkAddr { + impl AsRef for NetlinkAddress { fn as_ref(&self) -> &libc::sockaddr_nl { &self.0 } } - impl fmt::Display for NetlinkAddr { + impl fmt::Display for NetlinkAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "pid: {} groups: {}", self.pid(), self.groups()) } @@ -1796,38 +2378,71 @@ pub mod alg { /// Socket address for the Linux kernel crypto API #[derive(Copy, Clone)] #[repr(transparent)] - pub struct AlgAddr(pub(in super::super) sockaddr_alg); - - impl private::SockaddrLikePriv for AlgAddr {} - impl SockaddrLike for AlgAddr { - unsafe fn from_raw( - addr: *const libc::sockaddr, - l: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = l { - if l != mem::size_of::() as libc::socklen_t - { - return None; + pub struct AlgAddress(pub(in super::super) sockaddr_alg); + + impl AlgAddress { + sockaddr_family!(); + sockaddr_len_static!(); + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_alg { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_alg { + &mut self.0 + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for AlgAddress { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } + } + + impl Borrow for AlgAddress { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: AlgAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_ALG } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } - impl AsRef for AlgAddr { + impl AsRef for AlgAddress { fn as_ref(&self) -> &libc::sockaddr_alg { &self.0 } } // , PartialEq, Eq, Debug, Hash - impl PartialEq for AlgAddr { + impl PartialEq for AlgAddress { fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); ( @@ -1846,9 +2461,9 @@ pub mod alg { } } - impl Eq for AlgAddr {} + impl Eq for AlgAddress {} - impl Hash for AlgAddr { + impl Hash for AlgAddress { fn hash(&self, s: &mut H) { let inner = self.0; ( @@ -1862,9 +2477,9 @@ pub mod alg { } } - impl AlgAddr { + impl AlgAddress { /// Construct an `AF_ALG` socket from its cipher name and type. - pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr { + pub fn new(alg_type: &str, alg_name: &str) -> AlgAddress { let mut addr: sockaddr_alg = unsafe { mem::zeroed() }; addr.salg_family = AF_ALG as u16; addr.salg_type[..alg_type.len()] @@ -1872,21 +2487,25 @@ pub mod alg { addr.salg_name[..alg_name.len()] .copy_from_slice(alg_name.to_string().as_bytes()); - AlgAddr(addr) + AlgAddress(addr) } /// Return the socket's cipher type, for example `hash` or `aead`. pub fn alg_type(&self) -> &CStr { - unsafe { CStr::from_ptr(self.0.salg_type.as_ptr().cast()) } + unsafe { + CStr::from_ptr(self.0.salg_type.as_ptr().cast()) + } } /// Return the socket's cipher name, for example `sha1`. pub fn alg_name(&self) -> &CStr { - unsafe { CStr::from_ptr(self.0.salg_name.as_ptr().cast()) } + unsafe { + CStr::from_ptr(self.0.salg_name.as_ptr().cast()) + } } } - impl fmt::Display for AlgAddr { + impl fmt::Display for AlgAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, @@ -1897,7 +2516,7 @@ pub mod alg { } } - impl fmt::Debug for AlgAddr { + impl fmt::Debug for AlgAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } @@ -1906,14 +2525,13 @@ pub mod alg { feature! { #![feature = "ioctl"] -#[cfg(apple_targets)] +#[cfg(any(target_os = "ios", target_os = "macos"))] pub mod sys_control { - use crate::sys::socket::addr::AddressFamily; use libc::{self, c_uchar}; - use std::{fmt, mem, ptr}; + use std::{fmt, mem}; use std::os::unix::io::RawFd; use crate::{Errno, Result}; - use super::{private, SockaddrLike}; + use super::*; // FIXME: Move type into `libc` #[repr(C)] @@ -1937,50 +2555,88 @@ pub mod sys_control { /// #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] - pub struct SysControlAddr(pub(in super::super) libc::sockaddr_ctl); + pub struct SysControlAddress(pub(in super::super) libc::sockaddr_ctl); - impl private::SockaddrLikePriv for SysControlAddr {} - impl SockaddrLike for SysControlAddr { - unsafe fn from_raw(addr: *const libc::sockaddr, len: Option) - -> Option where Self: Sized - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; + impl SysControlAddress { + sockaddr_family!(); + sockaddr_len_static!(); + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_ctl { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_ctl { + &mut self.0 + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for SysControlAddress { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } + } + + impl Borrow for SysControlAddress { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: SysControlAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_SYSTEM } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) } )) } } - impl AsRef for SysControlAddr { + impl AsRef for SysControlAddress { fn as_ref(&self) -> &libc::sockaddr_ctl { &self.0 } } - impl SysControlAddr { - /// Construct a new `SysControlAddr` from its kernel unique identifier + impl SysControlAddress { + /// Construct a new `SysControlAddress` from its kernel unique identifier /// and unit number. - pub const fn new(id: u32, unit: u32) -> SysControlAddr { + pub const fn new(id: u32, unit: u32) -> SysControlAddress { let addr = libc::sockaddr_ctl { sc_len: mem::size_of::() as c_uchar, - sc_family: AddressFamily::System as c_uchar, + sc_family: libc::AF_SYSTEM as c_uchar, ss_sysaddr: libc::AF_SYS_CONTROL as u16, sc_id: id, sc_unit: unit, sc_reserved: [0; 5] }; - SysControlAddr(addr) + SysControlAddress(addr) } - /// Construct a new `SysControlAddr` from its human readable name and + /// Construct a new `SysControlAddress` from its human readable name and /// unit number. - pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result { + pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result { if name.len() > MAX_KCTL_NAME { return Err(Errno::ENAMETOOLONG); } @@ -1991,7 +2647,7 @@ pub mod sys_control { unsafe { ctl_info(sockfd, &mut info)?; } - Ok(SysControlAddr::new(info.ctl_id, unit)) + Ok(SysControlAddress::new(info.ctl_id, unit)) } /// Return the kernel unique identifier @@ -2005,7 +2661,7 @@ pub mod sys_control { } } - impl fmt::Display for SysControlAddr { + impl fmt::Display for SysControlAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self, f) } @@ -2017,14 +2673,17 @@ pub mod sys_control { mod datalink { feature! { #![feature = "net"] - use super::{fmt, mem, private, ptr, SockaddrLike}; + use super::*; /// Hardware Address #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] - pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll); + pub struct LinkAddress(pub(in super::super) libc::sockaddr_ll); + + impl LinkAddress { + sockaddr_family!(); + sockaddr_len_static!(); - impl LinkAddr { /// Physical-layer protocol pub fn protocol(&self) -> u16 { self.0.sll_protocol @@ -2062,9 +2721,19 @@ mod datalink { self.0.sll_addr[5], ]) } + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_ll { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_ll { + &mut self.0 + } } - impl fmt::Display for LinkAddr { + impl fmt::Display for LinkAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(addr) = self.addr() { write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", @@ -2079,25 +2748,48 @@ mod datalink { } } } - impl private::SockaddrLikePriv for LinkAddr {} - impl SockaddrLike for LinkAddr { - unsafe fn from_raw(addr: *const libc::sockaddr, - len: Option) - -> Option where Self: Sized - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; + + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for LinkAddress { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } + } + + impl Borrow for LinkAddress { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: LinkAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_PACKET } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } - impl AsRef for LinkAddr { + impl AsRef for LinkAddress { fn as_ref(&self) -> &libc::sockaddr_ll { &self.0 } @@ -2109,7 +2801,8 @@ mod datalink { #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "illumos", target_os = "netbsd", target_os = "haiku", @@ -2119,108 +2812,360 @@ mod datalink { mod datalink { feature! { #![feature = "net"] - use super::{fmt, mem, private, ptr, SockaddrLike}; + use super::*; + + unsafe fn link_true_len(addr: *const libc::sockaddr_dl) -> usize { + const OFFSET: usize = offset_of!(libc::sockaddr_dl, sdl_data); + + cfg_if! { + if #[cfg(target_os = "haiku")] { + unsafe { + OFFSET + *addr_of!((*addr).sdl_nlen) as usize + *addr_of!((*addr).sdl_alen) as usize + } + } else { + unsafe { + OFFSET + *addr_of!((*addr).sdl_nlen) as usize + *addr_of!((*addr).sdl_alen) as usize + *addr_of!((*addr).sdl_slen) as usize + } + } + } + } + + /// Hardware Address + #[derive(Debug, PartialEq, Eq, Hash)] + #[cfg_attr(not(doc), repr(C, align(2)))] + pub struct LinkAddr { + slice: [u8], + } + + #[allow(clippy::len_without_is_empty)] + impl LinkAddr { + const _ALIGN: () = assert!(mem::align_of::() == 2); + + // SAFETY: When dereferencing, `data` must be valid for reads of at least `len` bytes + // and must point to a valid link layer address. + #[allow(clippy::cast_ptr_alignment)] + pub(super) const fn from_raw_parts(data: *const libc::sockaddr_dl, len: usize) -> *const Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts(data_ptr, len); + + slice_ptr as *const Self + } + + // SAFETY: When dereferencing, `data` must be valid for reads and writes of at least `len` bytes + // and must point to a valid link layer address. + #[allow(clippy::cast_ptr_alignment)] + fn from_raw_parts_mut(data: *mut libc::sockaddr_dl, len: usize) -> *mut Self { + let data_ptr = data.cast::(); + let slice_ptr = ptr::slice_from_raw_parts_mut(data_ptr, len); + + + slice_ptr as *mut Self + } + + /// Returns the length of this socket address. + /// + /// The returned length is guaranteed to be in bounds of `[2, MAX]` with `MAX` being the + /// maximum length of a socket address (i.e. the size of [`libc::sockaddr_storage`]). + pub fn len(&self) -> usize { + self.slice.len() + } + + /// interface index, if != 0, system given index for interface + #[cfg(not(target_os = "haiku"))] + pub fn ifindex(&self) -> usize { + unsafe { + *addr_of!((*self.as_ptr()).sdl_index) as _ + } + } + + /// Datalink type + #[cfg(not(target_os = "haiku"))] + pub fn datalink_type(&self) -> u8 { + unsafe { + *addr_of!((*self.as_ptr()).sdl_type) as _ + } + } + + /// MAC address start position + pub fn nlen(&self) -> usize { + unsafe { + *addr_of!((*self.as_ptr()).sdl_nlen) as _ + } + } + + /// link level address length + pub fn alen(&self) -> usize { + unsafe { + *addr_of!((*self.as_ptr()).sdl_alen) as _ + } + } + + /// link layer selector length + #[cfg(not(target_os = "haiku"))] + pub fn slen(&self) -> usize { + unsafe { + *addr_of!((*self.as_ptr()).sdl_slen) as _ + } + } + + /// Returns the "true" length of this link address, including + /// its name, address and selector. + /// + /// This value can both be smaller or larger than [`Self::len`]. + pub fn true_len(&self) -> usize { + let len = unsafe { link_true_len(self.as_ptr()) }; + + debug_assert!(len <= self.len()); + + len + } + + /// Physical-layer address. + /// + /// Usually 6 bytes (MAC address). + // The cast is not unnecessary on all platforms. + pub fn addr(&self) -> Option<&[u8]> { + let nlen = self.nlen(); + let alen = self.alen(); + + let offset = offset_of!(libc::sockaddr_dl, sdl_data) + nlen; + + if offset + alen > self.len() { + return None; + } + + Some(&self.slice[offset..offset + alen]) + } + + /// Converts to a boxed [`UnixAddr`]. + /// + /// # Panics + /// + /// This method panics if the allocation fails. + pub fn to_boxed(&self) -> Box { + let layout = std::alloc::Layout::for_value(self); + + unsafe { + let box_ptr = std::alloc::alloc(layout); + assert_ne!(box_ptr, std::ptr::null_mut()); + + ptr::copy_nonoverlapping(self.as_ptr().cast(), box_ptr, self.len()); + + Box::from_raw(Self::from_raw_parts_mut(box_ptr.cast(), self.len())) + } + } + + /// Converts to an owned [`LinkAddress`] if it fits. + /// + /// If you are unsure whether it fits into [`LinkAddress`], consider using + /// [`Self::to_boxed`] instead. + pub fn to_owned(&self) -> Option { + if self.len() > mem::size_of::() { + return None; + } + + unsafe { + let mut addr = MaybeUninit::::zeroed(); + + ptr::copy_nonoverlapping(self.slice.as_ptr(), addr.as_mut_ptr().cast(), self.len()); + + Some(LinkAddress(addr.assume_init())) + } + } + + /// Converts to an owned [`LinkAddress`], truncating the length to its "true" length. + /// + /// This method returns `None` if the "true" length is larger that the length of the address + /// or the size of `libc::sockaddr_dl`. + pub fn to_owned_truncate(&self) -> Option { + cfg_if! { + if #[cfg(not(target_os = "illumos"))] { + if self.true_len() > self.len() || self.true_len() > mem::size_of::() { + return None; + } + + unsafe { + let mut addr = MaybeUninit::::zeroed(); + + ptr::copy_nonoverlapping(self.slice.as_ptr(), addr.as_mut_ptr().cast(), self.true_len()); + + addr_of_mut!((*addr.as_mut_ptr()).sdl_len).write(self.true_len() as _); + + Some(LinkAddress(addr.assume_init())) + } + } else { + // For illumos, len and true_len are equal + self.to_owned() + } + } + } + + /// Returns a raw pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of!`]. + pub const fn as_ptr(&self) -> *const libc::sockaddr_dl { + self.slice.as_ptr().cast() + } + + /// Returns a raw mutable pointer to the libc type. + /// + /// # Safety + /// + /// Keep in mind that it is in general *not* safe to dereference + /// this pointer, as the length of this address can be smaller than + /// the size of the libc type, leading to uninitialized fields. + /// + /// It is preferable to access individual fieds using [`addr_of_mut!`]. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_dl { + self.slice.as_mut_ptr().cast() + } + } + + impl fmt::Display for LinkAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(addr) = self.addr() { + if addr.len() == 6 { + write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + addr[0], + addr[1], + addr[2], + addr[3], + addr[4], + addr[5], + ) + } else { + write!(f, "") + } + } else { + Ok(()) + } + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for LinkAddr { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(self.as_ptr() as *const _, self.len()) } + } + } + + impl Borrow for LinkAddr { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } /// Hardware Address #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] - pub struct LinkAddr(pub(in super::super) libc::sockaddr_dl); + pub struct LinkAddress(pub(in super::super) libc::sockaddr_dl); - impl LinkAddr { - /// interface index, if != 0, system given index for interface - #[cfg(not(target_os = "haiku"))] - pub fn ifindex(&self) -> usize { - self.0.sdl_index as usize - } + impl LinkAddress { + sockaddr_family!(); - /// Datalink type - #[cfg(not(target_os = "haiku"))] - pub fn datalink_type(&self) -> u8 { - self.0.sdl_type - } + /// Returns the length of this socket address. + /// + /// The returned length is guaranteed to be in bounds of [2, SIZE] with SIZE being the size + /// of the underlying libc type. + fn len(&self) -> usize { + cfg_if! { + if #[cfg(not(target_os = "illumos"))] { + let len = self.0.sdl_len as _; + } else { + // On illumos, `sdl_len` is not available. That's the best guess we can make. + let len = unsafe { link_true_len(&self.0) }; + } + } - /// MAC address start position - pub fn nlen(&self) -> usize { - self.0.sdl_nlen as usize + debug_assert!(len <= mem::size_of::()); + + len } - /// link level address length - pub fn alen(&self) -> usize { - self.0.sdl_alen as usize + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const libc::sockaddr_dl { + &self.0 } - /// link layer selector length - #[cfg(not(target_os = "haiku"))] - pub fn slen(&self) -> usize { - self.0.sdl_slen as usize + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_dl { + &mut self.0 } + } - /// if link level address length == 0, - /// or `sdl_data` not be larger. - pub fn is_empty(&self) -> bool { - let nlen = self.nlen(); - let alen = self.alen(); - let data_len = self.0.sdl_data.len(); + impl std::ops::Deref for LinkAddress { + type Target = LinkAddr; - alen == 0 || nlen + alen >= data_len + fn deref(&self) -> &Self::Target { + unsafe { + &*LinkAddr::from_raw_parts(&self.0, self.len()) + } } + } - /// Physical-layer address (MAC) - // The cast is not unnecessary on all platforms. - #[allow(clippy::unnecessary_cast)] - pub fn addr(&self) -> Option<[u8; 6]> { - let nlen = self.nlen(); - let data = self.0.sdl_data; + impl std::ops::DerefMut for LinkAddress { + fn deref_mut(&mut self) -> &mut Self::Target { + let len = self.len(); - if self.is_empty() { - None - } else { - Some([ - data[nlen] as u8, - data[nlen + 1] as u8, - data[nlen + 2] as u8, - data[nlen + 3] as u8, - data[nlen + 4] as u8, - data[nlen + 5] as u8, - ]) + unsafe { + &mut *LinkAddr::from_raw_parts_mut(&mut self.0, len) } } } - impl fmt::Display for LinkAddr { + impl fmt::Display for LinkAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(addr) = self.addr() { - write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - addr[0], - addr[1], - addr[2], - addr[3], - addr[4], - addr[5]) - } else { - Ok(()) - } + (**self).fmt(f) } } - impl private::SockaddrLikePriv for LinkAddr {} - impl SockaddrLike for LinkAddr { - unsafe fn from_raw(addr: *const libc::sockaddr, - len: Option) - -> Option where Self: Sized - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; + + impl AsRef for LinkAddress { + fn as_ref(&self) -> &Addr { + (**self).as_ref() + } + } + + impl Borrow for LinkAddress { + fn borrow(&self) -> &Addr { + (**self).borrow() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: LinkAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_LINK } { - return None; - } - Some(Self(unsafe { ptr::read_unaligned(addr as *const _) })) } } - impl AsRef for LinkAddr { + impl AsRef for LinkAddress { fn as_ref(&self) -> &libc::sockaddr_dl { &self.0 } @@ -2231,7 +3176,6 @@ mod datalink { #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] pub mod vsock { use super::*; - use crate::sys::socket::addr::AddressFamily; use libc::{sa_family_t, sockaddr_vm}; use std::hash::{Hash, Hasher}; use std::{fmt, mem}; @@ -2243,36 +3187,55 @@ pub mod vsock { /// [vsock(7)](https://man7.org/linux/man-pages/man7/vsock.7.html) #[derive(Copy, Clone)] #[repr(transparent)] - pub struct VsockAddr(pub(in super::super) sockaddr_vm); - - impl private::SockaddrLikePriv for VsockAddr {} - impl SockaddrLike for VsockAddr { - unsafe fn from_raw( - addr: *const libc::sockaddr, - len: Option, - ) -> Option - where - Self: Sized, - { - if let Some(l) = len { - if l != mem::size_of::() as libc::socklen_t { - return None; + pub struct VsockAddress(pub(in super::super) sockaddr_vm); + + #[allow(clippy::cast_ptr_alignment)] + impl AsRef for VsockAddress { + fn as_ref(&self) -> &Addr { + unsafe { &*Addr::from_raw_parts(&self.0 as *const _ as *const _, self.len()) } + } + } + + impl Borrow for VsockAddress { + fn borrow(&self) -> &Addr { + self.as_ref() + } + } + + #[allow(clippy::cast_ptr_alignment)] + impl From for Address { + fn from(addr: VsockAddress) -> Self { + let mut storage = MaybeUninit::::zeroed(); + + unsafe { + ptr::copy_nonoverlapping( + &addr.0 as *const _ as *const u8, + storage.as_mut_ptr().cast(), + addr.len(), + ); + + Address { + storage: storage.assume_init(), + #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] + len: addr.len() as _, } } - if unsafe { (*addr).sa_family as i32 != libc::AF_VSOCK } { - return None; - } - unsafe { Some(Self(ptr::read_unaligned(addr as *const _))) } } } - impl AsRef for VsockAddr { + impl AsRef for VsockAddress { fn as_ref(&self) -> &libc::sockaddr_vm { &self.0 } } - impl PartialEq for VsockAddr { + impl PartialEq for VsockAddress { #[cfg(any(target_os = "android", target_os = "linux"))] fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); @@ -2282,23 +3245,14 @@ pub mod vsock { #[cfg(target_os = "macos")] fn eq(&self, other: &Self) -> bool { let (inner, other) = (self.0, other.0); - ( - inner.svm_family, - inner.svm_cid, - inner.svm_port, - inner.svm_len, - ) == ( - other.svm_family, - other.svm_cid, - other.svm_port, - inner.svm_len, - ) + (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len) + == (other.svm_family, other.svm_cid, other.svm_port, inner.svm_len) } } - impl Eq for VsockAddr {} + impl Eq for VsockAddress {} - impl Hash for VsockAddr { + impl Hash for VsockAddress { #[cfg(any(target_os = "android", target_os = "linux"))] fn hash(&self, s: &mut H) { let inner = self.0; @@ -2307,13 +3261,7 @@ pub mod vsock { #[cfg(target_os = "macos")] fn hash(&self, s: &mut H) { let inner = self.0; - ( - inner.svm_family, - inner.svm_cid, - inner.svm_port, - inner.svm_len, - ) - .hash(s); + (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).hash(s); } } @@ -2321,19 +3269,22 @@ pub mod vsock { /// /// The address for AF_VSOCK socket is defined as a combination of a /// 32-bit Context Identifier (CID) and a 32-bit port number. - impl VsockAddr { + impl VsockAddress { + sockaddr_family!(); + sockaddr_len_static!(); + /// Construct a `VsockAddr` from its raw fields. - pub fn new(cid: u32, port: u32) -> VsockAddr { + pub fn new(cid: u32, port: u32) -> VsockAddress { let mut addr: sockaddr_vm = unsafe { mem::zeroed() }; - addr.svm_family = AddressFamily::Vsock as sa_family_t; + addr.svm_family = libc::AF_VSOCK as sa_family_t; addr.svm_cid = cid; addr.svm_port = port; #[cfg(target_os = "macos")] { - addr.svm_len = std::mem::size_of::() as u8; + addr.svm_len = std::mem::size_of::() as u8; } - VsockAddr(addr) + VsockAddress(addr) } /// Context Identifier (CID) @@ -2345,21 +3296,228 @@ pub mod vsock { pub fn port(&self) -> u32 { self.0.svm_port } + + /// Returns a raw pointer to the libc type. + pub const fn as_ptr(&self) -> *const sockaddr_vm { + &self.0 + } + + /// Returns a raw mutable pointer to the libc type. + pub fn as_mut_ptr(&mut self) -> *mut sockaddr_vm { + &mut self.0 + } } - impl fmt::Display for VsockAddr { + impl fmt::Display for VsockAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "cid: {} port: {}", self.cid(), self.port()) } } - impl fmt::Debug for VsockAddr { + impl fmt::Debug for VsockAddress { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } } +macro_rules! raw_address_conv { + ($fname:ident, $nixty:tt, $libcty:ident, $af:ident, $doc:tt) => { + #[doc = $doc] + pub fn $fname(&self) -> Option<&$nixty> { + if self.family() != AddressFamily::$af { + return None; + } + + unsafe { + Some(&*(self.as_ptr().cast())) + } + } + }; +} + +/// Non-owning pointer to a raw socket address. +/// +/// In contrast to [`Addr`], this type may or may not have +/// a length associated with it, depending on the platform. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct RawAddr<'a> { + addr: &'a libc::sockaddr, +} + +#[allow(clippy::len_without_is_empty)] +impl<'a> RawAddr<'a> { + /// Creates a new `RawAddr` from a reference to a socket address. + /// + /// # Safety + /// + /// The reference must be castable to the concrete type of the address, + /// based on its address family. + #[allow(unused)] + pub(crate) const unsafe fn new(addr: &'a libc::sockaddr) -> Option { + Some(Self { addr }) + } + + /// Returns a raw pointer to the libc type. + /// + /// This pointer can be casted to a pointer of the concrete type of the address, + /// based on its address family. + pub const fn as_ptr(&self) -> *const libc::sockaddr { + self.addr as *const _ + } + + /// Returns the address family of the address. + pub const fn family(&self) -> AddressFamily { + AddressFamily::of(self.addr) + } + + /// Returns the length of the address, if possible to derive. + pub fn len(&self) -> Option { + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + match self.family() { + #[cfg(any(target_os = "android", target_os = "linux"))] + AddressFamily::ALG => Some(mem::size_of::()), + #[cfg(feature = "net")] + AddressFamily::INET => Some(mem::size_of::()), + #[cfg(feature = "net")] + AddressFamily::INET6 => Some(mem::size_of::()), + #[cfg(any(target_os = "android", target_os = "linux"))] + AddressFamily::NETLINK => Some(mem::size_of::()), + #[cfg(all( + feature = "net", + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ), + ))] + AddressFamily::PACKET => Some(mem::size_of::()), + #[cfg(all( + feature = "net", + target_os = "illumos", + ))] + AddressFamily::LINK => { + let ptr = self.as_ptr().cast::(); + let nlen = unsafe { addr_of!((*ptr).sdl_nlen).read() as usize }; + let alen = unsafe { addr_of!((*ptr).sdl_alen).read() as usize }; + let slen = unsafe { addr_of!((*ptr).sdl_slen).read() as usize }; + Some(offset_of!(libc::sockaddr_dl, sdl_data) + nlen + alen + slen) + } + #[cfg(any(target_os = "android", target_os = "linux"))] + AddressFamily::VSOCK => Some(mem::size_of::()), + _ => None, + } + } else { + Some(self.addr.sa_len as _) + } + } + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + raw_address_conv!(to_alg, AlgAddress, sockaddr_alg, ALG, "Converts to [`AlgAddress`], if the address family matches."); + + #[cfg(feature = "net")] + raw_address_conv!(to_ipv4, Ipv4Address, sockaddr_in, INET, "Converts to [`Ipv4Address`], if the address family matches."); + + #[cfg(feature = "net")] + raw_address_conv!(to_ipv6, Ipv6Address, sockaddr_in6, INET6, "Converts to [`Ipv6Address`], if the address family matches."); + + #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] + raw_address_conv!(to_vsock, VsockAddress, sockaddr_vm, VSOCK, "Converts to [`VsockAddress`], if the address family matches."); + + #[cfg(all( + feature = "net", + any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + target_os = "haiku", + target_os = "aix", + target_os = "openbsd", + ), + ))] + /// Converts to [`LinkAddr`], if the address family matches. + pub fn to_link(&self) -> Option<&LinkAddr> { + if self.family() != AddressFamily::LINK { + return None; + } + + unsafe { + Some(&*LinkAddr::from_raw_parts( + self.as_ptr().cast(), + self.len().unwrap(), + )) + } + } + + #[cfg(all( + feature = "net", + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + ), + ))] + raw_address_conv!(to_link, LinkAddress, sockaddr_ll, PACKET, "Converts to [`LinkAddress`], if the address family matches."); + + #[cfg(any(target_os = "android", target_os = "linux"))] + raw_address_conv!(to_netlink, NetlinkAddress, sockaddr_nl, NETLINK, "Converts to [`NetlinkAddress`], if the address family matches."); + + #[cfg(all( + feature = "ioctl", + any(target_os = "ios", target_os = "macos") + ))] + raw_address_conv!(to_sys_control, SysControlAddress, sockaddr_ctl, SYSTEM, "Converts to [`SysControlAddress`], if the address family matches."); + + /// Converts to [`UnixAddr`], if the address family matches. + #[cfg(not(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + )))] + pub fn to_unix(&self) -> Option<&UnixAddr> { + if self.family() != AddressFamily::UNIX { + return None; + } + + unsafe { + Some(&*UnixAddr::from_raw_parts( + self.as_ptr().cast(), + self.len().unwrap(), + )) + } + } + + /// Converts to [`Addr`], if its length can be derived. + pub fn to_addr(&self) -> Option<&Addr> { + self.len().map(|l| unsafe { + &*Addr::from_raw_parts(&*self.as_ptr().cast(), l) + }) + } +} + +impl<'a> fmt::Display for RawAddr<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.to_addr() { + Some(sa) => sa.fmt(f), + None => "".fmt(f), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -2389,25 +3547,27 @@ mod tests { mod link { #![allow(clippy::cast_ptr_alignment)] - #[cfg(any(apple_targets, target_os = "illumos"))] - use super::super::super::socklen_t; + #[allow(unused_imports)] use super::*; /// Don't panic when trying to display an empty datalink address #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd" ))] #[test] fn test_datalink_display() { - use super::super::LinkAddr; + use super::super::LinkAddress; use std::mem; - let la = LinkAddr(libc::sockaddr_dl { - sdl_len: 56, + let len = offset_of!(libc::sockaddr_dl, sdl_data) + 3; + + let la = LinkAddress(libc::sockaddr_dl { + sdl_len: len as _, sdl_family: 18, sdl_index: 5, sdl_type: 24, @@ -2419,68 +3579,23 @@ mod tests { format!("{la}"); } - #[cfg(all( - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux" - ), - target_endian = "little" - ))] - #[test] - fn linux_loopback() { - #[repr(align(2))] - struct Raw([u8; 20]); - - let bytes = Raw([ - 17u8, 0, 0, 0, 1, 0, 0, 0, 4, 3, 0, 6, 1, 2, 3, 4, 5, 6, 0, 0, - ]); - let sa = bytes.0.as_ptr().cast(); - let len = None; - let sock_addr = - unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap(); - assert_eq!(sock_addr.family(), Some(AddressFamily::Packet)); - match sock_addr.as_link_addr() { - Some(dl) => assert_eq!(dl.addr(), Some([1, 2, 3, 4, 5, 6])), - None => panic!("Can't unwrap sockaddr storage"), - } - } - - #[cfg(apple_targets)] - #[test] - fn macos_loopback() { - let bytes = - [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0]; - let sa = bytes.as_ptr().cast(); - let len = Some(bytes.len() as socklen_t); - let sock_addr = - unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap(); - assert_eq!(sock_addr.family(), Some(AddressFamily::Link)); - match sock_addr.as_link_addr() { - Some(dl) => { - assert!(dl.addr().is_none()); - } - None => panic!("Can't unwrap sockaddr storage"), - } - } - - #[cfg(apple_targets)] + #[cfg(any(target_os = "ios", target_os = "macos"))] #[test] fn macos_tap() { let bytes = [ 20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35, - 76, -80, + 76, -80, 0, 0, 0 ]; let ptr = bytes.as_ptr(); let sa = ptr as *const libc::sockaddr; - let len = Some(bytes.len() as socklen_t); + let len = bytes.len(); let sock_addr = - unsafe { SockaddrStorage::from_raw(sa, len).unwrap() }; - assert_eq!(sock_addr.family(), Some(AddressFamily::Link)); - match sock_addr.as_link_addr() { + unsafe { &*Addr::from_raw_parts(sa.cast(), len) }; + assert_eq!(sock_addr.family(), AddressFamily::LINK); + match sock_addr.to_link() { Some(dl) => { - assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176])) + assert_eq!(dl.addr(), Some(&[24u8, 101, 144, 221, 76, 176][..])) } None => panic!("Can't unwrap sockaddr storage"), } @@ -2492,41 +3607,58 @@ mod tests { let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176]; let ptr = bytes.as_ptr(); let sa = ptr as *const libc::sockaddr; - let len = Some(bytes.len() as socklen_t); - let _sock_addr = unsafe { SockaddrStorage::from_raw(sa, len) }; + let len = bytes.len(); + let sock_addr = unsafe { &*Addr::from_raw_parts(sa.cast(), len) }; - assert!(_sock_addr.is_some()); - - let sock_addr = _sock_addr.unwrap(); - - assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link); + assert_eq!(sock_addr.family(), AddressFamily::LINK); assert_eq!( - sock_addr.as_link_addr().unwrap().addr(), - Some([24u8, 101, 144, 221, 76, 176]) + sock_addr.to_link().unwrap().addr(), + Some(&[24u8, 101, 144, 221, 76, 176][..]) ); } + #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "illumos", + target_os = "netbsd", + // haiku doesn't support `getifaddrs` + // aix doesn't support `getifaddrs` + target_os = "openbsd", + ))] #[test] - fn size() { - #[cfg(any( - target_os = "aix", - target_os = "dragonfly", - target_os = "freebsd", - apple_targets, - target_os = "netbsd", - target_os = "illumos", - target_os = "openbsd", - target_os = "haiku" - ))] - let l = mem::size_of::(); - #[cfg(any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux" - ))] - let l = mem::size_of::(); - assert_eq!(LinkAddr::size() as usize, l); + fn test_to_owned() { + let ifaddrs = crate::ifaddrs::getifaddrs().unwrap(); + + for addr in ifaddrs.iter().map(|ifa| ifa.address).filter_map(|a| a.filter(|a| a.family() == AddressFamily::LINK)) { + let link_addr: &LinkAddr = addr.to_link().unwrap(); + + // On FreeBSD, the length gets rounded up to the next multiple + // of 8 bytes, which is 56 bytes. + // + // Source: https://github.com/freebsd/freebsd-src/blob/dcc4d2939f789a6d1f272ffeab2068ba2b7525ea/lib/libc/net/getifaddrs.c#L61 + // + // That means, that its length is larger than 54, the size of + // `libc::sockaddr_dl`. + #[cfg(target_os = "freebsd")] + assert_eq!(link_addr.len(), 56); + + #[cfg(target_os = "freebsd")] + assert_eq!(link_addr.to_owned(), None); + + let owned = link_addr.to_owned_truncate().unwrap(); + + assert!(owned.len() <= std::mem::size_of::()); + + assert_eq!(owned.len(), owned.true_len()); + + // We can dereference `owned` and get the same result when + // displaying it. + assert_eq!(format!("{}", owned), format!("{}", &*owned)); + } } } @@ -2537,24 +3669,9 @@ mod tests { #[test] fn display() { let s = "127.0.0.1:8080"; - let addr = SockaddrIn::from_str(s).unwrap(); + let addr = Ipv4Address::from_str(s).unwrap(); assert_eq!(s, format!("{addr}")); } - - #[test] - fn size() { - assert_eq!( - mem::size_of::(), - SockaddrIn::size() as usize - ); - } - - #[test] - fn ip() { - let s = "127.0.0.1:8080"; - let ip = SockaddrIn::from_str(s).unwrap().ip(); - assert_eq!("127.0.0.1", format!("{ip}")); - } } mod sockaddr_in6 { @@ -2564,30 +3681,15 @@ mod tests { #[test] fn display() { let s = "[1234:5678:90ab:cdef::1111:2222]:8080"; - let addr = SockaddrIn6::from_str(s).unwrap(); + let addr = Ipv6Address::from_str(s).unwrap(); assert_eq!(s, format!("{addr}")); } - #[test] - fn size() { - assert_eq!( - mem::size_of::(), - SockaddrIn6::size() as usize - ); - } - - #[test] - fn ip() { - let s = "[1234:5678:90ab:cdef::1111:2222]:8080"; - let ip = SockaddrIn6::from_str(s).unwrap().ip(); - assert_eq!("1234:5678:90ab:cdef::1111:2222", format!("{ip}")); - } - #[test] // Ensure that we can convert to-and-from std::net variants without change. fn to_and_from() { let s = "[1234:5678:90ab:cdef::1111:2222]:8080"; - let mut nix_sin6 = SockaddrIn6::from_str(s).unwrap(); + let mut nix_sin6 = Ipv6Address::from_str(s).unwrap(); nix_sin6.0.sin6_flowinfo = 0x12345678; nix_sin6.0.sin6_scope_id = 0x9abcdef0; @@ -2596,48 +3698,15 @@ mod tests { } } - mod sockaddr_storage { - use super::*; - - #[test] - fn from_sockaddr_un_named() { - let ua = UnixAddr::new("/var/run/mysock").unwrap(); - let ptr = ua.as_ptr().cast(); - let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } - .unwrap(); - assert_eq!(ss.len(), ua.len()); - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - #[test] - fn from_sockaddr_un_abstract_named() { - let name = String::from("nix\0abstract\0test"); - let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap(); - let ptr = ua.as_ptr().cast(); - let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } - .unwrap(); - assert_eq!(ss.len(), ua.len()); - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - #[test] - fn from_sockaddr_un_abstract_unnamed() { - let ua = UnixAddr::new_unnamed(); - let ptr = ua.as_ptr().cast(); - let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) } - .unwrap(); - assert_eq!(ss.len(), ua.len()); - } - } - mod unixaddr { + #[allow(unused_imports)] use super::*; #[cfg(any(target_os = "android", target_os = "linux"))] #[test] fn abstract_sun_path() { let name = String::from("nix\0abstract\0test"); - let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let addr = UnixAddress::new_abstract(name.as_bytes()).unwrap(); let sun_path1 = unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] }; @@ -2647,13 +3716,5 @@ mod tests { ]; assert_eq!(sun_path1, sun_path2); } - - #[test] - fn size() { - assert_eq!( - mem::size_of::(), - UnixAddr::size() as usize - ); - } } } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 76e09641c0..d162423089 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -1,5 +1,33 @@ //! Socket interface functions //! +//! # Addresses +//! +//! All socket addresses consist of three parts: +//! +//! - An [`AddressFamily`], which specifies the format of the address. +//! +//! - A length, even if isn't embedded into the libc address type. It indicates the number of bytes +//! of the address that should be read when interpreting it. +//! +//! - The address itself. +//! +//! ## `Address` vs `Addr` +//! +//! Socket addresses come in two flavors: +//! +//! - Addresses ending with `Address` are sized and safely convertible to libc's address types, regardless +//! of their length. Their address length is bounded by `[2, SIZE]` with `SIZE` being the libc address type's size. +//! The only exception is [`Address`], where the length can also be zero, indicating an empty, uninitialized address. +//! +//! - Addresses ending with `Addr` are unsized. They are generally *not* directly convertible to libc's address +//! types (they are convertible if and only if the address length matches the libc address type's size). Their address +//! length is bounded by `[2, MAX]` with `MAX` being the maximum address length, i.e. the size of [`sockaddr_storage`]. +//! Similar to above, [`Addr`] can also have a length of zero. Trying to instantiate or using an address with a length +//! greater than `MAX` can result in a panic. +//! +//! **Note**: Not all addresses have a dyn-sized variant, in fact, only a few do. That's because most addresses are fixed in +//! size and wouldn't benefit from a dyn-sized alternative. Only those with a variable length have a dyn-sized variant. +//! //! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html) #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg(feature = "uio")] @@ -12,13 +40,17 @@ 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, + CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE, }; #[cfg(not(target_os = "redox"))] use std::io::{IoSlice, IoSliceMut}; +#[allow(unused)] +use std::mem::MaybeUninit; #[cfg(feature = "net")] use std::net; use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, OwnedFd, RawFd}; +#[cfg(not(target_os = "redox"))] +use std::ptr::addr_of_mut; use std::{mem, ptr}; #[deny(missing_docs)] @@ -32,42 +64,47 @@ pub mod sockopt; * */ -pub use self::addr::{SockaddrLike, SockaddrStorage}; +pub use self::addr::{AddressFamily, InvalidAddressFamilyError, Addr, Address, UnixAddr, UnixAddress, RawAddr}; -#[cfg(any(target_os = "illumos", target_os = "solaris"))] -pub use self::addr::{AddressFamily, UnixAddr}; -#[cfg(not(any(target_os = "illumos", target_os = "solaris")))] -pub use self::addr::{AddressFamily, UnixAddr}; #[cfg(not(any( - target_os = "illumos", target_os = "solaris", - target_os = "haiku", target_os = "redox", )))] #[cfg(feature = "net")] -pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6}; +pub use self::addr::{LinkAddress, Ipv4Address, Ipv6Address}; #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", target_os = "illumos", - target_os = "solaris", + target_os = "netbsd", target_os = "haiku", + target_os = "aix", + target_os = "openbsd" +))] +#[cfg(feature = "net")] +pub use self::addr::LinkAddr; +#[cfg(any( + target_os = "solaris", target_os = "redox", ))] #[cfg(feature = "net")] -pub use self::addr::{SockaddrIn, SockaddrIn6}; +pub use self::addr::{Ipv4Address, Ipv6Address}; #[cfg(any(target_os = "android", target_os = "linux"))] -pub use crate::sys::socket::addr::alg::AlgAddr; +pub use crate::sys::socket::addr::alg::AlgAddress; #[cfg(any(target_os = "android", target_os = "linux"))] -pub use crate::sys::socket::addr::netlink::NetlinkAddr; -#[cfg(apple_targets)] +pub use crate::sys::socket::addr::netlink::NetlinkAddress; +#[cfg(any(target_os = "ios", target_os = "macos"))] #[cfg(feature = "ioctl")] -pub use crate::sys::socket::addr::sys_control::SysControlAddr; +pub use crate::sys::socket::addr::sys_control::SysControlAddress; #[cfg(any( target_os = "android", target_os = "linux", target_os = "macos" ))] -pub use crate::sys::socket::addr::vsock::VsockAddr; +pub use crate::sys::socket::addr::vsock::VsockAddress; #[cfg(all(feature = "uio", not(target_os = "redox")))] pub use libc::{cmsghdr, msghdr}; @@ -235,7 +272,7 @@ impl SockProtocol { /// ([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 + pub const KextEvent: SockProtocol = SockProtocol::Icmp; // Matches libc::SYSPROTO_EVENT } #[cfg(any(target_os = "android", target_os = "linux"))] libc_bitflags! { @@ -323,7 +360,7 @@ libc_bitflags! { MSG_DONTWAIT; /// Receive flags: Control Data was discarded (buffer too small) MSG_CTRUNC; - /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram + /// For raw (`AF_PACKET`), 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 @@ -488,7 +525,8 @@ cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - apple_targets + target_os = "macos", + target_os = "ios" ))] { /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred) #[repr(transparent)] @@ -564,75 +602,60 @@ impl Ipv6MembershipRequest { feature! { #![feature = "uio"] -/// Create a buffer large enough for storing some control messages as returned -/// by [`recvmsg`](fn.recvmsg.html). +/// Calculates the space needed for the provided arguments. /// -/// # Examples +/// The arguments are the names of the variants of [`ControlMessageOwnedSpace`]. This macro +/// is const-evaluable. +#[macro_export] +macro_rules! cmsg_space { + ($($x:ident $(($arg:expr))? ),* $(,)?) => {{ + 0usize $( + + <$crate::sys::socket::ControlMessageOwnedSpace>::$x $(($arg))?.space() + )* + }}; +} + +/// Creates a [`CmsgBuf`] with the capacity needed for **receiving** +/// the provided arguments. +/// +/// The arguments are the names of the variants of [`ControlMessageOwnedSpace`]. +/// +/// # Example /// /// ``` -/// # #[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); -/// # } +/// # use nix::{cmsg_space, cmsg_buf}; +/// let cmsg = cmsg_buf![ScmRights(2), ScmTimestamp]; +/// +/// assert_eq!(cmsg.capacity(), cmsg_space![ScmRights(2), ScmTimestamp]); /// ``` -// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a -// stack-allocated array. #[macro_export] -macro_rules! cmsg_space { - ( $( $x:ty ),* ) => { - { - let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*; - Vec::::with_capacity(space) - } - } -} +macro_rules! cmsg_buf { + ($($x:ident $(($arg:expr))? ),* $(,)?) => {{ + const SPACE: usize = $crate::cmsg_space![$($x $(($arg))? ),*]; -#[inline] -#[doc(hidden)] -pub fn cmsg_space() -> usize { - // SAFETY: CMSG_SPACE is always safe - unsafe { libc::CMSG_SPACE(mem::size_of::() as libc::c_uint) as usize } + <$crate::sys::socket::CmsgBuf>::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, -} +// FIXME (2023-11-13): the module-internal test `recvmmsg2` requires a version of the macro without +// an absolute path to `cmsg_space!`. This workaround is necessary until the macro resolution +// of the compiler isn't as horrendous as it is currently. +macro_rules! cmsg_vec_internal { + ($($x:ident $(($arg:expr))? ),* $(,)?) => {{ + const SPACE: usize = cmsg_space![$($x $(($arg))? ),*]; -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 - } - } + <$crate::sys::socket::CmsgBuf>::with_capacity(SPACE) + }}; } -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// An iterator created by [`CmsgBuf::iter`], yielding control messages of type +/// [`ControlMessageOwned`]. +#[derive(Clone, Copy, Debug)] pub struct CmsgIterator<'a> { /// Control message buffer to decode from. Must adhere to cmsg alignment. cmsghdr: Option<&'a cmsghdr>, - mhdr: &'a msghdr + // SAFETY: `msg_control` and `msg_controllen` must be initialized. + mhdr: MaybeUninit, } impl<'a> Iterator for CmsgIterator<'a> { @@ -648,7 +671,7 @@ impl<'a> Iterator for CmsgIterator<'a> { // 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 _); + let p = CMSG_NXTHDR(self.mhdr.as_ptr(), hdr as *const _); p.as_ref() }; cm @@ -660,7 +683,11 @@ impl<'a> Iterator for CmsgIterator<'a> { /// A type-safe wrapper around a single control message, as used with /// [`recvmsg`](#fn.recvmsg). /// +/// **Note**: This is *not* the owned version of [`ControlMessage`] as they don't +/// necessarily have the same variants. +/// /// [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 @@ -698,28 +725,38 @@ pub enum ControlMessageOwned { /// // Set up /// let message = "Ohayō!".as_bytes(); /// let in_socket = socket( - /// AddressFamily::Inet, + /// 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(); + /// let localhost = Ipv4Address::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(); + /// let address = 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(); + /// let l = sendmsg( + /// in_socket.as_raw_fd(), + /// &address, + /// &iov, + /// CmsgStr::empty(), + /// flags, + /// ).unwrap().bytes(); /// assert_eq!(message.len(), l); /// // Receive the message /// let mut buffer = vec![0u8; message.len()]; - /// let mut cmsgspace = cmsg_space!(TimeVal); + /// let mut cmsg = cmsg_buf![ScmTimestamp]; /// let mut iov = [IoSliceMut::new(&mut buffer)]; - /// let r = recvmsg::(in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), flags) - /// .unwrap(); - /// let rtime = match r.cmsgs().next() { + /// let _ = recvmsg( + /// in_socket.as_raw_fd(), + /// &mut iov, + /// cmsg.handle(), + /// flags, + /// ).unwrap(); + /// let rtime = match cmsg.iter().next() { /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime, /// Some(_) => panic!("Unexpected control message"), /// None => panic!("No control message") @@ -748,50 +785,48 @@ pub enum ControlMessageOwned { ScmTimestampns(TimeSpec), #[cfg(any( target_os = "android", - apple_targets, + 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", - apple_targets, + 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", - apple_targets, + 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", - apple_targets, + 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 @@ -804,7 +839,6 @@ pub enum ControlMessageOwned { /// 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 @@ -821,16 +855,14 @@ pub enum ControlMessageOwned { /// 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), /// `SOL_TLS` messages of type `TLS_GET_RECORD_TYPE` - #[cfg(any(target_os = "linux"))] + #[cfg(target_os = "linux")] TlsGetRecordType(TlsGetRecordType), /// Catch-all variant for unimplemented cmsg types. @@ -852,7 +884,7 @@ pub struct Timestamps { /// These constants correspond to TLS 1.2 message types, as defined in /// RFC 5246, Appendix A.1 -#[cfg(any(target_os = "linux"))] +#[cfg(target_os = "linux")] #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[repr(u8)] #[non_exhaustive] @@ -890,6 +922,7 @@ impl ControlMessageOwned { 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 @@ -941,8 +974,9 @@ impl ControlMessageOwned { #[cfg(any( target_os = "android", target_os = "freebsd", - apple_targets, - target_os = "linux" + target_os = "ios", + target_os = "linux", + target_os = "macos" ))] #[cfg(feature = "net")] (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => { @@ -951,8 +985,9 @@ impl ControlMessageOwned { } #[cfg(any( target_os = "android", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", ))] #[cfg(feature = "net")] @@ -962,7 +997,8 @@ impl ControlMessageOwned { } #[cfg(any( target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -973,7 +1009,8 @@ impl ControlMessageOwned { }, #[cfg(any( target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -1101,7 +1138,7 @@ pub enum ControlMessage<'a> { /// 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) + /// [`documentation`](https://docs.kernel.org/crypto/userspace-if.html) #[cfg(any( target_os = "android", target_os = "linux", @@ -1111,7 +1148,7 @@ pub enum ControlMessage<'a> { /// `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) + /// [`documentation`](https://docs.kernel.org/crypto/userspace-if.html) #[cfg(any( target_os = "android", target_os = "linux", @@ -1121,7 +1158,7 @@ pub enum ControlMessage<'a> { /// for `AF_ALG` crypto API. /// /// For further information, please refer to the - /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + /// [`documentation`](https://docs.kernel.org/crypto/userspace-if.html) #[cfg(any( target_os = "android", target_os = "linux", @@ -1138,7 +1175,6 @@ pub enum ControlMessage<'a> { /// 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. @@ -1146,11 +1182,11 @@ pub enum ControlMessage<'a> { /// 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", - apple_targets))] + 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. @@ -1158,12 +1194,12 @@ pub enum ControlMessage<'a> { /// 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", - apple_targets))] + 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`. @@ -1174,7 +1210,6 @@ pub enum ControlMessage<'a> { target_os = "dragonfly", ))] #[cfg(feature = "net")] - #[cfg_attr(docsrs, doc(cfg(feature = "net")))] Ipv4SendSrcAddr(&'a libc::in_addr), /// Configure the hop limit for v6 multicast traffic. @@ -1185,11 +1220,11 @@ pub enum ControlMessage<'a> { /// 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(target_os = "linux", target_os = "freebsd", - target_os = "dragonfly", target_os = "android", - apple_targets, target_os = "haiku"))] + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "freebsd", target_os = "dragonfly", + target_os = "android", target_os = "ios", + 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 @@ -1290,22 +1325,24 @@ impl<'a> ControlMessage<'a> { ControlMessage::UdpGsoSegments(gso_size) => { gso_size as *const _ as *const u8 }, - #[cfg(any(target_os = "linux", target_os = "netbsd", - target_os = "android", apple_targets))] + #[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 = "netbsd", - target_os = "freebsd", target_os = "android", - apple_targets))] + #[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 = "linux", target_os = "freebsd", - target_os = "dragonfly", target_os = "android", - apple_targets, target_os = "haiku"))] + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "freebsd", target_os = "dragonfly", + target_os = "android", target_os = "ios", + target_os = "haiku"))] #[cfg(feature = "net")] ControlMessage::Ipv6HopLimit(limit) => limit as *const _ as *const u8, #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] @@ -1357,22 +1394,24 @@ impl<'a> ControlMessage<'a> { ControlMessage::UdpGsoSegments(gso_size) => { mem::size_of_val(gso_size) }, - #[cfg(any(target_os = "linux", target_os = "netbsd", - target_os = "android", apple_targets))] + #[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 = "netbsd", - target_os = "freebsd", target_os = "android", - apple_targets))] + #[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 = "linux", target_os = "freebsd", - target_os = "dragonfly", target_os = "android", - apple_targets, target_os = "haiku"))] + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "freebsd", target_os = "dragonfly", + target_os = "android", target_os = "ios", + target_os = "haiku"))] #[cfg(feature = "net")] ControlMessage::Ipv6HopLimit(limit) => { mem::size_of_val(limit) @@ -1402,22 +1441,24 @@ impl<'a> ControlMessage<'a> { #[cfg(target_os = "linux")] #[cfg(feature = "net")] ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP, - #[cfg(any(target_os = "linux", target_os = "netbsd", - target_os = "android", apple_targets,))] + #[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 = "netbsd", - target_os = "freebsd", target_os = "android", - apple_targets))] + #[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 = "linux", target_os = "freebsd", - target_os = "dragonfly", target_os = "android", - apple_targets, target_os = "haiku"))] + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "freebsd", target_os = "dragonfly", + target_os = "android", target_os = "ios", + target_os = "haiku"))] #[cfg(feature = "net")] ControlMessage::Ipv6HopLimit(_) => libc::IPPROTO_IPV6, #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] @@ -1452,22 +1493,24 @@ impl<'a> ControlMessage<'a> { ControlMessage::UdpGsoSegments(_) => { libc::UDP_SEGMENT }, - #[cfg(any(target_os = "linux", target_os = "netbsd", - target_os = "android", apple_targets))] + #[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 = "netbsd", - target_os = "freebsd", target_os = "android", - apple_targets))] + #[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 = "linux", target_os = "freebsd", - target_os = "dragonfly", target_os = "android", - apple_targets, target_os = "haiku"))] + #[cfg(any(target_os = "linux", target_os = "macos", + target_os = "freebsd", target_os = "dragonfly", + target_os = "android", target_os = "ios", + target_os = "haiku"))] #[cfg(feature = "net")] ControlMessage::Ipv6HopLimit(_) => libc::IPV6_HOPLIMIT, #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] @@ -1493,433 +1536,1552 @@ impl<'a> ControlMessage<'a> { } } +/// Variants to be used with [`cmsg_space!`]. +/// +/// You shouldn't need to use this type directly. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum ControlMessageOwnedSpace { + /// See [`ControlMessageOwned::ScmRights`]. + /// + /// Argument is the number of file descriptors. + ScmRights(usize), + /// See [`ControlMessageOwned::ScmCredentials`]. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmCredentials, + /// See [`ControlMessageOwned::ScmCreds`]. + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + ScmCreds, + /// See [`ControlMessageOwned::ScmTimestamp`]. + ScmTimestamp, + /// See [`ControlMessageOwned::ScmTimestampns`]. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmTimestampsns, + /// See [`ControlMessageOwned::ScmTimestampns`]. + #[cfg(any(target_os = "android", target_os = "linux"))] + ScmTimestampns, + /// See [`ControlMessageOwned::Ipv4PacketInfo`]. + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + ))] + #[cfg(feature = "net")] + Ipv4PacketInfo, + /// See [`ControlMessageOwned::Ipv6PacketInfo`]. + #[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")] + Ipv6PacketInfo, + /// See [`ControlMessageOwned::Ipv4RecvIf`]. + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[cfg(feature = "net")] + Ipv4RecvIf, + /// See [`ControlMessageOwned::Ipv4RecvDstAddr`]. + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[cfg(feature = "net")] + Ipv4RecvDstAddr, + /// See [`ControlMessageOwned::Ipv4OrigDstAddr`]. + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] + #[cfg(feature = "net")] + Ipv4OrigDstAddr, + /// See [`ControlMessageOwned::Ipv6OrigDstAddr`]. + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] + #[cfg(feature = "net")] + Ipv6OrigDstAddr, + /// See [`ControlMessageOwned::UdpGroSegments`]. + #[cfg(target_os = "linux")] + #[cfg(feature = "net")] + UdpGroSegments, + /// See [`ControlMessageOwned::RxqOvfl`]. + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + RxqOvfl, + /// See [`ControlMessageOwned::Ipv4RecvErr`]. + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(feature = "net")] + Ipv4RecvErr, + /// See [`ControlMessageOwned::Ipv6RecvErr`]. + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(feature = "net")] + Ipv6RecvErr, + /// See [`ControlMessageOwned::TlsGetRecordType`]. + #[cfg(target_os = "linux")] + TlsGetRecordType, +} + +impl ControlMessageOwnedSpace { + const fn len(self) -> usize { + match self { + Self::ScmRights(n) => n * mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "linux"))] + Self::ScmCredentials => mem::size_of::(), + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + Self::ScmCreds => mem::size_of::(), + Self::ScmTimestamp => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "linux"))] + Self::ScmTimestampsns => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "linux"))] + Self::ScmTimestampns => mem::size_of::(), + #[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + ))] + #[cfg(feature = "net")] + Self::Ipv4PacketInfo => mem::size_of::(), + #[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")] + Self::Ipv6PacketInfo => mem::size_of::(), + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[cfg(feature = "net")] + Self::Ipv4RecvIf => mem::size_of::(), + #[cfg(any( + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", + ))] + #[cfg(feature = "net")] + Self::Ipv4RecvDstAddr => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] + #[cfg(feature = "net")] + Self::Ipv4OrigDstAddr => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] + #[cfg(feature = "net")] + Self::Ipv6OrigDstAddr => mem::size_of::(), + #[cfg(target_os = "linux")] + #[cfg(feature = "net")] + Self::UdpGroSegments => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] + Self::RxqOvfl => mem::size_of::(), + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(feature = "net")] + Self::Ipv4RecvErr => { + mem::size_of::() + mem::size_of::() + } + #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(feature = "net")] + Self::Ipv6RecvErr => { + mem::size_of::() + mem::size_of::() + } + #[cfg(target_os = "linux")] + Self::TlsGetRecordType => mem::size_of::(), + } + } + + #[doc(hidden)] + pub const fn space(self) -> usize { + // SAFETY: CMSG_SPACE has no sideeffects and is always safe. + unsafe { CMSG_SPACE(self.len() as libc::c_uint) as usize } + } +} -/// 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. +/// Sends a message through a connection-mode or connectionless-mode socket. /// -/// Allocates if cmsgs is nonempty. +/// If the socket is a connectionless-mode socket, the message will *usually* be sent +/// to the address passed in `addr`. Click [here] for more information. +/// +/// If the socket is connection-mode, `addr` will be ignored. In that case, using +/// [`Addr::empty`] for `addr` is recommended. +/// +/// Additionally to [`sendto`], it also allows to send control messages. +/// +/// [Further reading] /// /// # 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(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage], - flags: MsgFlags, addr: Option<&S>) -> Result - where S: SockaddrLike +/// +/// See [`recvmsg`] for an example using both functions. +/// +/// [here]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html +/// [Further reading]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html +pub fn sendmsg<'a, A, I>( + fd: RawFd, + addr: A, + iov: &I, + cmsgs: &CmsgStr, + flags: MsgFlags, +) -> Result +where + A: AsRef, + I: AsRef<[IoSlice<'a>]>, { - let capacity = cmsgs.iter().map(|c| c.space()).sum(); + let header = sendmsg_header(addr.as_ref(), iov.as_ref(), cmsgs); - // 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 ret = unsafe { libc::sendmsg(fd, &header, flags.bits()) }; - let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr); + let bytes = Errno::result(ret).map(|x| x as usize)?; - let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) }; - - Errno::result(ret).map(|r| r as usize) + Ok(SendMsgResult { bytes }) } - -/// 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. +/// Receives a message from a connection-mode or connectionless-mode socket. /// -/// Allocations are performed for cmsgs and to build `msghdr` buffer +/// It is normally used with connectionless-mode sockets because it permits the application to +/// retrieve the source address of received data. /// -/// # Arguments +/// Additionally to [`recvfrom`], it also allows to receive control messages. /// -/// * `fd`: Socket file descriptor -/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items -/// * `flags`: Optional flags passed directly to the operating system. +/// [Further reading] /// -/// # Returns -/// `Vec` with numbers of sent bytes on each sent message. +/// # Examples /// -/// # 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>( +/// The following example runs on Linux and Android only. +/// +#[cfg_attr(any(target_os = "linux", target_os = "android"), doc = "```")] +#[cfg_attr(not(any(target_os = "linux", target_os = "android")), doc = "```ignore")] +/// # use nix::sys::socket::*; +/// # use nix::cmsg_buf; +/// # use nix::sys::socket::sockopt::Timestamping; +/// # use std::os::fd::AsRawFd; +/// # use std::io::{IoSlice, IoSliceMut}; +/// // We use connectionless UDP sockets. +/// let send = socket( +/// AddressFamily::INET, +/// SockType::Datagram, +/// SockFlag::empty(), +/// Some(SockProtocol::Udp), +/// )?; +/// +/// let recv = socket( +/// AddressFamily::INET, +/// SockType::Datagram, +/// SockFlag::empty(), +/// Some(SockProtocol::Udp), +/// )?; +/// +/// // We enable timestamping on the receiving socket. They will be sent as +/// // control messages by the kernel. +/// setsockopt(&recv, Timestamping, &TimestampingFlag::all())?; +/// +/// // This is the address we are going to send the message. +/// let addr = "127.0.0.1:6069".parse::().unwrap(); +/// +/// bind(recv.as_raw_fd(), &addr)?; +/// +/// // The message we are trying to send: [0, 1, 2, ...]. +/// let msg: [u8; 1500] = std::array::from_fn(|i| i as u8); +/// +/// // Send `msg` on `send` without control messages. +/// // +/// // On connectionless sockets like UDP, the destination address is required. +/// // On connection-oriented sockets like TCP, `addr` would be ignored +/// // and `None` is usually passed instead. +/// let send_res = sendmsg( +/// send.as_raw_fd(), +/// &addr, +/// &[IoSlice::new(&msg)], +/// CmsgStr::empty(), +/// MsgFlags::empty(), +/// )?; +/// +/// // We have actually sent 1500 bytes. +/// assert_eq!(send_res.bytes(), 1500); +/// +/// // Initialize a buffer to receive `msg`. +/// let mut buf = [0u8; 1500]; +/// +/// // The timestamps will land here. The control message type is `ScmTimestampsns`. +/// let mut cmsg = cmsg_buf![ScmTimestampsns]; +/// +/// // Receive `msg` on `recv`. +/// let recv_res = recvmsg( +/// recv.as_raw_fd(), +/// &mut [IoSliceMut::new(&mut buf)], +/// cmsg.handle(), +/// MsgFlags::empty(), +/// )?; +/// +/// // We have actually received 1500 bytes. +/// assert_eq!(recv_res.bytes(), 1500); +/// +/// // Since this is a connectionless socket, the sender address is returned. +/// // On connection-oriented sockets like TCP, this would be `None`. +/// assert!(recv_res.address().family() == AddressFamily::INET); +/// +/// // The received message is identical to the sent one. +/// assert_eq!(buf, msg); +/// +/// // We have received a control message containing a timestamp. +/// assert!(matches!( +/// cmsg.iter().next(), +/// Some(ControlMessageOwned::ScmTimestampsns(_)), +/// )); +/// # Ok::<(), nix::Error>(()) +/// ``` +/// +/// [Further reading]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html +pub fn recvmsg( 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>]>, - C: AsRef<[ControlMessage<'a>]>, - S: SockaddrLike, -{ + iov: &mut [IoSliceMut<'_>], + mut cmsg_buffer: CmsgBufHandle<'_>, + flags: MsgFlags, +) -> Result { + let mut addr_buf = Address::default(); - let mut count = 0; + let mut header = recvmsg_header(addr_buf.as_mut_ptr(), iov, &mut cmsg_buffer); + let ret = unsafe { libc::recvmsg(fd, &mut header, flags.bits()) }; - 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 _; + let bytes = Errno::result(ret).map(|x| x as usize)?; - 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(); + if let Some(len) = cmsg_buffer.len { + *len = header.msg_controllen 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) }; + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + addr_buf.len = header.msg_namelen as _; } + } - // 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; + Ok(RecvMsgResult { bytes, hdr: header, addr_buf }) +} + +/// Primitive for encoded control messages that can be **sent**. +#[derive(Debug, PartialEq, Eq, Hash)] +#[cfg_attr(not(doc), repr(transparent))] +pub struct CmsgStr { + slice: [u8], +} + +impl CmsgStr { + /// Creates an empty [`CmsgStr`] with zero length. + pub const fn empty() -> &'static Self { + unsafe { Self::from_bytes_unchecked(&[]) } } - // 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; + /// Creates a new [`CmsgStr`] from the given bytes. + /// + /// [`write_cmsg_into`] can be used to encode an iterator of [`ControlMessage`]s + /// into a buffer. + /// + /// # Safety + /// + /// The given bytes must contain valid encoded control messages. + pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { + unsafe { &*(bytes as *const [u8] as *const Self) } + } + + /// Returns a raw pointer to the buffer. + pub const fn as_ptr(&self) -> *const u8 { + self.slice.as_ptr() + } - Ok(MultiResults { - rmm: data, - current_index: 0, - received: sent - }) + /// Returns the length of the buffer. + pub const fn len(&self) -> usize { + self.slice.len() + } + /// Returns whether the buffer is empty. + pub const fn is_empty(&self) -> bool { + self.slice.len() == 0 + } } +impl Default for &CmsgStr { + fn default() -> Self { + CmsgStr::empty() + } +} -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", -))] +/// Mutable handle to an control message buffer to be used with [`recvmsg`]. +/// +/// The handle contains a mutable reference to the (uninitialized) buffer and a +/// mutable reference to the length of the buffer, which is used to update the +/// length of valid control messages. #[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 - _cmsg_buffers: Option>, - msg_controllen: usize, +pub struct CmsgBufHandle<'a> { + buf: &'a mut [MaybeUninit], + // Invariant: `len` is always `Some` if `buf` is not empty. + len: Option<&'a mut 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 +impl<'a> CmsgBufHandle<'a> { + /// An empty handle. /// - /// `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 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::>(); + /// Use this if no control messages are needed. + pub fn empty() -> Self { + Self { buf: &mut [], len: None } + } - Self { - items: items.into_boxed_slice(), - addresses, - _cmsg_buffers: cmsg_buffers, - msg_controllen, - } + /// Creates a new handle for the given buffer and length. + /// + /// # Safety + /// + /// Normally casting a `&mut [u8]` to `&mut [MaybeUninit]` would be unsound, + /// as that allows us to write uninitialised bytes to the buffer. However this + /// implementation promises to not write uninitialised bytes to the buffer and + /// passes it directly to the system call. This promise ensures that this + /// function can be called using a buffer of type `&mut [u8]`. + + // Safety doc based on https://docs.rs/socket2/latest/socket2/struct.Socket.html#safety. + pub fn new(buf: &'a mut [MaybeUninit], len: &'a mut usize) -> Self { + Self { buf, len: Some(len) } + } + + fn capacity(&self) -> usize { + self.buf.len() + } + + fn as_mut_ptr(&mut self) -> *mut u8 { + self.buf.as_mut_ptr().cast() } } -/// 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]. +impl Default for CmsgBufHandle<'_> { + fn default() -> Self { + Self::empty() + } +} + +/// Writes the given control messages into the given buffer. /// -/// # 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. +/// Buffers are zero-initialized before writing. If the buffer is already zero-initialized, +/// consider using [`write_cmsg_into_unchecked`] instead. /// -/// 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. +/// Returns the number of bytes written into the buffer. If the buffer was too small, +/// the first control message that didn't fit in is returned additionally. +pub fn write_cmsg_into<'a, I>( + buf: &mut [u8], + cmsg: I, +) -> std::result::Result)> +where + I: IntoIterator>, +{ + buf.iter_mut().for_each(|b| *b = 0); -// 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 + // SAFETY: `buf` has been zero-initialized. + unsafe { + write_cmsg_into_unchecked(buf, cmsg) + } +} + +/// Writes the given control messages into the given buffer. +/// +/// Returns the number of bytes written into the buffer. If the buffer was too small, +/// the first control message that didn't fit in is returned additionally. +/// +/// # Safety +/// +/// `buf` must be zero-initialized before calling this function. +pub unsafe fn write_cmsg_into_unchecked<'a, I>( + buf: &mut [u8], + cmsg: I, +) -> std::result::Result)> +where + I: IntoIterator>, +{ + let mut mhdr = cmsg_dummy_mhdr(buf.as_mut_ptr(), buf.len()); + + // SAFETY: call to extern function without sideeffects. We need to start from a mutable + // reference before casting it to a `*const` as we want to use the resulting pointer mutably. + let mut cmsg_ptr = unsafe { CMSG_FIRSTHDR(mhdr.as_mut_ptr().cast_const()) }; + + let mut written = 0; + + let mut cmsg = cmsg.into_iter(); + + for c in cmsg.by_ref() { + if cmsg_ptr.is_null() || c.space() > buf.len() - written { + return Err((written, c)); + } + + written += c.space(); + + // SAFETY: we checked that there is enough space in `buf`. + // Additionally, relies on `CMSG_FIRSTHDR` and `CMSG_NXTHDR` for safety. + // `CMSG_FIRSTHDR` and `CMSG_NXTHDR` shouldn't care about the other + // uninitialized fields of `mhdr`. + // + // See https://man7.org/linux/man-pages/man3/cmsg.3.html. + unsafe { + c.encode_into(cmsg_ptr.cast()); + } + + // SAFETY: call to extern function without sideeffects. We need to start from a mutable + // reference before casting it to a `*const` as we want to use the resulting pointer mutably. + cmsg_ptr = unsafe { CMSG_NXTHDR(mhdr.as_mut_ptr().cast_const(), cmsg_ptr) }; + } + + Ok(written) +} + +// FIXME: make `const` once possible in stable rust. Last checked: 1.73.0. +fn cmsg_dummy_mhdr(buf: *mut u8, len: usize) -> MaybeUninit { + let mut mhdr = MaybeUninit::::zeroed(); + + // SAFETY: using `ptr::write` to not drop the old uninitialized value and `addr_of_mut` + // to not create references of `libc::msghdr` along the way. + unsafe { + addr_of_mut!((*mhdr.as_mut_ptr()).msg_control).write(buf.cast()); + addr_of_mut!((*mhdr.as_mut_ptr()).msg_controllen).write(len as _); + } + + mhdr +} + +/// Returns the exact number of bytes required to hold the given control messages. +pub fn cmsg_space_iter<'a, I>(cmsg: I) -> usize +where + I: IntoIterator>, +{ + cmsg.into_iter().map(|c| c.space()).sum() +} + +/// Non-extendable heap-allocated container for holding control messages +/// that can be **sent**. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct CmsgVec { + inner: Vec, +} + +impl CmsgVec { + /// Returns an empty [`CmsgVec`]. + /// + /// No allocations are performed. Use this if no control messages are needed. + pub const fn empty() -> Self { + Self { inner: Vec::new() } + } + + /// Returns an empty [`CmsgVec`] with the given capacity. + pub fn with_capacity(cap: usize) -> Self { + Self { inner: Vec::with_capacity(cap) } + } + + /// Allocates a buffer that contains the given control messages. + /// + /// The `size` parameter determines the size of the allocation in bytes. + /// [`cmsg_space_iter`] can be used to calculate the exact number of bytes required to + /// hold the control messages. + /// + /// If `size` is too small, the first control message that didn't fit in is returned additionally. + pub fn from_iter<'a, I>( + cmsg: I, + size: usize, + ) -> std::result::Result)> + where + I: IntoIterator>, + { + let mut cmsg_buf = vec![0; size]; + + // SAFETY: `cmsg_buf` is zero-initialized. + match unsafe { write_cmsg_into_unchecked(&mut cmsg_buf, cmsg) } { + Ok(written) => { + cmsg_buf.truncate(written); + + Ok(Self { + inner: cmsg_buf, + }) + } + Err((written, i)) => { + cmsg_buf.truncate(written); + + Err(( + Self { + inner: cmsg_buf, + }, + i, + )) + } + } + } + + /// Allocates a buffer that contains the given control messages. + /// + /// This is a shorthand for calling [`cmsg_space_iter`] with the cloned iterator, + /// followed by [`Self::from_iter`]. + pub fn from_iter_clone<'a, I>(cmsg: I) -> Self + where + I: IntoIterator>, + I::IntoIter: Clone, + { + let cmsg = cmsg.into_iter(); + + let len = cmsg_space_iter(cmsg.clone()); + + Self::from_iter(cmsg, len).unwrap() + } + + /// Writes the given control messages into the buffer, replacing the previous contents. + /// + /// The `size` parameter determines the minimum size of the allocation in bytes, but the internal + /// storage might allocate more. [`cmsg_space_iter`] can be used to calculate the exact number of + /// bytes required to hold the control messages. + /// + /// If `size` is too small, the first control message that didn't fit in is returned additionally. + /// + /// This function does not allocate if `size` is smaller than the current capacity. + pub fn write_iter<'a, I>( + &mut self, + cmsg: I, + size: usize, + ) -> std::result::Result<(), ControlMessage<'a>> + where + I: IntoIterator>, + { + self.inner.clear(); + self.inner.reserve(size); + + (0..size).for_each(|_| self.inner.push(0)); + + match unsafe { write_cmsg_into_unchecked(&mut self.inner, cmsg) } { + Ok(written) => { + self.inner.truncate(written); + + Ok(()) + } + Err((written, i)) => { + self.inner.truncate(written); + + Err(i) + } + } + } + + /// Writes the given control messages into the buffer, replacing the previous contents. + /// + /// This is a shorthand for calling [`cmsg_space_iter`] with the cloned iterator, + /// followed by [`Self::write_iter`]. + /// + /// This function does not allocate if the calculated size is smaller than the current capacity. + pub fn write_iter_clone<'a, I>(&mut self, cmsg: I) + where + I: IntoIterator>, + I::IntoIter: Clone, + { + let cmsg = cmsg.into_iter(); + + let len = cmsg_space_iter(cmsg.clone()); + + self.write_iter(cmsg, len).unwrap(); + } + + /// Writes the given control messages into the buffer, replacing the previous contents. + /// + /// This function does not allocate. If the current allocation can't hold all control messages, + /// the first control message that didn't fit in is returned additionally. + pub fn write_iter_in_place<'a, I>(&mut self, cmsg: I) -> std::result::Result<(), ControlMessage<'a>> + where + I: IntoIterator>, + { + self.write_iter(cmsg, self.inner.capacity()) + } + + /// Returns the length of the buffer. + /// + /// This is number of bytes that contain valid control messages. + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Returns true if the buffer contains no control messages. + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + /// Returns the capacity of the buffer. + /// + /// This is the number of bytes that can be written to the buffer. + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + /// Clears the buffer, removing all control messages. + pub fn clear(&mut self) { + self.inner.clear(); + } + + /// Reserves extra capacity for the buffer. + /// + /// The buffer will be able to hold at least `additional` more bytes + /// than its current length. + /// If there is already sufficient space, nothing happens. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX`. + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional); + } + + /// Shrinks the capacity of the buffer to at least the maximum of its length + /// and the given minimum capacity. + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity); + } + + /// Shrinks the capacity of the buffer as close as possible to its length. + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit(); + } +} + +impl std::ops::Deref for CmsgVec { + type Target = CmsgStr; + + fn deref(&self) -> &Self::Target { + // SAFETY: `CmsgVecWrite` is a newtype wrapper around `Vec`. + // `self.inner[..]` is guaranteed to be a valid `CmsgStr`. + unsafe { CmsgStr::from_bytes_unchecked(&self.inner) } + } +} + +/// Heap-allocated buffer for **receiving** control messages. +/// +/// # Cloning +/// +/// In the current implementation, when cloning the buffer, the capacity +/// is cloned as well, meaning that the cloned allocation will have the same +/// size as the original one. **This could change in future versions of nix**, +/// but not without increasing its major version. +#[derive(Debug, Clone)] +pub struct CmsgBuf { + inner: Vec>, + len: usize, +} + +impl CmsgBuf { + /// Returns an empty [`CmsgBuf`]. + /// + /// This function doesn't allocate. + pub const fn empty() -> Self { + Self { inner: Vec::new(), len: 0 } + } + + /// Returns an empty [`CmsgBuf`] with the given capacity. + pub fn with_capacity(cap: usize) -> Self { + let mut inner = Vec::with_capacity(cap); + + // SAFETY: `MaybeUninit` doesn't require initialization, and the length matches + // the capacity. + unsafe { + inner.set_len(cap); + } + + Self { inner, len: 0 } + } + + /// Returns the capacity of the buffer. + pub fn capacity(&self) -> usize { + debug_assert_eq!(self.inner.len(), self.inner.capacity()); + + self.inner.capacity() + } + + /// Returns the length of the buffer with valid control messages. + pub fn len(&self) -> usize { + self.len + } + + /// Returns true if the buffer contains no control messages. + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns a mutable handle to the buffer to be used with [`recvmsg`]. + pub fn handle(&mut self) -> CmsgBufHandle<'_> { + CmsgBufHandle::new(&mut self.inner, &mut self.len) + } + + /// Returns an iterator over the control messages in the buffer. + pub fn iter(&self) -> CmsgIterator<'_> { + if self.len == 0 { + let mhdr = cmsg_dummy_mhdr(ptr::null_mut(), 0); + + return CmsgIterator { cmsghdr: None, mhdr }; + } + + let mhdr = cmsg_dummy_mhdr(self.inner.as_ptr().cast_mut().cast(), self.len); + + CmsgIterator { + cmsghdr: unsafe { CMSG_FIRSTHDR(mhdr.as_ptr()).as_ref() }, + mhdr, + } + } +} + +impl Default for CmsgBuf { + fn default() -> Self { + Self::empty() + } +} + +fn sendmsg_header( + addr: &Addr, + iov: &[IoSlice<'_>], + cmsg: &CmsgStr, +) -> libc::msghdr { + let (addr_ptr, addr_len) = if addr.len() == 0 { + (ptr::null(), 0) + } else { + (addr.as_ptr(), addr.len()) + }; + + let (iov_ptr, iov_len) = (iov.as_ptr(), iov.len()); + let (cmsg_ptr, cmsg_len) = if cmsg.is_empty() { + (ptr::null(), 0) + } else { + (cmsg.as_ptr(), cmsg.len()) + }; + + let mut msg_hdr = MaybeUninit::::zeroed(); + let msg_hdr_ptr = msg_hdr.as_mut_ptr(); + + unsafe { + addr_of_mut!((*msg_hdr_ptr).msg_name).write(addr_ptr.cast_mut().cast()); + addr_of_mut!((*msg_hdr_ptr).msg_namelen).write(addr_len as _); + addr_of_mut!((*msg_hdr_ptr).msg_iov).write(iov_ptr.cast_mut().cast()); + addr_of_mut!((*msg_hdr_ptr).msg_iovlen).write(iov_len as _); + addr_of_mut!((*msg_hdr_ptr).msg_control).write(cmsg_ptr.cast_mut().cast()); + addr_of_mut!((*msg_hdr_ptr).msg_controllen).write(cmsg_len as _); + } + + unsafe { msg_hdr.assume_init() } +} + +fn recvmsg_header( + addr: *mut libc::sockaddr_storage, + iov: &mut [IoSliceMut<'_>], + cmsg: &mut CmsgBufHandle<'_>, +) -> libc::msghdr { + let (iov_ptr, iov_len) = (iov.as_mut().as_mut_ptr(), iov.as_mut().len()); + let (cmsg_ptr, cmsg_len) = if cmsg.capacity() == 0 { + (ptr::null_mut(), 0) + } else { + (cmsg.as_mut_ptr(), cmsg.capacity()) + }; + + let addr_size = mem::size_of::(); + + let (addr_ptr, addr_len) = if addr_size == 0 { + (ptr::null_mut(), 0) + } else { + (addr.cast(), addr_size) + }; + + let mut msg_hdr = MaybeUninit::::zeroed(); + let msg_hdr_ptr = msg_hdr.as_mut_ptr(); + + unsafe { + addr_of_mut!((*msg_hdr_ptr).msg_name).write(addr_ptr); + addr_of_mut!((*msg_hdr_ptr).msg_namelen).write(addr_len as _); + addr_of_mut!((*msg_hdr_ptr).msg_iov).write(iov_ptr.cast()); + addr_of_mut!((*msg_hdr_ptr).msg_iovlen).write(iov_len as _); + addr_of_mut!((*msg_hdr_ptr).msg_control).write(cmsg_ptr.cast()); + addr_of_mut!((*msg_hdr_ptr).msg_controllen).write(cmsg_len as _); + } + + unsafe { msg_hdr.assume_init() } +} + +/// Growable container holding the headers for [`sendmmsg`]. +/// +/// This allocation can be reused when calling [`sendmmsg`] multiple times, +/// which can be beneficial for performance. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug, Clone, Default)] +pub struct SendMmsgHeaders { + mmsghdrs: Vec, + sent: usize, +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +impl SendMmsgHeaders { + /// Creates a new container for the mmsg-headers. + /// + /// No allocations are performed. + pub const fn new() -> Self { + Self { + mmsghdrs: Vec::new(), + sent: 0, + } + } + + /// Creates a new container and reserves space for `cap` mmsg-headers. + pub fn with_capacity(cap: usize) -> Self { + Self { + mmsghdrs: Vec::with_capacity(cap), + sent: 0, + } + } + + /// Returns an iterator over [`SendMsgResult`], which contains the + /// metadata of the sent messages. + pub fn iter(&self) -> SendMmsgIter<'_> { + SendMmsgIter { hdrs: self.mmsghdrs[..self.sent].iter() } + } + + fn fill_send<'a, I>(&mut self, mut items: I) + where + I: Iterator], &'a CmsgStr)> + ExactSizeIterator, + { + // For panic-safety + self.sent = 0; + + let len = items.len(); + + self.mmsghdrs.clear(); + self.mmsghdrs.reserve(len); + + let mut total = 0; + + for (i, (addr, iov, cmsg)) in items.by_ref().take(len).enumerate() { + let mmsg_hdr = libc::mmsghdr { + msg_hdr: sendmsg_header(addr, iov, cmsg), + msg_len: 0, + }; + + self.mmsghdrs.push(mmsg_hdr); + + total = i + 1; + } + + + if total != len || items.next().is_some() { + panic!("Len returned by exact size iterator was not accurate"); + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Send for SendMmsgHeaders {} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Sync for SendMmsgHeaders {} + +/// An iterator returned by [`SendMmsgHeaders::iter`]. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug, Clone)] +pub struct SendMmsgIter<'a> { + hdrs: std::slice::Iter<'a, libc::mmsghdr>, +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +impl<'a> Iterator for SendMmsgIter<'a> { + type Item = SendMsgResult; + + fn next(&mut self) -> Option { + self.hdrs.next().map(|hdr| SendMsgResult { bytes: hdr.msg_len as _ }) + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Send for SendMmsgIter<'_> {} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Sync for SendMmsgIter<'_> {} + +/// Growable container holding the headers for [`recvmmsg`]. +/// +/// This allocation can be reused when calling [`recvmmsg`] multiple times, +/// which can be beneficial for performance. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug, Default)] +pub struct RecvMmsgHeaders { + mmsghdrs: Vec, + addresses: Vec
, + cmsg_len_ptrs: Vec<*mut usize>, + recv: usize, +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +impl RecvMmsgHeaders { + /// Creates a new container for the mmsg-headers. + /// + /// No allocations are performed. + pub const fn new() -> Self { + Self { + mmsghdrs: Vec::new(), + addresses: Vec::new(), + cmsg_len_ptrs: Vec::new(), + recv: 0, + } + } + + /// Creates a new container and reserves space for `cap` mmsg-headers. + pub fn with_capacity(cap: usize) -> Self { + Self { + mmsghdrs: Vec::with_capacity(cap), + addresses: Vec::with_capacity(cap), + cmsg_len_ptrs: Vec::with_capacity(cap), + recv: 0, + } + } + + /// Returns an iterator over [`RecvMsgResult`], which contains the + /// metadata of the received messages. + pub fn iter(&self) -> RecvMmsgIter<'_> { + RecvMmsgIter { + hdrs: self.mmsghdrs[..self.recv].iter(), + addr: self.addresses[..self.recv].iter(), + } + } + + fn fill_recv<'a, 'b, I>(&mut self, mut items: I) + where + 'b: 'a, + I: Iterator], CmsgBufHandle<'a>)> + ExactSizeIterator, + { + // For panic-safety + self.recv = 0; + + let len = items.len(); + + self.mmsghdrs.clear(); + self.mmsghdrs.reserve(len); + + self.addresses.clear(); + self.addresses.reserve(len); + + self.cmsg_len_ptrs.clear(); + self.cmsg_len_ptrs.reserve(len); + + for _ in 0..len { + // FIXME: maybe mem-setting the address buffers to zero is faster? + self.addresses.push(Address::default()); + } + + let mut addresses = self.addresses.iter_mut().map(Address::as_mut_ptr); + + let mut total = 0; + + for (i, (iov, mut cmsg)) in items.by_ref().take(len).enumerate() { + let mmsg_hdr = libc::mmsghdr { + msg_hdr: recvmsg_header(addresses.next().unwrap(), iov, &mut cmsg), + msg_len: 0, + }; + + self.mmsghdrs.push(mmsg_hdr); + + self.cmsg_len_ptrs.push(cmsg.len.map_or(ptr::null_mut(), |l| l as *mut _)); + + total = i + 1; + } + + if total != len || items.next().is_some() { + panic!("Len returned by exact size iterator was not accurate"); + } + } + + unsafe fn update_lens(&mut self) { + for ((mhdr, len), _addr) in self + .mmsghdrs + .iter() + .zip(self.cmsg_len_ptrs.iter_mut()) + .zip(self.addresses.iter_mut()) + .take(self.recv) + { + unsafe { + if let Some(len) = len.as_mut() { + *len = mhdr.msg_hdr.msg_controllen as _; + } + } + + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + _addr.len = mhdr.msg_hdr.msg_namelen as _; + } + } + } + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Send for RecvMmsgHeaders {} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Sync for RecvMmsgHeaders {} + +/// An iterator returned by [`RecvMmsgHeaders::iter`]. +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +#[derive(Debug, Clone)] +pub struct RecvMmsgIter<'a> { + addr: std::slice::Iter<'a, Address>, + hdrs: std::slice::Iter<'a, libc::mmsghdr>, +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +impl Iterator for RecvMmsgIter<'_> { + type Item = RecvMsgResult; + + fn next(&mut self) -> Option { + let hdr = *self.hdrs.next()?; + let bytes = hdr.msg_len as _; + let addr_buf = *self.addr.next().unwrap(); + + Some(RecvMsgResult { bytes, hdr: hdr.msg_hdr, addr_buf }) + } + fn size_hint(&self) -> (usize, Option) { + self.hdrs.size_hint() + } +} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Send for RecvMmsgIter<'_> {} + +#[cfg(any( + target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "netbsd", +))] +unsafe impl Sync for RecvMmsgIter<'_> {} + +/// 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. +/// +/// Returns an iterator producing [`SendMsgResult`], one per sent message. +/// +/// # Panics +/// +/// This function panics if: +/// +/// - The length of the [`ExactSizeIterator`] is not accurate. +/// - The number of messages exceeds `u32::MAX` (not applicable for FreeBSD). +/// +/// # Bugs (in underlying implementation, at least in Linux) +/// +/// If an error occurs after at least one message has been sent, the +/// call succeeds, and returns the number of messages sent. The +/// error code is lost. The caller can retry the transmission, +/// starting at the first failed message, but there is no guarantee +/// that, if an error is returned, it will be the same as the one +/// that was lost on the previous call. +/// +/// # Examples +/// +/// See [`recvmmsg`] for an example using both functions. #[cfg(any( target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", ))] -pub fn recvmmsg<'a, XS, S, I>( +pub fn sendmmsg<'a, J, A, I>( fd: RawFd, - data: &'a mut MultiHeaders, - slices: XS, + headers: &mut SendMmsgHeaders, + items: J, flags: MsgFlags, - mut timeout: Option, -) -> crate::Result> +) -> crate::Result where - XS: IntoIterator, - I: AsMut<[IoSliceMut<'a>]>, + J: IntoIterator, + J::IntoIter: ExactSizeIterator, + A: AsRef + ?Sized + 'a, + I: AsRef<[IoSlice<'a>]> + ?Sized + 'a, { - let mut count = 0; - for (i, (mut slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() { - let p = &mut mmsghdr.msg_hdr; - p.msg_iov = slice.as_mut().as_mut_ptr().cast(); - p.msg_iovlen = slice.as_mut().len() as _; + headers.fill_send(items.into_iter().map(|(addr, iov, cmsg)| (addr.as_ref(), iov.as_ref(), cmsg))); - // 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; - } + #[cfg(not(target_os = "freebsd"))] + let mmsghdrs_len = headers.mmsghdrs.len().try_into().unwrap(); - let timeout_ptr = timeout - .as_mut() - .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec); + #[cfg(target_os = "freebsd")] + let mmsghdrs_len = headers.mmsghdrs.len() as _; - // 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 received safely (i.e. `count` is the minimum of the sizes of `slices` and `data.items`) - let received = Errno::result(unsafe { - libc::recvmmsg( + let sent = Errno::result(unsafe { + libc::sendmmsg( fd, - data.items.as_mut_ptr(), - count as _, + headers.mmsghdrs.as_mut_ptr(), + mmsghdrs_len, flags.bits() as _, - timeout_ptr, ) })? as usize; - Ok(MultiResults { - rmm: data, - current_index: 0, - received, - }) -} + headers.sent = sent; -/// Iterator over results of [`recvmmsg`]/[`sendmmsg`] -#[cfg(any( - target_os = "linux", - target_os = "android", - target_os = "freebsd", - target_os = "netbsd", -))] -#[derive(Debug)] -pub struct MultiResults<'a, S> { - // preallocated structures - rmm: &'a MultiHeaders, - current_index: usize, - received: usize, + Ok(sent) } +/// 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. A further extension over [`recvmsg`] is +/// support for a timeout on the receive operation. +/// +/// Returns the number of messages received. The metadata, such as the sender address and length +/// of the messages, can be accessed by calling [`RecvMmsgHeaders::iter`]. +/// +/// # Panics +/// +/// This function panics if: +/// +/// - The length of the [`ExactSizeIterator`] is not accurate. +/// - The number of messages exceeds `u32::MAX` (not applicable for FreeBSD). +/// +/// # 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 not all +/// 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. +/// +/// # Examples +/// +/// ``` +/// # use nix::sys::socket::*; +/// # use std::os::fd::AsRawFd; +/// # use std::io::{IoSlice, IoSliceMut}; +/// // We use connectionless UDP sockets. +/// let send = socket( +/// AddressFamily::INET, +/// SockType::Datagram, +/// SockFlag::empty(), +/// Some(SockProtocol::Udp), +/// )?; +/// +/// let recv = socket( +/// AddressFamily::INET, +/// SockType::Datagram, +/// SockFlag::empty(), +/// Some(SockProtocol::Udp), +/// )?; +/// +/// // This is the address we are going to send the message. +/// let addr = "127.0.0.1:6069".parse::().unwrap(); +/// +/// bind(recv.as_raw_fd(), &addr)?; +/// +/// // The two messages we are trying to send: [0, 1, 2, ...] and [0, 2, 4, ...]. +/// let msg_1: [u8; 1500] = std::array::from_fn(|i| i as u8); +/// let send_iov_1 = [IoSlice::new(&msg_1)]; +/// +/// let msg_2: [u8; 1500] = std::array::from_fn(|i| (i * 2) as u8); +/// let send_iov_2 = [IoSlice::new(&msg_2)]; +/// +/// // We preallocate headers for 2 messages. +/// let mut send_headers = SendMmsgHeaders::with_capacity(2); +/// +/// // Zip everything together. +/// // +/// // On connectionless sockets like UDP, destination addresses are required. +/// // Each message can be sent to a different address. +/// let send_items = [ +/// (&addr, &send_iov_1, CmsgStr::empty()), +/// (&addr, &send_iov_2, CmsgStr::empty()), +/// ]; +/// +/// // Send the messages on the send socket. +/// let mut sent = sendmmsg( +/// send.as_raw_fd(), +/// &mut send_headers, +/// send_items, +/// MsgFlags::empty(), +/// )?; +/// +/// // We have actually sent 2 messages. +/// assert_eq!(sent, 2); +/// +/// // We have actually sent 1500 bytes per message. +/// assert!(send_headers.iter().all(|res| res.bytes() == 1500)); +/// +/// // Initialize buffers to receive the messages. +/// let mut buf_1 = [0u8; 1500]; +/// let mut recv_iov_1 = [IoSliceMut::new(&mut buf_1)]; +/// +/// let mut buf_2 = [0u8; 1500]; +/// let mut recv_iov_2 = [IoSliceMut::new(&mut buf_2)]; +/// +/// // We preallocate headers for 2 messages. +/// let mut recv_headers = RecvMmsgHeaders::new(); +/// +/// // Zip everything together. +/// let mut recv_items = [ +/// (&mut recv_iov_1, Default::default()), +/// (&mut recv_iov_2, Default::default()), +/// ]; +/// +/// // Receive `msg` on `recv`. +/// let mut recv = recvmmsg( +/// recv.as_raw_fd(), +/// &mut recv_headers, +/// recv_items, +/// MsgFlags::empty(), +/// None, +/// )?; +/// +/// // We have actually received two messages. +/// assert_eq!(recv, 2); +/// +/// // We have actually received 1500 bytes per message. +/// // Since this is a connectionless socket, the sender address is returned as well. +/// assert!(recv_headers.iter().all(|res| { +/// res.bytes() == 1500 && res.address().family() == AddressFamily::INET +/// })); +/// +/// // The received messages are identical to the sent ones. +/// assert_eq!(buf_1, msg_1); +/// assert_eq!(buf_2, msg_2); +/// +/// # Ok::<(), nix::Error>(()) +/// ``` #[cfg(any( target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "netbsd", ))] -impl<'a, S> Iterator for MultiResults<'a, S> +pub fn recvmmsg<'a, 'b, J, I>( + fd: RawFd, + headers: &mut RecvMmsgHeaders, + items: J, + flags: MsgFlags, + mut timeout: Option, +) -> crate::Result where - S: Copy + SockaddrLike, + J: IntoIterator)>, + J::IntoIter: ExactSizeIterator, + I: AsMut<[IoSliceMut<'b>]> + ?Sized + 'a, { - type Item = RecvMsg<'a, 'a, S>; + headers.fill_recv(items.into_iter().map(|(iov, cmsg)| (iov.as_mut(), cmsg))); - // 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, - ) - }) + let timeout_ptr = timeout + .as_mut() + .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec); + + #[cfg(not(target_os = "freebsd"))] + let mmsghdrs_len = headers.mmsghdrs.len().try_into().unwrap(); + + #[cfg(target_os = "freebsd")] + let mmsghdrs_len = headers.mmsghdrs.len() as _; + + let recv = Errno::result(unsafe { + libc::recvmmsg( + fd, + headers.mmsghdrs.as_mut_ptr(), + mmsghdrs_len, + flags.bits() as _, + timeout_ptr, + ) + })? as usize; + + headers.recv = recv; + + unsafe { + headers.update_lens(); } + + Ok(recv) } -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 _) - }, - } +/// Contains the metadata for the sent message. +#[derive(Debug, Clone, Copy)] +pub struct SendMsgResult { + bytes: usize, +} + +impl SendMsgResult { + /// Returns the number of bytes sent. + pub fn bytes(&self) -> usize { + self.bytes } } -#[derive(Debug)] -pub struct IoSliceIterator<'a> { - index: usize, - remaining: usize, - slices: &'a [IoSlice<'a>], +unsafe impl Send for SendMsgResult {} + +unsafe impl Sync for SendMsgResult {} + +/// Contains the metadata for the received message. +#[derive(Debug, Clone, Copy)] +pub struct RecvMsgResult { + bytes: usize, + hdr: libc::msghdr, + addr_buf: Address, } -impl<'a> Iterator for IoSliceIterator<'a> { - type Item = &'a [u8]; +impl RecvMsgResult { + /// Returns the number of bytes received. + pub fn bytes(&self) -> usize { + self.bytes + } - 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; - } + /// Returns the address of the sender. + /// + /// **Note**: This method should only be called if the socket is connectionless. + /// When using connection-mode sockets (like TCP), the address of the peer is already known, + /// thus the kernel will not return it. + pub fn address(&self) -> Address { + self.addr_buf + } - Some(slice) + /// Returns the received flags of the message from the kernel. + pub fn flags(&self) -> MsgFlags { + MsgFlags::from_bits_truncate(self.hdr.msg_flags as _) } } +unsafe impl Send for RecvMsgResult {} + +unsafe impl Sync for RecvMsgResult {} + // test contains both recvmmsg and timestaping which is linux only // there are existing tests for recvmmsg only in tests/ #[cfg(target_os = "linux")] #[cfg(test)] mod test { - use crate::sys::socket::{AddressFamily, ControlMessageOwned}; use crate::*; use std::str::FromStr; - use std::os::unix::io::AsRawFd; #[cfg_attr(qemu, ignore)] #[test] - fn test_recvmm2() -> crate::Result<()> { - use crate::sys::socket::{ - sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType, - SockaddrIn, TimestampingFlag, - }; + fn test_recvmm_2() -> crate::Result<()> { + use crate::sys::socket::sockopt::Timestamping; + use crate::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap(); + let sock_addr = Ipv4Address::from_str("127.0.0.1:6791").unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, )?; let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::SOCK_NONBLOCK, None, )?; - crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?; + crate::sys::socket::bind(rsock.as_raw_fd(), sock_addr)?; setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?; @@ -1942,20 +3104,27 @@ mod test { let flags = MsgFlags::empty(); let iov1 = [IoSlice::new(&sbuf)]; - let cmsg = cmsg_space!(crate::sys::socket::Timestamps); - sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); + sendmsg(ssock.as_raw_fd(), sock_addr, &iov1, Default::default(), flags).unwrap(); + + let mut headers = super::RecvMmsgHeaders::with_capacity(recv_iovs.len()); - let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg)); + let mut cmsgs = Vec::with_capacity(recv_iovs.len()); + + for _ in 0..recv_iovs.len() { + cmsgs.push(cmsg_vec_internal![ScmTimestampsns]); + } let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10)); - let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter_mut(), flags, Some(t))?; + let items = recv_iovs.iter_mut().zip(cmsgs.iter_mut().map(CmsgBuf::handle)); - for rmsg in recv { + let _ = super::recvmmsg(rsock.as_raw_fd(), &mut headers, items, flags, Some(t))?; + + for (i, rmsg) in headers.iter().enumerate() { #[cfg(not(any(qemu, target_arch = "aarch64")))] let mut saw_time = false; - let mut recvd = 0; - for cmsg in rmsg.cmsgs() { + + for cmsg in cmsgs[i].iter() { if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg { let ts = timestamps.system; @@ -1977,179 +3146,12 @@ mod test { #[cfg(not(any(qemu, target_arch = "aarch64")))] assert!(saw_time); - for iov in rmsg.iovs() { - recvd += iov.len(); - } - assert_eq!(recvd, 400); + assert_eq!(rmsg.bytes(), 400); } Ok(()) } } -unsafe fn read_mhdr<'a, 'i, S>( - mhdr: msghdr, - r: isize, - msg_controllen: usize, - mut address: S, -) -> RecvMsg<'a, 'i, S> - where S: SockaddrLike -{ - // The cast is not unnecessary on all platforms. - #[allow(clippy::unnecessary_cast)] - let cmsghdr = { - let ptr = if mhdr.msg_controllen > 0 { - debug_assert!(!mhdr.msg_control.is_null()); - debug_assert!(msg_controllen >= mhdr.msg_controllen as usize); - unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) } - } else { - ptr::null() - }; - - unsafe { - ptr.as_ref() - } - }; - - // Ignore errors if this socket address has statically-known length - // - // This is to ensure that unix socket addresses have their length set appropriately. - let _ = unsafe { address.set_length(mhdr.msg_namelen as usize) }; - - RecvMsg { - bytes: r as usize, - cmsghdr, - address: Some(address), - flags: MsgFlags::from_bits_truncate(mhdr.msg_flags), - mhdr, - iobufs: std::marker::PhantomData, - } -} - -/// Pack pointers to various structures into into msghdr -/// -/// # Safety -/// `iov_buffer` and `iov_buffer_len` must point to a slice -/// of `IoSliceMut` and number of available elements or be a null pointer and 0 -/// -/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used -/// to store control headers later or be a null pointer and 0 if control -/// headers are not used -/// -/// Buffers must remain valid for the whole lifetime of msghdr -unsafe fn pack_mhdr_to_receive( - iov_buffer: *mut IoSliceMut, - iov_buffer_len: usize, - cmsg_buffer: *mut u8, - cmsg_capacity: usize, - address: *mut S, -) -> msghdr - where - S: SockaddrLike -{ - // Musl's msghdr has private fields, so this is the only way to - // initialize it. - let mut mhdr = mem::MaybeUninit::::zeroed(); - let p = mhdr.as_mut_ptr(); - unsafe { - (*p).msg_name = address as *mut c_void; - (*p).msg_namelen = S::size(); - (*p).msg_iov = iov_buffer as *mut iovec; - (*p).msg_iovlen = iov_buffer_len as _; - (*p).msg_control = cmsg_buffer as *mut c_void; - (*p).msg_controllen = cmsg_capacity as _; - (*p).msg_flags = 0; - mhdr.assume_init() - } -} - -fn pack_mhdr_to_send<'a, I, C, S>( - cmsg_buffer: &mut [u8], - iov: I, - cmsgs: C, - addr: Option<&S> -) -> msghdr - where - I: AsRef<[IoSlice<'a>]>, - C: AsRef<[ControlMessage<'a>]>, - S: SockaddrLike + 'a -{ - let capacity = cmsg_buffer.len(); - - // The message header must be initialized before the individual cmsgs. - let cmsg_ptr = if capacity > 0 { - cmsg_buffer.as_mut_ptr().cast() - } 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()).cast_mut().cast(); - (*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().cast_mut().cast(); - (*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_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr()) - }; - - let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) }; - - let r = Errno::result(ret)?; - - Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) }) -} } /// Create an endpoint for communication @@ -2179,7 +3181,7 @@ pub fn socket>>( let mut ty = ty as c_int; ty |= flags.bits(); - let res = unsafe { libc::socket(domain as c_int, ty, protocol) }; + let res = unsafe { libc::socket(domain.family(), ty, protocol) }; match res { -1 => Err(Errno::last()), @@ -2213,7 +3215,7 @@ pub fn socketpair>>( let mut fds = [-1, -1]; let res = unsafe { - libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr()) + libc::socketpair(domain.family(), ty, protocol, fds.as_mut_ptr()) }; Errno::result(res)?; @@ -2234,8 +3236,11 @@ pub fn listen(sock: &F, backlog: usize) -> Result<()> { /// 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()) }; +pub fn bind(fd: RawFd, addr: A) -> Result<()> +where + A: AsRef, +{ + let res = unsafe { libc::bind(fd, addr.as_ref().as_ptr().cast(), addr.as_ref().len() as _) }; Errno::result(res).map(drop) } @@ -2281,8 +3286,11 @@ pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result { /// 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()) }; +pub fn connect(fd: RawFd, addr: A) -> Result<()> +where + A: AsRef, +{ + let res = unsafe { libc::connect(fd, addr.as_ref().as_ptr().cast(), addr.as_ref().len() as _) }; Errno::result(res).map(drop) } @@ -2309,13 +3317,13 @@ pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result { /// address of the sender. /// /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html) -pub fn recvfrom( +pub fn recvfrom( sockfd: RawFd, buf: &mut [u8], -) -> Result<(usize, Option)> { +) -> Result<(usize, Address)> { unsafe { - let mut addr = mem::MaybeUninit::::uninit(); - let mut len = mem::size_of_val(&addr) as socklen_t; + let mut addr = Address::default(); + let mut len = mem::size_of::() as socklen_t; let ret = Errno::result(libc::recvfrom( sockfd, @@ -2326,27 +3334,42 @@ pub fn recvfrom( &mut len as *mut socklen_t, ))? as usize; - Ok((ret, T::from_raw(addr.assume_init().as_ptr(), Some(len)))) + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + addr.len = len as _; + } + } + + Ok((ret, addr)) } } /// Send a message to a socket /// /// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html) -pub fn sendto( +pub fn sendto( fd: RawFd, buf: &[u8], - addr: &dyn SockaddrLike, + addr: A, flags: MsgFlags, -) -> Result { +) -> Result +where + A: AsRef, +{ let ret = unsafe { libc::sendto( fd, buf.as_ptr().cast(), buf.len() as size_t, flags.bits(), - addr.as_ptr(), - addr.len(), + addr.as_ref().as_ptr().cast(), + addr.as_ref().len() as _, ) }; @@ -2358,7 +3381,12 @@ pub fn sendto( /// [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().cast(), buf.len() as size_t, flags.bits()) + libc::send( + fd, + buf.as_ptr().cast(), + buf.len() as size_t, + flags.bits(), + ) }; Errno::result(ret).map(|r| r as usize) @@ -2420,32 +3448,59 @@ pub fn setsockopt( /// 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 { +pub fn getpeername(fd: RawFd) -> Result
{ unsafe { - let mut addr = mem::MaybeUninit::::uninit(); - let mut len = T::size(); + let mut addr = Address::default(); + let mut len = mem::size_of::() as _; - let ret = libc::getpeername(fd, addr.as_mut_ptr().cast(), &mut len); + let ret = + libc::getpeername(fd, addr.as_mut_ptr().cast(), &mut len); Errno::result(ret)?; - T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL) + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + addr.len = len as _; + } + } + + Ok(addr) } } /// 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 { +pub fn getsockname(fd: RawFd) -> Result
{ unsafe { - let mut addr = mem::MaybeUninit::::uninit(); - let mut len = T::size(); + let mut addr = Address::default(); - let ret = libc::getsockname(fd, addr.as_mut_ptr().cast(), &mut len); + let mut len = mem::size_of::() as _; + + let ret = + libc::getsockname(fd, addr.as_mut_ptr().cast(), &mut len); Errno::result(ret)?; - T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL) + cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "fuchsia", + target_os = "illumos", + target_os = "linux", + target_os = "redox", + ))] { + addr.len = len as _; + } + } + + Ok(addr) } } @@ -2481,7 +3536,7 @@ mod tests { #[cfg(not(target_os = "redox"))] #[test] fn can_use_cmsg_space() { - let _ = cmsg_space!(u8); + let _ = cmsg_space!(ScmTimestamp); } #[cfg(not(any( @@ -2492,7 +3547,7 @@ mod tests { #[test] fn can_open_routing_socket() { let _ = super::socket( - super::AddressFamily::Route, + super::AddressFamily::ROUTE, super::SockType::Raw, super::SockFlag::empty(), None, diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 511babf8d8..f929a12379 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -332,7 +332,8 @@ cfg_if! { } else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "solaris"))] { @@ -476,7 +477,12 @@ sockopt_impl!( libc::SO_KEEPALIVE, bool ); -#[cfg(any(target_os = "dragonfly", target_os = "freebsd", apple_targets))] +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" +))] sockopt_impl!( /// Get the credentials of the peer process of a connected unix domain /// socket. @@ -486,7 +492,7 @@ sockopt_impl!( libc::LOCAL_PEERCRED, super::XuCred ); -#[cfg(apple_targets)] +#[cfg(any(target_os = "macos", target_os = "ios"))] sockopt_impl!( /// Get the PID of the peer process of a connected unix domain socket. LocalPeerPid, @@ -504,7 +510,7 @@ sockopt_impl!( libc::SO_PEERCRED, super::UnixCredentials ); -#[cfg(apple_targets)] +#[cfg(any(target_os = "ios", target_os = "macos"))] #[cfg(feature = "net")] sockopt_impl!( #[cfg_attr(docsrs, doc(cfg(feature = "net")))] @@ -832,8 +838,9 @@ sockopt_impl!( ); #[cfg(any( target_os = "android", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", ))] #[cfg(feature = "net")] @@ -850,8 +857,9 @@ sockopt_impl!( #[cfg(any( target_os = "android", target_os = "freebsd", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -868,7 +876,8 @@ sockopt_impl!( ); #[cfg(any( target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -885,7 +894,8 @@ sockopt_impl!( ); #[cfg(any( target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -1025,7 +1035,7 @@ sockopt_impl!( libc::IPV6_ORIGDSTADDR, bool ); -#[cfg(apple_targets)] +#[cfg(any(target_os = "ios", target_os = "macos"))] sockopt_impl!( /// Set "don't fragment packet" flag on the IP packet. IpDontFrag, @@ -1034,7 +1044,12 @@ sockopt_impl!( libc::IP_DONTFRAG, bool ); -#[cfg(any(target_os = "android", apple_targets, target_os = "linux",))] +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", +))] sockopt_impl!( /// Set "don't fragment packet" flag on the IPv6 packet. Ipv6DontFrag, @@ -1400,7 +1415,7 @@ mod test { use super::super::*; let (a, b) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -1417,7 +1432,7 @@ mod test { use super::super::*; let (a, _b) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -1432,7 +1447,7 @@ mod test { use super::super::*; let s = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -1448,7 +1463,7 @@ mod test { use super::super::*; let s = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), None, diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 19b17fec46..49a118de69 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -1,9 +1,10 @@ #[cfg(any(target_os = "linux", target_os = "android"))] use crate::*; use libc::c_char; -use nix::sys::socket::{getsockname, AddressFamily, UnixAddr}; +use nix::sys::socket::*; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; +use std::iter::once; use std::net::{SocketAddrV4, SocketAddrV6}; use std::os::unix::io::{AsRawFd, RawFd}; use std::path::Path; @@ -14,17 +15,13 @@ use std::str::FromStr; #[cfg_attr(qemu, ignore)] #[test] pub fn test_timestamping() { - use nix::sys::socket::{ - recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping, - ControlMessageOwned, MsgFlags, SockFlag, SockType, SockaddrIn, - TimestampingFlag, - }; + use nix::sys::socket::{sockopt::Timestamping, *}; use std::io::{IoSlice, IoSliceMut}; - let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap(); + let sock_addr = Ipv4Address::from_str("127.0.0.1:6790").unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -32,13 +29,13 @@ pub fn test_timestamping() { .expect("send socket failed"); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + nix::sys::socket::bind(rsock.as_raw_fd(), sock_addr).unwrap(); setsockopt(&rsock, Timestamping, &TimestampingFlag::all()).unwrap(); @@ -48,14 +45,16 @@ pub fn test_timestamping() { let iov1 = [IoSlice::new(&sbuf)]; let mut iov2 = [IoSliceMut::new(&mut rbuf)]; - let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps); - sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap(); - let recv = - recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags) - .unwrap(); + let mut cmsg = cmsg_buf![ScmTimestampsns]; + + sendmsg(ssock.as_raw_fd(), sock_addr, &iov1, CmsgStr::empty(), flags) + .unwrap(); + + let _ = + recvmsg(rsock.as_raw_fd(), &mut iov2, cmsg.handle(), flags).unwrap(); let mut ts = None; - for c in recv.cmsgs() { + for c in cmsg.iter() { if let ControlMessageOwned::ScmTimestampsns(timestamps) = c { ts = Some(timestamps.system); } @@ -76,7 +75,7 @@ pub fn test_timestamping() { pub fn test_path_to_sock_addr() { let path = "/foo/bar"; let actual = Path::new(path); - let addr = UnixAddr::new(actual).unwrap(); + let addr = UnixAddress::new(actual).unwrap(); let expect: &[c_char] = unsafe { slice::from_raw_parts(path.as_ptr().cast(), path.len()) }; @@ -95,7 +94,7 @@ fn calculate_hash(t: &T) -> u64 { pub fn test_addr_equality_path() { let path = "/foo/bar"; let actual = Path::new(path); - let addr1 = UnixAddr::new(actual).unwrap(); + let addr1 = UnixAddress::new(actual).unwrap(); let mut addr2 = addr1; unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 }; @@ -108,7 +107,7 @@ pub fn test_addr_equality_path() { #[test] pub fn test_abstract_sun_path_too_long() { let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough"); - let addr = UnixAddr::new_abstract(name.as_bytes()); + let addr = UnixAddress::new_abstract(name.as_bytes()); addr.expect_err("assertion failed"); } @@ -116,7 +115,7 @@ pub fn test_abstract_sun_path_too_long() { #[test] pub fn test_addr_equality_abstract() { let name = String::from("nix\0abstract\0test"); - let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let addr1 = UnixAddress::new_abstract(name.as_bytes()).unwrap(); let mut addr2 = addr1; assert_eq!(addr1, addr2); @@ -132,12 +131,12 @@ pub fn test_addr_equality_abstract() { #[test] pub fn test_abstract_uds_addr() { let empty = String::new(); - let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap(); + let addr = UnixAddress::new_abstract(empty.as_bytes()).unwrap(); let sun_path: [u8; 0] = []; assert_eq!(addr.as_abstract(), Some(&sun_path[..])); let name = String::from("nix\0abstract\0test"); - let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap(); + let addr = UnixAddress::new_abstract(name.as_bytes()).unwrap(); let sun_path = [ 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101, 115, 116, @@ -153,9 +152,7 @@ pub fn test_abstract_uds_addr() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] pub fn test_unnamed_uds_addr() { - use crate::nix::sys::socket::SockaddrLike; - - let addr = UnixAddr::new_unnamed(); + let addr = UnixAddress::new_unnamed(); assert!(addr.is_unnamed()); assert_eq!(addr.len(), 2); @@ -168,32 +165,35 @@ pub fn test_unnamed_uds_addr() { #[test] pub fn test_getsockname() { use nix::sys::socket::bind; - use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{socket, SockFlag, SockType}; let tempdir = tempfile::tempdir().unwrap(); let sockname = tempdir.path().join("sock"); let sock = socket( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, SockFlag::empty(), None, ) .expect("socket failed"); - let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + let sockaddr = UnixAddress::new(&sockname).unwrap(); + bind(sock.as_raw_fd(), sockaddr).expect("bind failed"); assert_eq!( sockaddr, - getsockname(sock.as_raw_fd()).expect("getsockname failed") + *getsockname(sock.as_raw_fd()) + .expect("getsockname failed") + .to_unix() + .unwrap() ); } #[test] pub fn test_socketpair() { - use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType}; + use nix::sys::socket::{socketpair, SockFlag, SockType}; use nix::unistd::{read, write}; let (fd1, fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -208,30 +208,28 @@ pub fn test_socketpair() { #[test] pub fn test_recvmsg_sockaddr_un() { - use nix::sys::socket::{ - self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType, - }; + use nix::sys::socket::{self, *}; let tempdir = tempfile::tempdir().unwrap(); let sockname = tempdir.path().join("sock"); let sock = socket( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Datagram, SockFlag::empty(), None, ) .expect("socket failed"); - let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + let sockaddr = UnixAddress::new(&sockname).unwrap(); + bind(sock.as_raw_fd(), sockaddr).expect("bind failed"); // Send a message let send_buffer = "hello".as_bytes(); if let Err(e) = socket::sendmsg( sock.as_raw_fd(), + sockaddr, &[std::io::IoSlice::new(send_buffer)], - &[], + CmsgStr::empty(), MsgFlags::empty(), - Some(&sockaddr), ) { crate::skip!("Couldn't send ({e:?}), so skipping test"); } @@ -239,11 +237,16 @@ pub fn test_recvmsg_sockaddr_un() { // Receive the message let mut recv_buffer = [0u8; 32]; let mut iov = [std::io::IoSliceMut::new(&mut recv_buffer)]; - let received = - socket::recvmsg(sock.as_raw_fd(), &mut iov, None, MsgFlags::empty()) - .unwrap(); + + let res = socket::recvmsg( + sock.as_raw_fd(), + &mut iov, + Default::default(), + MsgFlags::empty(), + ) + .unwrap(); // Check the address in the received message - assert_eq!(sockaddr, received.address.unwrap()); + assert_eq!(sockaddr, *res.address().to_unix().unwrap()); } #[test] @@ -251,17 +254,16 @@ pub fn test_std_conversions() { use nix::sys::socket::*; let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); + let sock_addr = Ipv4Address::from(std_sa); assert_eq!(std_sa, sock_addr.into()); let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap(); - let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa); + let sock_addr: Ipv6Address = Ipv6Address::from(std_sa); assert_eq!(std_sa, sock_addr.into()); } mod recvfrom { use super::*; - use nix::sys::socket::*; use nix::{errno::Errno, Result}; use std::thread; @@ -272,10 +274,10 @@ mod recvfrom { ssock: RawFd, f_send: Fs, mut f_recv: Fr, - ) -> Option + ) -> Option
where Fs: Fn(RawFd, &[u8], MsgFlags) -> Result + Send + 'static, - Fr: FnMut(usize, Option), + Fr: FnMut(usize, Address), { let mut buf: [u8; 13] = [0u8; 13]; let mut l = 0; @@ -291,7 +293,7 @@ mod recvfrom { while l < std::mem::size_of_val(MSG) { let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap(); f_recv(len, from_); - from = from_; + from = Some(from_); l += len; } assert_eq!(&buf, MSG); @@ -302,7 +304,7 @@ mod recvfrom { #[test] pub fn stream() { let (fd2, fd1) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -315,17 +317,17 @@ mod recvfrom { #[test] pub fn udp() { let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); + let sock_addr = Ipv4Address::from(std_sa); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -334,18 +336,18 @@ mod recvfrom { let from = sendrecv( rsock.as_raw_fd(), ssock.as_raw_fd(), - move |s, m, flags| sendto(s.as_raw_fd(), m, &sock_addr, flags), + move |s, m, flags| sendto(s.as_raw_fd(), m, sock_addr, flags), |_, _| {}, ); // UDP sockets should set the from address - assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap()); + assert_eq!(AddressFamily::INET, from.unwrap().family()); } #[cfg(target_os = "linux")] mod udp_offload { use super::*; use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment}; - use std::io::IoSlice; + use std::{io::IoSlice, iter::once}; #[test] // Disable the test under emulation because it fails in Cirrus-CI. Lack @@ -359,9 +361,9 @@ mod recvfrom { // with size 2 and two UDP packet with size 1 will be sent. let segment_size: u16 = 2; - let sock_addr = SockaddrIn::new(127, 0, 0, 1, 6791); + let sock_addr = Ipv4Address::new(127, 0, 0, 1, 6791); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -371,9 +373,9 @@ mod recvfrom { setsockopt(&rsock, UdpGsoSegment, &(segment_size as _)) .expect("setsockopt UDP_SEGMENT failed"); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -388,13 +390,11 @@ mod recvfrom { move |s, m, flags| { let iov = [IoSlice::new(m)]; let cmsg = ControlMessage::UdpGsoSegments(&segment_size); - sendmsg( - s.as_raw_fd(), - &iov, - &[cmsg], - flags, - Some(&sock_addr), - ) + let cmsg_space = cmsg_space_iter(once(cmsg)); + let cmsg = + CmsgVec::from_iter(once(cmsg), cmsg_space).unwrap(); + sendmsg(s.as_raw_fd(), sock_addr, &iov, &cmsg, flags) + .map(|r| r.bytes()) }, { let num_packets_received_ref = &mut num_packets_received; @@ -424,7 +424,7 @@ mod recvfrom { // that `setsockopt` doesn't fail with error let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -448,19 +448,19 @@ mod recvfrom { let std_sa = SocketAddrV4::from_str("127.0.0.1:6793").unwrap(); let std_sa2 = SocketAddrV4::from_str("127.0.0.1:6794").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); - let sock_addr2 = SockaddrIn::from(std_sa2); + let sock_addr = Ipv4Address::from(std_sa); + let sock_addr2 = Ipv4Address::from(std_sa2); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -474,25 +474,29 @@ mod recvfrom { let batch_size = 15; let mut iovs = Vec::with_capacity(1 + batch_size); let mut addrs = Vec::with_capacity(1 + batch_size); - let mut data = MultiHeaders::preallocate(1 + batch_size, None); + let mut headers = + SendMmsgHeaders::with_capacity(1 + batch_size); let iov = IoSlice::new(m); // first chunk: iovs.push([iov]); - addrs.push(Some(sock_addr)); + addrs.push(sock_addr); for _ in 0..batch_size { iovs.push([iov]); - addrs.push(Some(sock_addr2)); + addrs.push(sock_addr2); } - let res = sendmmsg(s, &mut data, &iovs, addrs, [], flags)?; - let mut sent_messages = 0; + let items = addrs + .iter() + .zip(iovs.iter()) + .map(|(addr, iov)| (addr, iov, CmsgStr::empty())); + + let sent_messages = sendmmsg(s, &mut headers, items, flags)?; let mut sent_bytes = 0; - for item in res { - sent_messages += 1; - sent_bytes += item.bytes; + for item in headers.iter() { + sent_bytes += item.bytes(); } - // + assert_eq!(sent_messages, iovs.len()); assert_eq!(sent_bytes, sent_messages * m.len()); Ok(sent_messages) @@ -500,7 +504,7 @@ mod recvfrom { |_, _| {}, ); // UDP sockets should set the from address - assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap()); + assert_eq!(AddressFamily::INET, from.unwrap().family()); } #[cfg(any( @@ -511,25 +515,24 @@ mod recvfrom { ))] #[test] pub fn udp_recvmmsg() { - use nix::sys::socket::{recvmmsg, MsgFlags}; use std::io::IoSliceMut; const NUM_MESSAGES_SENT: usize = 2; const DATA: [u8; 2] = [1, 2]; let inet_addr = SocketAddrV4::from_str("127.0.0.1:6798").unwrap(); - let sock_addr = SockaddrIn::from(inet_addr); + let sock_addr = Ipv4Address::from(inet_addr); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -541,7 +544,7 @@ mod recvfrom { sendto( ssock.as_raw_fd(), &DATA[..], - &sock_addr, + sock_addr, MsgFlags::empty(), ) .unwrap(); @@ -558,23 +561,21 @@ mod recvfrom { .map(|buf| [IoSliceMut::new(&mut buf[..])]), ); - let mut data = - MultiHeaders::::preallocate(msgs.len(), None); + let mut headers = RecvMmsgHeaders::with_capacity(msgs.len()); - let res: Vec> = recvmmsg( + let recv = recvmmsg( rsock.as_raw_fd(), - &mut data, - msgs.iter_mut(), + &mut headers, + msgs.iter_mut().map(|iov| (iov, Default::default())), MsgFlags::empty(), None, ) - .expect("recvmmsg") - .collect(); - assert_eq!(res.len(), DATA.len()); + .expect("recvmmsg"); + assert_eq!(recv, DATA.len()); - for RecvMsg { address, bytes, .. } in res.into_iter() { - assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap()); - assert_eq!(DATA.len(), bytes); + for h in headers.iter() { + assert_eq!(AddressFamily::INET, h.address().family()); + assert_eq!(DATA.len(), h.bytes()); } for buf in &receive_buffers { @@ -592,25 +593,24 @@ mod recvfrom { ))] #[test] pub fn udp_recvmmsg_dontwait_short_read() { - use nix::sys::socket::{recvmmsg, MsgFlags}; use std::io::IoSliceMut; const NUM_MESSAGES_SENT: usize = 2; const DATA: [u8; 4] = [1, 2, 3, 4]; let inet_addr = SocketAddrV4::from_str("127.0.0.1:6799").unwrap(); - let sock_addr = SockaddrIn::from(inet_addr); + let sock_addr = Ipv4Address::from(inet_addr); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -622,7 +622,7 @@ mod recvfrom { sendto( ssock.as_raw_fd(), &DATA[..], - &sock_addr, + sock_addr, MsgFlags::empty(), ) .unwrap(); @@ -644,25 +644,22 @@ mod recvfrom { .map(|buf| [IoSliceMut::new(&mut buf[..])]), ); - let mut data = MultiHeaders::::preallocate( - NUM_MESSAGES_SENT + 2, - None, - ); + let mut headers = RecvMmsgHeaders::with_capacity(NUM_MESSAGES_SENT + 1); - let res: Vec> = recvmmsg( + let recv = recvmmsg( rsock.as_raw_fd(), - &mut data, - msgs.iter_mut(), + &mut headers, + msgs.iter_mut().map(|iov| (iov, Default::default())), MsgFlags::MSG_DONTWAIT, None, ) - .expect("recvmmsg") - .collect(); - assert_eq!(res.len(), NUM_MESSAGES_SENT); + .expect("recvmmsg"); + + assert_eq!(recv, NUM_MESSAGES_SENT); - for RecvMsg { address, bytes, .. } in res.into_iter() { - assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap()); - assert_eq!(DATA.len(), bytes); + for h in headers.iter() { + assert_eq!(AddressFamily::INET, h.address().family()); + assert_eq!(DATA.len(), h.bytes()); } for buf in &receive_buffers[..NUM_MESSAGES_SENT] { @@ -675,18 +672,18 @@ mod recvfrom { let addr = std::net::Ipv6Addr::from_str("::1").unwrap(); let rport = 6789; let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0); - let raddr = SockaddrIn6::from(rstd_sa); + let raddr = Ipv6Address::from(rstd_sa); let sport = 6790; let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0); - let saddr = SockaddrIn6::from(sstd_sa); + let saddr = Ipv6Address::from(sstd_sa); let rsock = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - match bind(rsock.as_raw_fd(), &raddr) { + match bind(rsock.as_raw_fd(), raddr) { Err(Errno::EADDRNOTAVAIL) => { println!("IPv6 not available, skipping test."); return; @@ -695,22 +692,22 @@ mod recvfrom { Ok(()) => (), } let ssock = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - bind(ssock.as_raw_fd(), &saddr).unwrap(); + bind(ssock.as_raw_fd(), saddr).unwrap(); let from = sendrecv( rsock.as_raw_fd(), ssock.as_raw_fd(), - move |s, m, flags| sendto(s.as_raw_fd(), m, &raddr, flags), + move |s, m, flags| sendto(s.as_raw_fd(), m, raddr, flags), |_, _| {}, ); - assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap()); + assert_eq!(AddressFamily::INET6, from.unwrap().family()); let osent_addr = from.unwrap(); - let sent_addr = osent_addr.as_sockaddr_in6().unwrap(); + let sent_addr = osent_addr.to_ipv6().unwrap(); assert_eq!(sent_addr.ip(), addr); assert_eq!(sent_addr.port(), sport); } @@ -720,14 +717,19 @@ mod recvfrom { #[test] pub fn test_recvmsg_ebadf() { use nix::errno::Errno; - use nix::sys::socket::{recvmsg, MsgFlags}; + use nix::sys::socket::*; use std::io::IoSliceMut; let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; let fd = -1; // Bad file descriptor - let r = recvmsg::<()>(fd.as_raw_fd(), &mut iov, None, MsgFlags::empty()); + let r = recvmsg( + fd.as_raw_fd(), + &mut iov, + Default::default(), + MsgFlags::empty(), + ); assert_eq!(r.err().unwrap(), Errno::EBADF); } @@ -737,15 +739,12 @@ pub fn test_recvmsg_ebadf() { #[cfg_attr(qemu, ignore)] #[test] pub fn test_scm_rights() { - use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, - }; + use nix::sys::socket::*; use nix::unistd::{close, pipe, read, write}; use std::io::{IoSlice, IoSliceMut}; let (fd1, fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -758,15 +757,19 @@ pub fn test_scm_rights() { let iov = [IoSlice::new(b"hello")]; let fds = [r.as_raw_fd()]; let cmsg = ControlMessage::ScmRights(&fds); + let cmsg_space = cmsg_space_iter(once(cmsg)); + let cmsg = CmsgVec::from_iter(once(cmsg), cmsg_space).unwrap(); + assert_eq!( - sendmsg::<()>( + sendmsg( fd1.as_raw_fd(), + Addr::empty(), &iov, - &[cmsg], + &cmsg, MsgFlags::empty(), - None ) - .unwrap(), + .unwrap() + .bytes(), 5 ); } @@ -775,16 +778,17 @@ pub fn test_scm_rights() { let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![ScmRights(1)]; + + let msg = recvmsg( fd2.as_raw_fd(), &mut iov, - Some(&mut cmsgspace), + cmsg.handle(), MsgFlags::empty(), ) .unwrap(); - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { if let ControlMessageOwned::ScmRights(fd) = cmsg { assert_eq!(received_r, None); assert_eq!(fd.len(), 1); @@ -793,9 +797,9 @@ pub fn test_scm_rights() { panic!("unexpected cmsg"); } } - assert_eq!(msg.bytes, 5); + assert_eq!(msg.bytes(), 5); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); } @@ -815,8 +819,8 @@ pub fn test_scm_rights() { pub fn test_af_alg_cipher() { use nix::sys::socket::sockopt::AlgSetKey; use nix::sys::socket::{ - accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr, - ControlMessage, MsgFlags, SockFlag, SockType, + accept, bind, sendmsg, setsockopt, socket, AlgAddress, ControlMessage, + MsgFlags, SockFlag, SockType, }; use nix::unistd::read; use std::io::IoSlice; @@ -838,15 +842,15 @@ pub fn test_af_alg_cipher() { let payload = vec![2u8; payload_len]; let sock = socket( - AddressFamily::Alg, + AddressFamily::ALG, SockType::SeqPacket, SockFlag::empty(), None, ) .expect("socket failed"); - let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + let sockaddr = AlgAddress::new(alg_type, alg_name); + bind(sock.as_raw_fd(), sockaddr).expect("bind failed"); assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name); assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type); @@ -858,13 +862,15 @@ pub fn test_af_alg_cipher() { ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), ControlMessage::AlgSetIv(iv.as_slice()), ]; + let cmsg_space = cmsg_space_iter(msgs.iter().copied()); + let cmsg = CmsgVec::from_iter(msgs, cmsg_space).unwrap(); let iov = IoSlice::new(&payload); - sendmsg::<()>( + sendmsg( session_socket.as_raw_fd(), + Addr::empty(), &[iov], - &msgs, + &cmsg, MsgFlags::empty(), - None, ) .expect("sendmsg encrypt"); @@ -882,12 +888,14 @@ pub fn test_af_alg_cipher() { ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice()), ]; - sendmsg::<()>( + let cmsg_space = cmsg_space_iter(msgs.iter().copied()); + let cmsg = CmsgVec::from_iter(msgs, cmsg_space).unwrap(); + sendmsg( session_socket.as_raw_fd(), + Addr::empty(), &[iov], - &msgs, + &cmsg, MsgFlags::empty(), - None, ) .expect("sendmsg decrypt"); @@ -910,8 +918,8 @@ pub fn test_af_alg_aead() { use nix::fcntl::{fcntl, FcntlArg, OFlag}; use nix::sys::socket::sockopt::{AlgSetAeadAuthSize, AlgSetKey}; use nix::sys::socket::{ - accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr, - ControlMessage, MsgFlags, SockFlag, SockType, + accept, bind, sendmsg, setsockopt, socket, AlgAddress, ControlMessage, + MsgFlags, SockFlag, SockType, }; use nix::unistd::read; use std::io::IoSlice; @@ -947,15 +955,15 @@ pub fn test_af_alg_aead() { } let sock = socket( - AddressFamily::Alg, + AddressFamily::ALG, SockType::SeqPacket, SockFlag::empty(), None, ) .expect("socket failed"); - let sockaddr = AlgAddr::new(alg_type, alg_name); - bind(sock.as_raw_fd(), &sockaddr).expect("bind failed"); + let sockaddr = AlgAddress::new(alg_type, alg_name); + bind(sock.as_raw_fd(), sockaddr).expect("bind failed"); setsockopt(&sock, AlgSetAeadAuthSize, &auth_size) .expect("setsockopt AlgSetAeadAuthSize"); @@ -969,13 +977,16 @@ pub fn test_af_alg_aead() { ControlMessage::AlgSetAeadAssoclen(&assoc_size), ]; + let cmsg_space = cmsg_space_iter(msgs.iter().copied()); + let cmsg = CmsgVec::from_iter(msgs, cmsg_space).unwrap(); + let iov = IoSlice::new(&payload); - sendmsg::<()>( + sendmsg( session_socket.as_raw_fd(), + Addr::empty(), &[iov], - &msgs, + &cmsg, MsgFlags::empty(), - None, ) .expect("sendmsg encrypt"); @@ -1001,12 +1012,14 @@ pub fn test_af_alg_aead() { ControlMessage::AlgSetIv(iv.as_slice()), ControlMessage::AlgSetAeadAssoclen(&assoc_size), ]; - sendmsg::<()>( + let cmsg_space = cmsg_space_iter(msgs.iter().copied()); + let cmsg = CmsgVec::from_iter(msgs, cmsg_space).unwrap(); + sendmsg( session_socket.as_raw_fd(), + Addr::empty(), &[iov], - &msgs, + &cmsg, MsgFlags::empty(), - None, ) .expect("sendmsg decrypt"); @@ -1041,22 +1054,22 @@ pub fn test_af_alg_aead() { pub fn test_sendmsg_ipv4packetinfo() { use cfg_if::cfg_if; use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn, + bind, sendmsg, socket, ControlMessage, Ipv4Address, MsgFlags, SockFlag, + SockType, }; use std::io::IoSlice; let sock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("socket failed"); - let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000); + let sock_addr = Ipv4Address::new(127, 0, 0, 1, 4000); - bind(sock.as_raw_fd(), &sock_addr).expect("bind failed"); + bind(sock.as_raw_fd(), sock_addr).expect("bind failed"); let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; let iov = [IoSlice::new(&slice)]; @@ -1071,21 +1084,17 @@ pub fn test_sendmsg_ipv4packetinfo() { let pi = libc::in_pktinfo { ipi_ifindex: 0, /* Unspecified interface */ ipi_addr: libc::in_addr { s_addr: 0 }, - ipi_spec_dst: sock_addr.as_ref().sin_addr, + ipi_spec_dst: <_ as AsRef>::as_ref(&sock_addr).sin_addr, }; } } let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)]; + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg, cmsg_space).unwrap(); - sendmsg( - sock.as_raw_fd(), - &iov, - &cmsg, - MsgFlags::empty(), - Some(&sock_addr), - ) - .expect("sendmsg"); + sendmsg(sock.as_raw_fd(), sock_addr, &iov, &cmsg, MsgFlags::empty()) + .expect("sendmsg"); } // Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`. @@ -1106,13 +1115,13 @@ pub fn test_sendmsg_ipv4packetinfo() { pub fn test_sendmsg_ipv6packetinfo() { use nix::errno::Errno; use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn6, + bind, sendmsg, socket, ControlMessage, Ipv6Address, MsgFlags, SockFlag, + SockType, }; use std::io::IoSlice; let sock = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, @@ -1120,9 +1129,9 @@ pub fn test_sendmsg_ipv6packetinfo() { .expect("socket failed"); let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap(); - let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa); + let sock_addr: Ipv6Address = Ipv6Address::from(std_sa); - if let Err(Errno::EADDRNOTAVAIL) = bind(sock.as_raw_fd(), &sock_addr) { + if let Err(Errno::EADDRNOTAVAIL) = bind(sock.as_raw_fd(), sock_addr) { println!("IPv6 not available, skipping test."); return; } @@ -1132,19 +1141,16 @@ pub fn test_sendmsg_ipv6packetinfo() { let pi = libc::in6_pktinfo { ipi6_ifindex: 0, /* Unspecified interface */ - ipi6_addr: sock_addr.as_ref().sin6_addr, + ipi6_addr: <_ as AsRef>::as_ref(&sock_addr) + .sin6_addr, }; let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)]; + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg, cmsg_space).unwrap(); - sendmsg::( - sock.as_raw_fd(), - &iov, - &cmsg, - MsgFlags::empty(), - Some(&sock_addr), - ) - .expect("sendmsg"); + sendmsg(sock.as_raw_fd(), sock_addr, &iov, &cmsg, MsgFlags::empty()) + .expect("sendmsg"); } // Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This @@ -1164,37 +1170,44 @@ pub fn test_sendmsg_ipv6packetinfo() { #[test] pub fn test_sendmsg_ipv4sendsrcaddr() { use nix::sys::socket::{ - bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags, - SockFlag, SockType, SockaddrIn, + bind, cmsg_space_iter, sendmsg, socket, CmsgVec, ControlMessage, + Ipv4Address, MsgFlags, SockFlag, SockType, }; use std::io::IoSlice; let sock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("socket failed"); - let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0); - bind(sock.as_raw_fd(), &unspec_sock_addr).expect("bind failed"); - let bound_sock_addr: SockaddrIn = getsockname(sock.as_raw_fd()).unwrap(); - let localhost_sock_addr: SockaddrIn = - SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port()); + let unspec_sock_addr = Ipv4Address::new(0, 0, 0, 0, 0); + bind(sock.as_raw_fd(), unspec_sock_addr).expect("bind failed"); + let bound_sock_addr = getsockname(sock.as_raw_fd()).unwrap(); + let localhost_sock_addr: Ipv4Address = Ipv4Address::new( + 127, + 0, + 0, + 1, + bound_sock_addr.to_ipv4().unwrap().port(), + ); let slice = [1u8, 2, 3, 4, 5, 6, 7, 8]; let iov = [IoSlice::new(&slice)]; let cmsg = [ControlMessage::Ipv4SendSrcAddr( - &localhost_sock_addr.as_ref().sin_addr, + &<_ as AsRef>::as_ref(&localhost_sock_addr).sin_addr, )]; + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg, cmsg_space).unwrap(); sendmsg( sock.as_raw_fd(), + localhost_sock_addr, &iov, &cmsg, MsgFlags::empty(), - Some(&localhost_sock_addr), ) .expect("sendmsg"); } @@ -1209,7 +1222,7 @@ fn test_scm_rights_single_cmsg_multiple_fds() { recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags, }; use std::io::{IoSlice, IoSliceMut}; - use std::os::unix::io::{AsRawFd, RawFd}; + use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixDatagram; use std::thread; @@ -1218,19 +1231,20 @@ fn test_scm_rights_single_cmsg_multiple_fds() { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!([RawFd; 2]); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![ScmRights(2)]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .unwrap(); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = cmsg.iter(); match cmsgs.next() { Some(ControlMessageOwned::ScmRights(fds)) => { assert_eq!( @@ -1244,7 +1258,7 @@ fn test_scm_rights_single_cmsg_multiple_fds() { } assert!(cmsgs.next().is_none(), "unexpected control msg"); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); }); @@ -1252,8 +1266,16 @@ fn test_scm_rights_single_cmsg_multiple_fds() { let iov = [IoSlice::new(&slice)]; let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout let cmsg = [ControlMessage::ScmRights(&fds)]; - sendmsg::<()>(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None) - .unwrap(); + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg, cmsg_space).unwrap(); + sendmsg( + send.as_raw_fd(), + Addr::empty(), + &iov, + &cmsg, + MsgFlags::empty(), + ) + .unwrap(); thread.join().unwrap(); } @@ -1263,14 +1285,11 @@ fn test_scm_rights_single_cmsg_multiple_fds() { // raw `sendmsg`. #[test] pub fn test_sendmsg_empty_cmsgs() { - use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag, - SockType, - }; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; let (fd1, fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -1280,8 +1299,15 @@ pub fn test_sendmsg_empty_cmsgs() { { let iov = [IoSlice::new(b"hello")]; assert_eq!( - sendmsg::<()>(fd1.as_raw_fd(), &iov, &[], MsgFlags::empty(), None) - .unwrap(), + sendmsg( + fd1.as_raw_fd(), + Addr::empty(), + &iov, + CmsgStr::empty(), + MsgFlags::empty() + ) + .unwrap() + .bytes(), 5 ); } @@ -1290,22 +1316,23 @@ pub fn test_sendmsg_empty_cmsgs() { let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; - let mut cmsgspace = cmsg_space!([RawFd; 1]); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![ScmRights(1)]; + + let msg = recvmsg( fd2.as_raw_fd(), &mut iov, - Some(&mut cmsgspace), + cmsg.handle(), MsgFlags::empty(), ) .unwrap(); - if msg.cmsgs().next().is_some() { + if cmsg.iter().next().is_some() { panic!("unexpected cmsg"); } assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.bytes, 5); + assert_eq!(msg.bytes(), 5); } } @@ -1318,8 +1345,8 @@ pub fn test_sendmsg_empty_cmsgs() { #[test] fn test_scm_credentials() { use nix::sys::socket::{ - recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials, + recvmsg, sendmsg, socketpair, ControlMessage, ControlMessageOwned, + MsgFlags, SockFlag, SockType, }; #[cfg(any(target_os = "android", target_os = "linux"))] use nix::sys::socket::{setsockopt, sockopt::PassCred}; @@ -1327,7 +1354,7 @@ fn test_scm_credentials() { use std::io::{IoSlice, IoSliceMut}; let (send, recv) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -1339,20 +1366,24 @@ fn test_scm_credentials() { { let iov = [IoSlice::new(b"hello")]; #[cfg(any(target_os = "android", target_os = "linux"))] - let cred = UnixCredentials::new(); + let cred = nix::sys::socket::UnixCredentials::new(); #[cfg(any(target_os = "android", target_os = "linux"))] - let cmsg = ControlMessage::ScmCredentials(&cred); + let cmsg = [ControlMessage::ScmCredentials(&cred)]; #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - let cmsg = ControlMessage::ScmCreds; + let cmsg = [ControlMessage::ScmCreds]; + + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg, cmsg_space).unwrap(); assert_eq!( - sendmsg::<()>( + sendmsg( send.as_raw_fd(), + Addr::empty(), &iov, - &[cmsg], + &cmsg, MsgFlags::empty(), - None ) - .unwrap(), + .unwrap() + .bytes(), 5 ); } @@ -1361,17 +1392,21 @@ fn test_scm_credentials() { let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; - let mut cmsgspace = cmsg_space!(UnixCredentials); - let msg = recvmsg::<()>( + #[cfg(any(target_os = "android", target_os = "linux"))] + let mut cmsg = cmsg_buf![ScmCredentials]; + #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] + let mut cmsg = cmsg_buf![ScmCreds]; + + let msg = recvmsg( recv.as_raw_fd(), &mut iov, - Some(&mut cmsgspace), + cmsg.handle(), MsgFlags::empty(), ) .unwrap(); let mut received_cred = None; - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { let cred = match cmsg { #[cfg(any(target_os = "android", target_os = "linux"))] ControlMessageOwned::ScmCredentials(cred) => cred, @@ -1386,9 +1421,9 @@ fn test_scm_credentials() { received_cred = Some(cred); } received_cred.expect("no creds received"); - assert_eq!(msg.bytes, 5); + assert_eq!(msg.bytes(), 5); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); } } @@ -1401,8 +1436,8 @@ fn test_scm_credentials() { #[cfg_attr(qemu, ignore)] #[test] fn test_scm_credentials_and_rights() { - let space = cmsg_space!(libc::ucred, RawFd); - test_impl_scm_credentials_and_rights(space); + const SPACE: usize = cmsg_space!(ScmCredentials, ScmRights(1)); + test_impl_scm_credentials_and_rights(SPACE); } /// Ensure that passing a an oversized control message buffer to recvmsg @@ -1413,23 +1448,18 @@ fn test_scm_credentials_and_rights() { #[cfg_attr(qemu, ignore)] #[test] fn test_too_large_cmsgspace() { - let space = vec![0u8; 1024]; - test_impl_scm_credentials_and_rights(space); + test_impl_scm_credentials_and_rights(1024); } #[cfg(any(target_os = "android", target_os = "linux"))] -fn test_impl_scm_credentials_and_rights(mut space: Vec) { +fn test_impl_scm_credentials_and_rights(space: usize) { use libc::ucred; use nix::sys::socket::sockopt::PassCred; - use nix::sys::socket::{ - recvmsg, sendmsg, setsockopt, socketpair, ControlMessage, - ControlMessageOwned, MsgFlags, SockFlag, SockType, - }; use nix::unistd::{close, getgid, getpid, getuid, pipe, write}; use std::io::{IoSlice, IoSliceMut}; let (send, recv) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -1453,15 +1483,19 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { ControlMessage::ScmCredentials(&cred), ControlMessage::ScmRights(&fds), ]; + let cmsg_space = cmsg_space_iter(cmsgs.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsgs, cmsg_space).unwrap(); + assert_eq!( - sendmsg::<()>( + sendmsg( send.as_raw_fd(), + Addr::empty(), &iov, - &cmsgs, + &cmsg, MsgFlags::empty(), - None ) - .unwrap(), + .unwrap() + .bytes(), 5 ); } @@ -1469,18 +1503,20 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { { let mut buf = [0u8; 5]; let mut iov = [IoSliceMut::new(&mut buf[..])]; - let msg = recvmsg::<()>( + let mut cmsg = CmsgBuf::with_capacity(space); + + let msg = recvmsg( recv.as_raw_fd(), &mut iov, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .unwrap(); let mut received_cred = None; - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + assert_eq!(cmsg.iter().count(), 2, "expected 2 cmsgs"); - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { match cmsg { ControlMessageOwned::ScmRights(fds) => { assert_eq!(received_r, None, "already received fd"); @@ -1498,9 +1534,9 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { } } received_cred.expect("no creds received"); - assert_eq!(msg.bytes, 5); + assert_eq!(msg.bytes(), 5); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); } @@ -1516,7 +1552,9 @@ fn test_impl_scm_credentials_and_rights(mut space: Vec) { // Test creating and using named unix domain sockets #[test] pub fn test_named_unixdomain() { - use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr}; + use nix::sys::socket::{ + accept, bind, connect, listen, socket, UnixAddress, + }; use nix::sys::socket::{SockFlag, SockType}; use nix::unistd::{read, write}; use std::thread; @@ -1524,25 +1562,25 @@ pub fn test_named_unixdomain() { let tempdir = tempfile::tempdir().unwrap(); let sockname = tempdir.path().join("sock"); let s1 = socket( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, SockFlag::empty(), None, ) .expect("socket failed"); - let sockaddr = UnixAddr::new(&sockname).unwrap(); - bind(s1.as_raw_fd(), &sockaddr).expect("bind failed"); + let sockaddr = UnixAddress::new(&sockname).unwrap(); + bind(s1.as_raw_fd(), sockaddr).expect("bind failed"); listen(&s1, 10).expect("listen failed"); let thr = thread::spawn(move || { let s2 = socket( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, SockFlag::empty(), None, ) .expect("socket failed"); - connect(s2.as_raw_fd(), &sockaddr).expect("connect failed"); + connect(s2.as_raw_fd(), sockaddr).expect("connect failed"); write(&s2, b"hello").expect("write failed"); }); @@ -1563,16 +1601,15 @@ pub fn test_unnamed_unixdomain() { use nix::sys::socket::{SockFlag, SockType}; let (fd_1, _fd_2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), ) .expect("socketpair failed"); - let addr_1: UnixAddr = - getsockname(fd_1.as_raw_fd()).expect("getsockname failed"); - assert!(addr_1.is_unnamed()); + let addr_1 = getsockname(fd_1.as_raw_fd()).expect("getsockname failed"); + assert!(addr_1.to_unix().unwrap().is_unnamed()); } // Test creating and using unnamed unix domain addresses for autobinding sockets @@ -1583,7 +1620,7 @@ pub fn test_unnamed_unixdomain_autobind() { use nix::sys::socket::{SockFlag, SockType}; let fd = socket( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, SockFlag::empty(), None, @@ -1592,11 +1629,11 @@ pub fn test_unnamed_unixdomain_autobind() { // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the // socket is autobound to an abstract address" - bind(fd.as_raw_fd(), &UnixAddr::new_unnamed()).expect("bind failed"); + bind(fd.as_raw_fd(), UnixAddress::new_unnamed()).expect("bind failed"); + + let addr = getsockname(fd.as_raw_fd()).expect("getsockname failed"); - let addr: UnixAddr = - getsockname(fd.as_raw_fd()).expect("getsockname failed"); - let addr = addr.as_abstract().unwrap(); + let addr = addr.to_unix().unwrap().as_abstract().unwrap(); // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2 // (as of 2022-11) @@ -1604,25 +1641,30 @@ pub fn test_unnamed_unixdomain_autobind() { } // Test creating and using named system control sockets -#[cfg(apple_targets)] +#[cfg(any(target_os = "macos", target_os = "ios"))] #[test] pub fn test_syscontrol() { use nix::errno::Errno; use nix::sys::socket::{ - socket, SockFlag, SockProtocol, SockType, SysControlAddr, + socket, AddressFamily, SockFlag, SockProtocol, SockType, + SysControlAddress, }; let fd = socket( - AddressFamily::System, + AddressFamily::SYSTEM, SockType::Datagram, SockFlag::empty(), SockProtocol::KextControl, ) .expect("socket failed"); - SysControlAddr::from_name(fd.as_raw_fd(), "com.apple.net.utun_control", 0) - .expect("resolving sys_control name failed"); + SysControlAddress::from_name( + fd.as_raw_fd(), + "com.apple.net.utun_control", + 0, + ) + .expect("resolving sys_control name failed"); assert_eq!( - SysControlAddr::from_name(fd.as_raw_fd(), "foo.bar.lol", 0).err(), + SysControlAddress::from_name(fd.as_raw_fd(), "foo.bar.lol", 0).err(), Some(Errno::ENOENT) ); @@ -1633,41 +1675,30 @@ pub fn test_syscontrol() { #[cfg(any( target_os = "android", target_os = "freebsd", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] fn loopback_address( + ifaces: &nix::ifaddrs::InterfaceAddresses, family: AddressFamily, -) -> Option { - use nix::ifaddrs::getifaddrs; +) -> Option> { use nix::net::if_::*; - use nix::sys::socket::SockaddrLike; - use std::io; - use std::io::Write; - - let mut addrs = match getifaddrs() { - Ok(iter) => iter, - Err(e) => { - let stdioerr = io::stderr(); - let mut handle = stdioerr.lock(); - writeln!(handle, "getifaddrs: {e:?}").unwrap(); - return None; - } - }; + // return first address matching family - addrs.find(|ifaddr| { + ifaces.iter().find(|ifaddr| { ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK) - && ifaddr.address.as_ref().and_then(SockaddrLike::family) - == Some(family) + && ifaddr.address.as_ref().map(RawAddr::family) == Some(family) }) } #[cfg(any( target_os = "android", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", ))] // qemu doesn't seem to be emulating this correctly in these architectures @@ -1676,9 +1707,7 @@ fn loopback_address( qemu, any( target_arch = "mips", - target_arch = "mips32r6", target_arch = "mips64", - target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -1686,14 +1715,18 @@ fn loopback_address( )] #[test] pub fn test_recv_ipv4pktinfo() { + use nix::ifaddrs::getifaddrs; use nix::net::if_::*; use nix::sys::socket::sockopt::Ipv4PacketInfo; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let lo_ifaddr = loopback_address(AddressFamily::Inet); + let ifaddrs = match getifaddrs().ok() { + Some(x) => x, + None => return, + }; + + let lo_ifaddr = loopback_address(&ifaddrs, AddressFamily::INET); let (lo_name, lo) = match lo_ifaddr { Some(ifaddr) => ( ifaddr.interface_name, @@ -1702,15 +1735,14 @@ pub fn test_recv_ipv4pktinfo() { None => return, }; let receive = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - bind(receive.as_raw_fd(), &lo).expect("bind failed"); - let sa: SockaddrIn = - getsockname(receive.as_raw_fd()).expect("getsockname failed"); + bind(receive.as_raw_fd(), lo.to_ipv4().unwrap()).expect("bind failed"); + let sa = getsockname(receive.as_raw_fd()).expect("getsockname failed"); setsockopt(&receive, Ipv4PacketInfo, &true).expect("setsockopt failed"); { @@ -1718,33 +1750,40 @@ pub fn test_recv_ipv4pktinfo() { let iov = [IoSlice::new(&slice)]; let send = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); + sendmsg( + send.as_raw_fd(), + sa, + &iov, + CmsgStr::empty(), + MsgFlags::empty(), + ) + .expect("sendmsg failed"); } { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::in_pktinfo); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![Ipv4PacketInfo]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .expect("recvmsg failed"); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = cmsg.iter(); if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next() { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); @@ -1755,14 +1794,15 @@ pub fn test_recv_ipv4pktinfo() { ); } assert!(cmsgs.next().is_none(), "unexpected additional control msg"); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); } } #[cfg(any( target_os = "freebsd", - apple_targets, + target_os = "ios", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -1772,9 +1812,7 @@ pub fn test_recv_ipv4pktinfo() { qemu, any( target_arch = "mips", - target_arch = "mips32r6", target_arch = "mips64", - target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -1782,14 +1820,18 @@ pub fn test_recv_ipv4pktinfo() { )] #[test] pub fn test_recvif() { + use nix::ifaddrs::getifaddrs; use nix::net::if_::*; use nix::sys::socket::sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf}; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let lo_ifaddr = loopback_address(AddressFamily::Inet); + let ifaddrs = match getifaddrs().ok() { + Some(x) => x, + None => return, + }; + + let lo_ifaddr = loopback_address(&ifaddrs, AddressFamily::INET); let (lo_name, lo) = match lo_ifaddr { Some(ifaddr) => ( ifaddr.interface_name, @@ -1798,15 +1840,14 @@ pub fn test_recvif() { None => return, }; let receive = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - bind(receive.as_raw_fd(), &lo).expect("bind failed"); - let sa: SockaddrIn = - getsockname(receive.as_raw_fd()).expect("getsockname failed"); + bind(receive.as_raw_fd(), lo.to_ipv4().unwrap()).expect("bind failed"); + let sa = getsockname(receive.as_raw_fd()).expect("getsockname failed"); setsockopt(&receive, Ipv4RecvIf, &true) .expect("setsockopt IP_RECVIF failed"); setsockopt(&receive, Ipv4RecvDstAddr, &true) @@ -1817,35 +1858,42 @@ pub fn test_recvif() { let iov = [IoSlice::new(&slice)]; let send = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); + sendmsg( + send.as_raw_fd(), + sa, + &iov, + CmsgStr::empty(), + MsgFlags::empty(), + ) + .expect("sendmsg failed"); } { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![Ipv4RecvIf, Ipv4RecvDstAddr]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .expect("recvmsg failed"); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs"); + assert_eq!(cmsg.iter().count(), 2, "expected 2 cmsgs"); let mut rx_recvif = false; let mut rx_recvdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { match cmsg { ControlMessageOwned::Ipv4RecvIf(dl) => { rx_recvif = true; @@ -1859,11 +1907,11 @@ pub fn test_recvif() { } ControlMessageOwned::Ipv4RecvDstAddr(addr) => { rx_recvdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in() { - assert_eq!(sin.as_ref().sin_addr.s_addr, + if let Some(sin) = lo.to_ipv4() { + assert_eq!(<_ as AsRef>::as_ref(&sin).sin_addr.s_addr, addr.s_addr, "unexpected destination address (expected {}, got {})", - sin.as_ref().sin_addr.s_addr, + <_ as AsRef>::as_ref(&sin).sin_addr.s_addr, addr.s_addr); } else { panic!("unexpected Sockaddr"); @@ -1874,7 +1922,7 @@ pub fn test_recvif() { } assert!(rx_recvif); assert!(rx_recvdstaddr); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); } } @@ -1883,13 +1931,17 @@ pub fn test_recvif() { #[cfg_attr(qemu, ignore)] #[test] pub fn test_recvif_ipv4() { + use nix::ifaddrs::getifaddrs; use nix::sys::socket::sockopt::Ipv4OrigDstAddr; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let lo_ifaddr = loopback_address(AddressFamily::Inet); + let ifaddrs = match getifaddrs().ok() { + Some(x) => x, + None => return, + }; + + let lo_ifaddr = loopback_address(&ifaddrs, AddressFamily::INET); let (_lo_name, lo) = match lo_ifaddr { Some(ifaddr) => ( ifaddr.interface_name, @@ -1898,15 +1950,14 @@ pub fn test_recvif_ipv4() { None => return, }; let receive = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - bind(receive.as_raw_fd(), &lo).expect("bind failed"); - let sa: SockaddrIn = - getsockname(receive.as_raw_fd()).expect("getsockname failed"); + bind(receive.as_raw_fd(), lo.to_ipv4().unwrap()).expect("bind failed"); + let sa = getsockname(receive.as_raw_fd()).expect("getsockname failed"); setsockopt(&receive, Ipv4OrigDstAddr, &true) .expect("setsockopt IP_ORIGDSTADDR failed"); @@ -1915,42 +1966,49 @@ pub fn test_recvif_ipv4() { let iov = [IoSlice::new(&slice)]; let send = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); + sendmsg( + send.as_raw_fd(), + sa, + &iov, + CmsgStr::empty(), + MsgFlags::empty(), + ) + .expect("sendmsg failed"); } { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_in); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![Ipv4OrigDstAddr]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .expect("recvmsg failed"); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); + assert_eq!(cmsg.iter().count(), 1, "expected 1 cmsgs"); let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { match cmsg { ControlMessageOwned::Ipv4OrigDstAddr(addr) => { rx_recvorigdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in() { - assert_eq!(sin.as_ref().sin_addr.s_addr, + if let Some(sin) = lo.to_ipv4() { + assert_eq!(<_ as AsRef>::as_ref(&sin).sin_addr.s_addr, addr.sin_addr.s_addr, "unexpected destination address (expected {}, got {})", - sin.as_ref().sin_addr.s_addr, + <_ as AsRef>::as_ref(&sin).sin_addr.s_addr, addr.sin_addr.s_addr); } else { panic!("unexpected Sockaddr"); @@ -1960,7 +2018,7 @@ pub fn test_recvif_ipv4() { } } assert!(rx_recvorigdstaddr); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); } } @@ -1969,13 +2027,17 @@ pub fn test_recvif_ipv4() { #[cfg_attr(qemu, ignore)] #[test] pub fn test_recvif_ipv6() { + use nix::ifaddrs::getifaddrs; use nix::sys::socket::sockopt::Ipv6OrigDstAddr; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let lo_ifaddr = loopback_address(AddressFamily::Inet6); + let ifaddrs = match getifaddrs().ok() { + Some(x) => x, + None => return, + }; + + let lo_ifaddr = loopback_address(&ifaddrs, AddressFamily::INET6); let (_lo_name, lo) = match lo_ifaddr { Some(ifaddr) => ( ifaddr.interface_name, @@ -1984,15 +2046,14 @@ pub fn test_recvif_ipv6() { None => return, }; let receive = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - bind(receive.as_raw_fd(), &lo).expect("bind failed"); - let sa: SockaddrIn6 = - getsockname(receive.as_raw_fd()).expect("getsockname failed"); + bind(receive.as_raw_fd(), lo.to_ipv6().unwrap()).expect("bind failed"); + let sa = getsockname(receive.as_raw_fd()).expect("getsockname failed"); setsockopt(&receive, Ipv6OrigDstAddr, &true) .expect("setsockopt IP_ORIGDSTADDR failed"); @@ -2001,42 +2062,49 @@ pub fn test_recvif_ipv6() { let iov = [IoSlice::new(&slice)]; let send = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); + sendmsg( + send.as_raw_fd(), + sa, + &iov, + CmsgStr::empty(), + MsgFlags::empty(), + ) + .expect("sendmsg failed"); } { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::sockaddr_in6); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![Ipv6OrigDstAddr]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .expect("recvmsg failed"); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs"); + assert_eq!(cmsg.iter().count(), 1, "expected 1 cmsgs"); let mut rx_recvorigdstaddr = false; - for cmsg in msg.cmsgs() { + for cmsg in cmsg.iter() { match cmsg { ControlMessageOwned::Ipv6OrigDstAddr(addr) => { rx_recvorigdstaddr = true; - if let Some(sin) = lo.as_sockaddr_in6() { - assert_eq!(sin.as_ref().sin6_addr.s6_addr, + if let Some(sin) = lo.to_ipv6() { + assert_eq!(<_ as AsRef>::as_ref(&sin).sin6_addr.s6_addr, addr.sin6_addr.s6_addr, "unexpected destination address (expected {:?}, got {:?})", - sin.as_ref().sin6_addr.s6_addr, + <_ as AsRef>::as_ref(&sin).sin6_addr.s6_addr, addr.sin6_addr.s6_addr); } else { panic!("unexpected Sockaddr"); @@ -2046,7 +2114,7 @@ pub fn test_recvif_ipv6() { } } assert!(rx_recvorigdstaddr); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); } } @@ -2054,8 +2122,9 @@ pub fn test_recvif_ipv6() { #[cfg(any( target_os = "android", target_os = "freebsd", - apple_targets, + target_os = "ios", target_os = "linux", + target_os = "macos", target_os = "netbsd", target_os = "openbsd", ))] @@ -2065,9 +2134,7 @@ pub fn test_recvif_ipv6() { qemu, any( target_arch = "mips", - target_arch = "mips32r6", target_arch = "mips64", - target_arch = "mips64r6", target_arch = "powerpc64", ) ), @@ -2075,14 +2142,18 @@ pub fn test_recvif_ipv6() { )] #[test] pub fn test_recv_ipv6pktinfo() { + use nix::ifaddrs::getifaddrs; use nix::net::if_::*; use nix::sys::socket::sockopt::Ipv6RecvPacketInfo; - use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6}; - use nix::sys::socket::{getsockname, setsockopt, socket}; - use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags}; + use nix::sys::socket::*; use std::io::{IoSlice, IoSliceMut}; - let lo_ifaddr = loopback_address(AddressFamily::Inet6); + let ifaddrs = match getifaddrs().ok() { + Some(x) => x, + None => return, + }; + + let lo_ifaddr = loopback_address(&ifaddrs, AddressFamily::INET6); let (lo_name, lo) = match lo_ifaddr { Some(ifaddr) => ( ifaddr.interface_name, @@ -2091,15 +2162,14 @@ pub fn test_recv_ipv6pktinfo() { None => return, }; let receive = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("receive socket failed"); - bind(receive.as_raw_fd(), &lo).expect("bind failed"); - let sa: SockaddrIn6 = - getsockname(receive.as_raw_fd()).expect("getsockname failed"); + bind(receive.as_raw_fd(), lo.to_ipv6().unwrap()).expect("bind failed"); + let sa = getsockname(receive.as_raw_fd()).expect("getsockname failed"); setsockopt(&receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed"); { @@ -2107,33 +2177,40 @@ pub fn test_recv_ipv6pktinfo() { let iov = [IoSlice::new(&slice)]; let send = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, ) .expect("send socket failed"); - sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa)) - .expect("sendmsg failed"); + sendmsg( + send.as_raw_fd(), + sa, + &iov, + CmsgStr::empty(), + MsgFlags::empty(), + ) + .expect("sendmsg failed"); } { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut space = cmsg_space!(libc::in6_pktinfo); - let msg = recvmsg::<()>( + let mut cmsg = cmsg_buf![Ipv6PacketInfo]; + + let msg = recvmsg( receive.as_raw_fd(), &mut iovec, - Some(&mut space), + cmsg.handle(), MsgFlags::empty(), ) .expect("recvmsg failed"); assert!(!msg - .flags + .flags() .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC)); - let mut cmsgs = msg.cmsgs(); + let mut cmsgs = cmsg.iter(); if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next() { let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex"); @@ -2144,7 +2221,7 @@ pub fn test_recv_ipv6pktinfo() { ); } assert!(cmsgs.next().is_none(), "unexpected additional control msg"); - assert_eq!(msg.bytes, 8); + assert_eq!(msg.bytes(), 8); assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]); } } @@ -2152,84 +2229,52 @@ pub fn test_recv_ipv6pktinfo() { #[cfg(any(target_os = "android", target_os = "linux"))] #[test] pub fn test_vsock() { - use nix::sys::socket::SockaddrLike; - use nix::sys::socket::{AddressFamily, VsockAddr}; - use std::mem; + use nix::sys::socket::VsockAddress; let port: u32 = 3000; - let addr_local = VsockAddr::new(libc::VMADDR_CID_LOCAL, port); + let addr_local = VsockAddress::new(libc::VMADDR_CID_LOCAL, port); assert_eq!(addr_local.cid(), libc::VMADDR_CID_LOCAL); assert_eq!(addr_local.port(), port); - let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); + let addr_any = + VsockAddress::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY); assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY); assert_ne!(addr_local, addr_any); assert_ne!(calculate_hash(&addr_local), calculate_hash(&addr_any)); - let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); - let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + let addr1 = VsockAddress::new(libc::VMADDR_CID_HOST, port); + let addr2 = VsockAddress::new(libc::VMADDR_CID_HOST, port); assert_eq!(addr1, addr2); assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); - - let addr3 = unsafe { - VsockAddr::from_raw( - addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr, - Some(mem::size_of::().try_into().unwrap()), - ) - } - .unwrap(); - assert_eq!( - addr3.as_ref().svm_family, - AddressFamily::Vsock as libc::sa_family_t - ); - assert_eq!(addr3.as_ref().svm_cid, addr1.cid()); - assert_eq!(addr3.as_ref().svm_port, addr1.port()); } #[cfg(target_os = "macos")] #[test] pub fn test_vsock() { - use nix::sys::socket::SockaddrLike; - use nix::sys::socket::{AddressFamily, VsockAddr}; - use std::mem; + use nix::sys::socket::VsockAddress; let port: u32 = 3000; // macOS doesn't have a VMADDR_CID_LOCAL, so test with host again - let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port); + let addr_host = VsockAddress::new(libc::VMADDR_CID_HOST, port); assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST); assert_eq!(addr_host.port(), port); - let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); + let addr_any = + VsockAddress::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY); assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY); assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY); assert_ne!(addr_host, addr_any); assert_ne!(calculate_hash(&addr_host), calculate_hash(&addr_any)); - let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port); - let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port); + let addr1 = VsockAddress::new(libc::VMADDR_CID_HOST, port); + let addr2 = VsockAddress::new(libc::VMADDR_CID_HOST, port); assert_eq!(addr1, addr2); assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2)); - - let addr3 = unsafe { - VsockAddr::from_raw( - addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr, - Some(mem::size_of::().try_into().unwrap()), - ) - } - .unwrap(); - assert_eq!( - addr3.as_ref().svm_family, - AddressFamily::Vsock as libc::sa_family_t - ); - let cid = addr3.as_ref().svm_cid; - let port = addr3.as_ref().svm_port; - assert_eq!(cid, addr1.cid()); - assert_eq!(port, addr1.port()); } // Disable the test on emulated platforms because it fails in Cirrus-CI. Lack @@ -2239,44 +2284,46 @@ pub fn test_vsock() { #[test] fn test_recvmsg_timestampns() { use nix::sys::socket::*; - use nix::sys::time::*; use std::io::{IoSlice, IoSliceMut}; use std::time::*; // Set up let message = "Ohayō!".as_bytes(); let in_socket = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); setsockopt(&in_socket, sockopt::ReceiveTimestampns, &true).unwrap(); - let localhost = SockaddrIn::new(127, 0, 0, 1, 0); - bind(in_socket.as_raw_fd(), &localhost).unwrap(); - let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap(); + let localhost = Ipv4Address::new(127, 0, 0, 1, 0); + bind(in_socket.as_raw_fd(), localhost).unwrap(); + let address = 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(); + let l = sendmsg( + in_socket.as_raw_fd(), + address, + &iov, + CmsgStr::empty(), + flags, + ) + .unwrap() + .bytes(); assert_eq!(message.len(), l); // Receive the message let mut buffer = vec![0u8; message.len()]; - let mut cmsgspace = nix::cmsg_space!(TimeSpec); + let mut cmsg = cmsg_buf![ScmTimestampns]; let mut iov = [IoSliceMut::new(&mut buffer)]; - let r = recvmsg::<()>( - in_socket.as_raw_fd(), - &mut iov, - Some(&mut cmsgspace), - flags, - ) - .unwrap(); - let rtime = match r.cmsgs().next() { + + let _ = + recvmsg(in_socket.as_raw_fd(), &mut iov, cmsg.handle(), flags).unwrap(); + let rtime = match cmsg.iter().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), None => panic!("No control message"), @@ -2298,46 +2345,54 @@ fn test_recvmsg_timestampns() { #[test] fn test_recvmmsg_timestampns() { use nix::sys::socket::*; - use nix::sys::time::*; use std::io::{IoSlice, IoSliceMut}; use std::time::*; // Set up let message = "Ohayō!".as_bytes(); let in_socket = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); setsockopt(&in_socket, sockopt::ReceiveTimestampns, &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(); + let localhost = Ipv4Address::from_str("127.0.0.1:0").unwrap(); + bind(in_socket.as_raw_fd(), localhost).unwrap(); + let address = 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(); + let l = sendmsg( + in_socket.as_raw_fd(), + address, + &iov, + CmsgStr::empty(), + flags, + ) + .unwrap() + .bytes(); assert_eq!(message.len(), l); // Receive the message let mut buffer = vec![0u8; message.len()]; - let cmsgspace = nix::cmsg_space!(TimeSpec); let mut iov = vec![[IoSliceMut::new(&mut buffer)]]; - let mut data = MultiHeaders::preallocate(1, Some(cmsgspace)); - let r: Vec> = recvmmsg( + let mut headers = RecvMmsgHeaders::with_capacity(1); + + let mut cmsgs = [cmsg_buf![ScmTimestampns]]; + + let _ = recvmmsg( in_socket.as_raw_fd(), - &mut data, - iov.iter_mut(), + &mut headers, + iov.iter_mut().zip(cmsgs.iter_mut().map(CmsgBuf::handle)), flags, None, ) - .unwrap() - .collect(); - let rtime = match r[0].cmsgs().next() { + .unwrap(); + + let rtime = match cmsgs[0].iter().next() { Some(ControlMessageOwned::ScmTimestampns(rtime)) => rtime, Some(_) => panic!("Unexpected control message"), None => panic!("No control message"), @@ -2367,25 +2422,25 @@ fn test_recvmsg_rxq_ovfl() { let bufsize = message.len() * 2; let in_socket = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); let out_socket = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap(); - bind(in_socket.as_raw_fd(), &localhost).unwrap(); + let localhost = Ipv4Address::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(); - connect(out_socket.as_raw_fd(), &address).unwrap(); + let address = getsockname(in_socket.as_raw_fd()).unwrap(); + connect(out_socket.as_raw_fd(), address).unwrap(); // Set SO_RXQ_OVFL flag. setsockopt(&in_socket, RxqOvfl, &1).unwrap(); @@ -2404,30 +2459,31 @@ fn test_recvmsg_rxq_ovfl() { for _ in 0..3 { let l = sendmsg( out_socket.as_raw_fd(), + address, &iov, - &[], + CmsgStr::empty(), flags, - Some(&address), ) - .unwrap(); + .unwrap() + .bytes(); assert_eq!(message.len(), l); } // Receive the message and check the drop counter if any. loop { let mut buffer = vec![0u8; message.len()]; - let mut cmsgspace = nix::cmsg_space!(u32); + let mut cmsg = cmsg_buf![RxqOvfl]; let mut iov = [IoSliceMut::new(&mut buffer)]; - match recvmsg::<()>( + match recvmsg( in_socket.as_raw_fd(), &mut iov, - Some(&mut cmsgspace), + cmsg.handle(), MsgFlags::MSG_DONTWAIT, ) { - Ok(r) => { - drop_counter = match r.cmsgs().next() { + Ok(_) => { + drop_counter = match cmsg.iter().next() { Some(ControlMessageOwned::RxqOvfl(drop_counter)) => { drop_counter } @@ -2471,9 +2527,9 @@ mod linux_errqueue { PortUnreach = 3, // ICMP_PORT_UNREACH } - test_recverr_impl::( + test_recverr_impl::<_, _>( "127.0.0.1:6800", - AddressFamily::Inet, + AddressFamily::INET, sockopt::Ipv4RecvErr, libc::SO_EE_ORIGIN_ICMP, IcmpTypes::DestUnreach as u8, @@ -2486,7 +2542,10 @@ mod linux_errqueue { { if let Some(origin) = err_addr { // Validate that our network error originated from 127.0.0.1:0. - assert_eq!(origin.sin_family, AddressFamily::Inet as _); + assert_eq!( + origin.sin_family, + AddressFamily::INET.family() as _ + ); assert_eq!( origin.sin_addr.s_addr, u32::from_be(0x7f000001) @@ -2519,9 +2578,9 @@ mod linux_errqueue { PortUnreach = 4, // ICMPV6_PORT_UNREACH } - test_recverr_impl::( + test_recverr_impl::<_, _>( "[::1]:6801", - AddressFamily::Inet6, + AddressFamily::INET6, sockopt::Ipv6RecvErr, libc::SO_EE_ORIGIN_ICMP6, IcmpV6Types::DestUnreach as u8, @@ -2536,7 +2595,7 @@ mod linux_errqueue { // Validate that our network error originated from localhost:0. assert_eq!( origin.sin6_family, - AddressFamily::Inet6 as _ + AddressFamily::INET6.family() as _ ); assert_eq!( origin.sin6_addr.s6_addr, @@ -2554,7 +2613,7 @@ mod linux_errqueue { ) } - fn test_recverr_impl( + fn test_recverr_impl( sa: &str, af: AddressFamily, opt: OPT, @@ -2571,14 +2630,14 @@ mod linux_errqueue { const MESSAGE_CONTENTS: &str = "ABCDEF"; let std_sa = std::net::SocketAddr::from_str(sa).unwrap(); - let sock_addr = SockaddrStorage::from(std_sa); + let sock_addr = Address::from(std_sa); let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None) .unwrap(); setsockopt(&sock, opt, &true).unwrap(); if let Err(e) = sendto( sock.as_raw_fd(), MESSAGE_CONTENTS.as_bytes(), - &sock_addr, + sock_addr, MsgFlags::empty(), ) { assert_eq!(e, Errno::EADDRNOTAVAIL); @@ -2588,25 +2647,30 @@ mod linux_errqueue { let mut buf = [0u8; 8]; let mut iovec = [IoSliceMut::new(&mut buf)]; - let mut cspace = cmsg_space!(libc::sock_extended_err, SA); + + let mut cmsg = if af == AddressFamily::INET6 { + cmsg_buf![Ipv6RecvErr] + } else { + cmsg_buf![Ipv4RecvErr] + }; let msg = recvmsg( sock.as_raw_fd(), &mut iovec, - Some(&mut cspace), + cmsg.handle(), MsgFlags::MSG_ERRQUEUE, ) .unwrap(); // The sent message / destination associated with the error is returned: - assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len()); + assert_eq!(msg.bytes(), MESSAGE_CONTENTS.as_bytes().len()); // recvmsg(2): "The original destination address of the datagram that caused the error is // supplied via msg_name;" however, this is not literally true. E.g., an earlier version // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into // 127.0.0.1 (::1). - assert_eq!(msg.address, Some(sock_addr)); + assert_eq!(msg.address(), sock_addr); // Check for expected control message. - let ext_err = match msg.cmsgs().next() { + let ext_err = match cmsg.iter().next() { Some(cmsg) => testf(&cmsg), None => panic!("No control message"), }; @@ -2620,7 +2684,7 @@ mod linux_errqueue { // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors. assert_eq!(ext_err.ee_info, 0); - let bytes = msg.bytes; + let bytes = msg.bytes(); assert_eq!(&buf[..bytes], MESSAGE_CONTENTS.as_bytes()); } } @@ -2631,19 +2695,16 @@ mod linux_errqueue { #[cfg(target_os = "linux")] #[test] pub fn test_txtime() { - use nix::sys::socket::{ - bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage, - MsgFlags, SockFlag, SockType, SockaddrIn, - }; + use nix::sys::socket::*; use nix::sys::time::TimeValLike; use nix::time::{clock_gettime, ClockId}; require_kernel_version!(test_txtime, ">= 5.8"); - let sock_addr = SockaddrIn::from_str("127.0.0.1:6802").unwrap(); + let sock_addr = Ipv4Address::from_str("127.0.0.1:6802").unwrap(); let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -2657,13 +2718,13 @@ pub fn test_txtime() { setsockopt(&ssock, sockopt::TxTime, &txtime_cfg).unwrap(); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); let sbuf = [0u8; 2048]; let iov1 = [std::io::IoSlice::new(&sbuf)]; @@ -2672,20 +2733,28 @@ pub fn test_txtime() { let delay = std::time::Duration::from_secs(1).into(); let txtime = (now + delay).num_nanoseconds() as u64; - let cmsg = ControlMessage::TxTime(&txtime); + let cmsg = [ControlMessage::TxTime(&txtime)]; + let cmsg_space = cmsg_space_iter(cmsg.iter().copied()); + let cmsg = CmsgVec::from_iter(cmsg.iter().copied(), cmsg_space).unwrap(); sendmsg( ssock.as_raw_fd(), + sock_addr, &iov1, - &[cmsg], + &cmsg, MsgFlags::empty(), - Some(&sock_addr), ) .unwrap(); let mut rbuf = [0u8; 2048]; let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)]; - recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, None, MsgFlags::empty()) - .unwrap(); + + recvmsg( + rsock.as_raw_fd(), + &mut iov2, + Default::default(), + MsgFlags::empty(), + ) + .unwrap(); } // cfg needed for capability check. @@ -2693,14 +2762,13 @@ pub fn test_txtime() { #[test] fn test_icmp_protocol() { use nix::sys::socket::{ - sendto, socket, AddressFamily, MsgFlags, SockFlag, SockProtocol, - SockType, SockaddrIn, + sendto, socket, Ipv4Address, MsgFlags, SockFlag, SockProtocol, SockType, }; require_capability!("test_icmp_protocol", CAP_NET_RAW); let owned_fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Raw, SockFlag::empty(), SockProtocol::Icmp, @@ -2716,7 +2784,223 @@ fn test_icmp_protocol() { 0x00, 0x00, // Sequence Number ]; - let dest_addr = SockaddrIn::new(127, 0, 0, 1, 0); - sendto(owned_fd.as_raw_fd(), &packet, &dest_addr, MsgFlags::empty()) + let dest_addr = Ipv4Address::new(127, 0, 0, 1, 0); + sendto(owned_fd.as_raw_fd(), &packet, dest_addr, MsgFlags::empty()) .unwrap(); } + +#[test] +fn test_cmsg_vec_write_empty() { + let cmsg = CmsgVec::empty(); + + assert!(cmsg.is_empty()); + + // No allocation has been performed. + assert!(cmsg.capacity() == 0); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_cmsg_vec_write_from_iter() { + use nix::sys::socket::ControlMessage; + + const RXQ: u32 = 22; + + let cmsgs = [ + ControlMessage::ScmRights(&[1, 2, 3]), + ControlMessage::RxqOvfl(&RXQ), + ]; + + let cmsg_space = cmsg_space_iter(cmsgs.iter().copied()); + + let cmsg = CmsgVec::from_iter(cmsgs.iter().copied(), cmsg_space).unwrap(); + + assert_eq!(cmsg.len(), cmsg_space); + assert_eq!(cmsg.capacity(), cmsg_space); + + let (cmsg_without_rxq, rem) = + CmsgVec::from_iter(cmsgs.iter().copied(), cmsg_space - 1).unwrap_err(); + + assert_eq!( + cmsg_without_rxq.len(), + cmsg_space_iter(cmsgs[..1].iter().copied()), + ); + + // No reallocation has been peformed. + assert_eq!(cmsg_without_rxq.capacity(), cmsg_space - 1); + + assert_eq!(rem, cmsgs[1]); + + let cmsg_clone_iter = CmsgVec::from_iter_clone(cmsgs.iter().copied()); + + assert_eq!(cmsg_clone_iter.len(), cmsg_space); + assert_eq!(cmsg_clone_iter.capacity(), cmsg_space); + + assert_eq!(cmsg_clone_iter, cmsg); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_cmsg_vec_write_reserve_and_write_in_place() { + use nix::sys::socket::ControlMessage; + + const RXQ: u32 = 22; + + let cmsgs = [ + ControlMessage::ScmRights(&[1, 2, 3]), + ControlMessage::RxqOvfl(&RXQ), + ]; + + let cmsg_space = cmsg_space_iter(cmsgs.iter().copied()); + + let mut vec = CmsgVec::empty(); + + vec.reserve(cmsg_space); + + let cap = vec.capacity(); + assert!(cap >= cmsg_space); + + vec.write_iter_in_place(cmsgs.iter().copied()).unwrap(); + + assert_eq!(vec.len(), cmsg_space); + assert_eq!(vec.capacity(), cap); + + const TX: u64 = 42; + + let additional_space = cmsg_space_iter(once(ControlMessage::TxTime(&TX))); + + vec.reserve(additional_space); + + let cap = vec.capacity(); + assert!(cap >= cmsg_space + additional_space); + + vec.write_iter_in_place( + cmsgs + .iter() + .copied() + .chain(once(ControlMessage::TxTime(&TX))), + ) + .unwrap(); + + assert_eq!(vec.len(), cmsg_space + additional_space); + assert_eq!(vec.capacity(), cap); +} + +#[test] +#[cfg(target_os = "linux")] +#[cfg_attr(not(panic = "unwind"), igonre = "test requires unwinding")] +fn test_cmsg_vec_write_write_in_place_unwind() { + use std::panic::{self, AssertUnwindSafe}; + + use nix::sys::socket::ControlMessage; + + const RXQ: u32 = 22; + + let cmsgs = [ + ControlMessage::ScmRights(&[1, 2, 3]), + ControlMessage::RxqOvfl(&RXQ), + ]; + + { + let mut vec = CmsgVec::empty(); + + panic::catch_unwind(AssertUnwindSafe(|| { + vec.write_iter_in_place(cmsgs.iter().copied()).unwrap(); + })) + .unwrap_err(); + + assert_eq!(vec.len(), 0); + } + + { + let mut vec = CmsgVec::from_iter_clone(cmsgs[..1].iter().copied()); + + panic::catch_unwind(AssertUnwindSafe(|| { + vec.write_iter_in_place(cmsgs.iter().copied()).unwrap(); + })) + .unwrap_err(); + + assert_eq!(vec.len(), cmsg_space_iter(cmsgs[..1].iter().copied())); + } +} + +#[test] +fn test_cmsg_vec_write_resize() { + use nix::sys::socket::ControlMessage; + + { + let mut cmsg = CmsgVec::empty(); + + cmsg.reserve(10); + assert!(cmsg.capacity() >= 10); + + let cap = cmsg.capacity(); + + cmsg.reserve(cap); + assert_eq!(cmsg.capacity(), cap); + + cmsg.reserve(cmsg.capacity() + 1); + assert!(cmsg.capacity() > cap); + + let cap = cmsg.capacity(); + + cmsg.shrink_to(cap + 1); + assert_eq!(cap, cmsg.capacity()); + } + + { + let mut cmsg = + CmsgVec::from_iter_clone(once(ControlMessage::ScmRights(&[ + 1, 2, 3, + ]))); + + let cap = cmsg.capacity(); + + assert_eq!(cmsg.len(), cap); + + cmsg.reserve(10); + + assert!(cmsg.capacity() >= cap + 10); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_cmsg_vec_write_clone() { + use nix::sys::socket::ControlMessage; + + const RXQ: u32 = 22; + + let cmsgs = [ + ControlMessage::ScmRights(&[1, 2, 3]), + ControlMessage::RxqOvfl(&RXQ), + ]; + + let cmsg = CmsgVec::from_iter_clone(cmsgs); + + let cloned = cmsg.clone(); + + assert_eq!(cmsg, cloned); +} + +#[test] +fn test_cmsg_buf_macro_cap() { + let cmsg = cmsg_buf![ScmRights(3)]; + + assert_eq!(cmsg.capacity(), cmsg_space![ScmRights(3)]); +} + +#[test] +fn cmsg_empty_sanity() { + assert_eq!(CmsgStr::empty().len(), 0); +} + +#[test] +#[cfg(any(target_os = "android", target_os = "linux"))] +fn unix_addr_to_boxed() { + let unix_addr = UnixAddress::new_unnamed(); + + let boxed = unix_addr.to_boxed(); + + assert_eq!(unix_addr, *boxed); +} diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index a3100c99a8..401fd4c558 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -12,12 +12,12 @@ use std::os::unix::io::{AsRawFd, FromRawFd, OwnedFd}; #[test] pub fn test_local_peercred_seqpacket() { use nix::{ - sys::socket::socketpair, + sys::socket::{socketpair, AddressFamily}, unistd::{Gid, Uid}, }; let (fd1, _fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::SeqPacket, None, SockFlag::empty(), @@ -29,16 +29,21 @@ pub fn test_local_peercred_seqpacket() { assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); } -#[cfg(any(target_os = "dragonfly", target_os = "freebsd", apple_targets))] +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "ios" +))] #[test] pub fn test_local_peercred_stream() { use nix::{ - sys::socket::socketpair, + sys::socket::{socketpair, AddressFamily}, unistd::{Gid, Uid}, }; let (fd1, _fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -50,13 +55,13 @@ pub fn test_local_peercred_stream() { assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); } -#[cfg(apple_targets)] +#[cfg(any(target_os = "ios", target_os = "macos"))] #[test] pub fn test_local_peer_pid() { - use nix::sys::socket::socketpair; + use nix::sys::socket::{socketpair, AddressFamily}; let (fd1, _fd2) = socketpair( - AddressFamily::Unix, + AddressFamily::UNIX, SockType::Stream, None, SockFlag::empty(), @@ -74,7 +79,7 @@ fn is_so_mark_functional() { require_capability!("is_so_mark_functional", CAP_NET_ADMIN); let s = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), None, @@ -88,7 +93,7 @@ fn is_so_mark_functional() { #[test] fn test_so_buf() { let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp, @@ -105,22 +110,22 @@ fn test_so_buf() { #[test] fn test_so_tcp_maxseg() { - use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn}; + use nix::sys::socket::{accept, bind, connect, listen, Ipv4Address}; use nix::unistd::write; use std::net::SocketAddrV4; use std::str::FromStr; let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap(); - let sock_addr = SockaddrIn::from(std_sa); + let sock_addr = Ipv4Address::from(std_sa); let rsock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, ) .unwrap(); - bind(rsock.as_raw_fd(), &sock_addr).unwrap(); + bind(rsock.as_raw_fd(), sock_addr).unwrap(); listen(&rsock, 10).unwrap(); let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap(); // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some @@ -138,13 +143,13 @@ fn test_so_tcp_maxseg() { // Connect and check the MSS that was advertised let ssock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, ) .unwrap(); - connect(ssock.as_raw_fd(), &sock_addr).unwrap(); + connect(ssock.as_raw_fd(), sock_addr).unwrap(); let rsess = accept(rsock.as_raw_fd()).unwrap(); let rsess = unsafe { OwnedFd::from_raw_fd(rsess) }; write(&rsess, b"hello").unwrap(); @@ -165,7 +170,7 @@ fn test_so_tcp_maxseg() { #[test] fn test_so_type() { let sockfd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), None, @@ -203,7 +208,7 @@ fn test_tcp_congestion() { use std::ffi::OsString; let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), None, @@ -229,7 +234,7 @@ fn test_bindtodevice() { skip_if_not_root!("test_bindtodevice"); let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), None, @@ -245,7 +250,7 @@ fn test_bindtodevice() { #[test] fn test_so_tcp_keepalive() { let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, @@ -279,7 +284,7 @@ fn test_so_tcp_keepalive() { #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(qemu, ignore)] fn test_get_mtu() { - use nix::sys::socket::{bind, connect, SockaddrIn}; + use nix::sys::socket::{bind, connect, Ipv4Address}; use std::net::SocketAddrV4; use std::str::FromStr; @@ -287,7 +292,7 @@ fn test_get_mtu() { let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap(); let usock = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), SockProtocol::Udp, @@ -295,8 +300,8 @@ fn test_get_mtu() { .unwrap(); // Bind and initiate connection - bind(usock.as_raw_fd(), &SockaddrIn::from(std_sa)).unwrap(); - connect(usock.as_raw_fd(), &SockaddrIn::from(std_sb)).unwrap(); + bind(usock.as_raw_fd(), Ipv4Address::from(std_sa)).unwrap(); + connect(usock.as_raw_fd(), Ipv4Address::from(std_sb)).unwrap(); // Loopback connections have 2^16 - the maximum - MTU assert_eq!(getsockopt(&usock, sockopt::IpMtu), Ok(u16::MAX as i32)) @@ -306,7 +311,7 @@ fn test_get_mtu() { #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] fn test_ttl_opts() { let fd4 = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -315,7 +320,7 @@ fn test_ttl_opts() { setsockopt(&fd4, sockopt::Ipv4Ttl, &1) .expect("setting ipv4ttl on an inet socket should succeed"); let fd6 = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, @@ -326,10 +331,10 @@ fn test_ttl_opts() { } #[test] -#[cfg(apple_targets)] +#[cfg(any(target_os = "ios", target_os = "macos"))] fn test_dontfrag_opts() { let fd4 = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, @@ -341,7 +346,7 @@ fn test_dontfrag_opts() { "unsetting IP_DONTFRAG on an inet stream socket should succeed", ); let fd4d = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Datagram, SockFlag::empty(), None, @@ -356,13 +361,18 @@ fn test_dontfrag_opts() { } #[test] -#[cfg(any(target_os = "android", apple_targets, target_os = "linux",))] +#[cfg(any( + target_os = "android", + target_os = "ios", + target_os = "linux", + target_os = "macos", +))] // Disable the test under emulation because it fails in Cirrus-CI. Lack // of QEMU support is suspected. #[cfg_attr(qemu, ignore)] fn test_v6dontfrag_opts() { let fd6 = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, @@ -375,7 +385,7 @@ fn test_v6dontfrag_opts() { "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed", ); let fd6d = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Datagram, SockFlag::empty(), None, @@ -393,7 +403,7 @@ fn test_v6dontfrag_opts() { #[cfg(target_os = "linux")] fn test_so_priority() { let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, @@ -408,7 +418,7 @@ fn test_so_priority() { #[cfg(target_os = "linux")] fn test_ip_tos() { let fd = socket( - AddressFamily::Inet, + AddressFamily::INET, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp, @@ -426,7 +436,7 @@ fn test_ip_tos() { #[cfg_attr(qemu, ignore)] fn test_ipv6_tclass() { let fd = socket( - AddressFamily::Inet6, + AddressFamily::INET6, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp,