summaryrefslogtreecommitdiffstats
path: root/vendor/nix/src/sys/socket
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /vendor/nix/src/sys/socket
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/nix/src/sys/socket')
-rw-r--r--vendor/nix/src/sys/socket/addr.rs2685
-rw-r--r--vendor/nix/src/sys/socket/mod.rs2465
-rw-r--r--vendor/nix/src/sys/socket/sockopt.rs1470
3 files changed, 6620 insertions, 0 deletions
diff --git a/vendor/nix/src/sys/socket/addr.rs b/vendor/nix/src/sys/socket/addr.rs
new file mode 100644
index 000000000..1783531d4
--- /dev/null
+++ b/vendor/nix/src/sys/socket/addr.rs
@@ -0,0 +1,2685 @@
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "haiku",
+ target_os = "fuchsia",
+ target_os = "aix",
+))]
+#[cfg(feature = "net")]
+pub use self::datalink::LinkAddr;
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+pub use self::vsock::VsockAddr;
+use super::sa_family_t;
+use crate::errno::Errno;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+))]
+use crate::sys::socket::addr::sys_control::SysControlAddr;
+use crate::{NixPath, Result};
+use cfg_if::cfg_if;
+use memoffset::offset_of;
+use std::convert::TryInto;
+use std::ffi::OsStr;
+use std::hash::{Hash, Hasher};
+use std::os::unix::ffi::OsStrExt;
+use std::path::Path;
+use std::{fmt, mem, net, ptr, slice};
+
+/// Convert a std::net::Ipv4Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
+ libc::in_addr {
+ s_addr: u32::from_ne_bytes(addr.octets())
+ }
+}
+
+/// Convert a std::net::Ipv6Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
+ libc::in6_addr {
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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))
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Packet = libc::AF_PACKET,
+ /// KEXT Controls and Notifications
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ System = libc::AF_SYSTEM,
+ /// Amateur radio AX.25 protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ax25 = libc::AF_AX25,
+ /// IPX - Novell protocols
+ #[cfg(not(any(target_os = "aix", target_os = "redox")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bridge = libc::AF_BRIDGE,
+ /// Access to raw ATM PVCs
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Security = libc::AF_SECURITY,
+ /// Key management protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Key = libc::AF_KEY,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ash = libc::AF_ASH,
+ /// Acorn Econet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Econet = libc::AF_ECONET,
+ /// Access to ATM Switched Virtual Circuits
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AtmSvc = libc::AF_ATMSVC,
+ /// Reliable Datagram Sockets (RDS) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Irda = libc::AF_IRDA,
+ /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Wanpipe = libc::AF_WANPIPE,
+ /// Logical link control (IEEE 802.2 LLC) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Llc = libc::AF_LLC,
+ /// InfiniBand native addressing
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ib = libc::AF_IB,
+ /// Multiprotocol Label Switching
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Mpls = libc::AF_MPLS,
+ /// Controller Area Network automotive bus protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Can = libc::AF_CAN,
+ /// TIPC, "cluster domain sockets" protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Tipc = libc::AF_TIPC,
+ /// Bluetooth low-level socket protocol
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "redox",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bluetooth = libc::AF_BLUETOOTH,
+ /// IUCV (inter-user communication vehicle) z/VM protocol for
+ /// hypervisor-guest interaction
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iucv = libc::AF_IUCV,
+ /// Rx, Andrew File System remote procedure call protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxRpc = libc::AF_RXRPC,
+ /// New "modular ISDN" driver interface protocol
+ #[cfg(not(any(
+ target_os = "aix",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Isdn = libc::AF_ISDN,
+ /// Nokia cellular modem IPC/RPC interface
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Phonet = libc::AF_PHONET,
+ /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ieee802154 = libc::AF_IEEE802154,
+ /// Ericsson's Communication CPU to Application CPU interface (CAIF)
+ /// protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Caif = libc::AF_CAIF,
+ /// Interface to kernel crypto API
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Alg = libc::AF_ALG,
+ /// Near field communication
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Nfc = libc::AF_NFC,
+ /// VMWare VSockets protocol for hypervisor-guest interaction.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Vsock = libc::AF_VSOCK,
+ /// ARPANet IMP addresses
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ImpLink = libc::AF_IMPLINK,
+ /// PUP protocols, e.g. BSP
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Pup = libc::AF_PUP,
+ /// MIT CHAOS protocols
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Chaos = libc::AF_CHAOS,
+ /// Novell and Xerox protocol
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ns = libc::AF_NS,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iso = libc::AF_ISO,
+ /// Bell Labs virtual circuit switch ?
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Datakit = libc::AF_DATAKIT,
+ /// CCITT protocols, X.25 etc
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ccitt = libc::AF_CCITT,
+ /// DEC Direct data link interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Dli = libc::AF_DLI,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Lat = libc::AF_LAT,
+ /// NSC Hyperchannel
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Hylink = libc::AF_HYLINK,
+ /// Link layer interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Link = libc::AF_LINK,
+ /// connection-oriented IP, aka ST II
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Coip = libc::AF_COIP,
+ /// Computer Network Technology
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Cnt = libc::AF_CNT,
+ /// Native ATM access
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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<AddressFamily> {
+ 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",
+ target_os = "ios",
+ target_os = "macos",
+ 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
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ sun_len: u8,
+}
+
+// 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)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+ Pathname(&'a Path),
+ Unnamed,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Abstract(&'a [u8]),
+}
+impl<'a> UnixAddrKind<'a> {
+ /// Safety: sun & sun_len must be valid
+ #[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 =
+ sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
+ if path_len == 0 {
+ return Self::Unnamed;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ if sun.sun_path[0] == 0 {
+ let name = slice::from_raw_parts(
+ sun.sun_path.as_ptr().add(1) as *const u8,
+ path_len - 1,
+ );
+ return Self::Abstract(name);
+ }
+ let pathname =
+ slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
+ if pathname.last() == Some(&0) {
+ // A trailing NUL is not considered part of the path, and it does
+ // not need to be included in the addrlen passed to functions like
+ // bind(). However, Linux adds a trailing NUL, even if one was not
+ // originally present, when returning addrs from functions like
+ // getsockname() (the BSDs do not do that). So we need to filter
+ // out any trailing NUL here, so sockaddrs can round-trip through
+ // the kernel and still compare equal.
+ Self::Pathname(Path::new(OsStr::from_bytes(
+ &pathname[0..pathname.len() - 1],
+ )))
+ } else {
+ Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+ }
+ }
+}
+
+impl UnixAddr {
+ /// Create a new sockaddr_un representing a filesystem path.
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms
+ pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> {
+ path.with_nix_path(|cstr| unsafe {
+ let mut ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ ..mem::zeroed()
+ };
+
+ let bytes = cstr.to_bytes();
+
+ if bytes.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
+ }
+
+ let sun_len = (bytes.len()
+ + offset_of!(libc::sockaddr_un, sun_path))
+ .try_into()
+ .unwrap();
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ {
+ ret.sun_len = sun_len;
+ }
+ ptr::copy_nonoverlapping(
+ bytes.as_ptr(),
+ ret.sun_path.as_mut_ptr() as *mut u8,
+ bytes.len(),
+ );
+
+ Ok(UnixAddr::from_raw_parts(ret, sun_len))
+ })?
+ }
+
+ /// Create a new `sockaddr_un` 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"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all platforms
+ pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> {
+ unsafe {
+ let mut ret = libc::sockaddr_un {
+ sun_family: AddressFamily::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();
+
+ // Abstract addresses are represented by sun_path[0] ==
+ // b'\0', so copy starting one byte in.
+ ptr::copy_nonoverlapping(
+ path.as_ptr(),
+ ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
+ path.len(),
+ );
+
+ Ok(UnixAddr::from_raw_parts(ret, sun_len))
+ }
+ }
+
+ /// Create a new `sockaddr_un` representing an "unnamed" unix socket address.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_unnamed() -> UnixAddr {
+ let ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ ..unsafe { mem::zeroed() }
+ };
+
+ let sun_len: u8 =
+ offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap();
+
+ unsafe { UnixAddr::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.
+ ///
+ /// # 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 {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ {
+ UnixAddr { sun, sun_len }
+ } else {
+ assert_eq!(sun_len, sun.sun_len);
+ UnixAddr {sun}
+ }
+ }
+ }
+
+ 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"))]
+ #[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 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 {
+ &self.sun
+ }
+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+ &mut self.sun
+ }
+
+ fn sun_len(&self) -> u8 {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ ))]
+ {
+ self.sun_len
+ } else {
+ self.sun.sun_len
+ }
+ }
+ }
+}
+
+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<libc::socklen_t>,
+ ) -> Option<Self>
+ 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 (*addr).sa_family as i32 != libc::AF_UNIX {
+ return None;
+ }
+ let mut su: libc::sockaddr_un = 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::<libc::sockaddr_un>() as libc::socklen_t
+ );
+ } else {
+ let su_len = len.unwrap_or((*addr).sa_len as libc::socklen_t);
+ }
+ };
+ ptr::copy(addr as *const u8, sup, su_len as usize);
+ Some(Self::from_raw_parts(su, su_len as u8))
+ }
+
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+ }
+
+ 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(())
+ }
+}
+
+impl AsRef<libc::sockaddr_un> for UnixAddr {
+ fn as_ref(&self) -> &libc::sockaddr_un {
+ &self.sun
+ }
+}
+
+#[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(())
+}
+
+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("<unbound UNIX socket>"),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
+ }
+ }
+}
+
+impl PartialEq for UnixAddr {
+ fn eq(&self, other: &UnixAddr) -> bool {
+ self.kind() == other.kind()
+ }
+}
+
+impl Eq for UnixAddr {}
+
+impl Hash for UnixAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ self.kind().hash(s)
+ }
+}
+
+/// Anything that, in C, can be cast back and forth to `sockaddr`.
+///
+/// Most implementors also implement `AsRef<libc::XXX>` 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
+ }
+
+ /// 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<libc::socklen_t>,
+ ) -> Option<Self>
+ 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<AddressFamily> {
+ // 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
+ })
+ }
+
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ 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
+ }
+ }
+ }
+
+ /// Return the available space in the structure
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<Self>() as libc::socklen_t
+ }
+
+ /// 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)
+ }
+}
+
+/// 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 std::error::Error for SocketAddressLengthNotDynamic {}
+
+impl private::SockaddrLikePriv for () {
+ fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+ ptr::null_mut()
+ }
+}
+
+/// `()` can be used in place of a real Sockaddr when no address is expected,
+/// for example for a field of `Option<S> 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()
+ }
+
+ unsafe fn from_raw(
+ _: *const libc::sockaddr,
+ _: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ None
+ }
+
+ fn family(&self) -> Option<AddressFamily> {
+ None
+ }
+
+ fn len(&self) -> libc::socklen_t {
+ 0
+ }
+}
+
+/// An IPv4 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn(libc::sockaddr_in);
+
+#[cfg(feature = "net")]
+impl SockaddrIn {
+ /// Returns the IP address associated with this socket address, in native
+ /// endian.
+ 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.
+ pub fn new(a: u8, b: u8, c: u8, d: u8, port: u16) -> Self {
+ Self(libc::sockaddr_in {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ 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_port: u16::to_be(port),
+ sin_addr: libc::in_addr {
+ s_addr: u32::from_ne_bytes([a, b, c, d]),
+ },
+ sin_zero: unsafe { mem::zeroed() },
+ })
+ }
+
+ /// 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)
+ }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in> for SockaddrIn {
+ fn as_ref(&self) -> &libc::sockaddr_in {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn {
+ 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);
+ write!(
+ f,
+ "{}.{}.{}.{}:{}",
+ ne >> 24,
+ (ne >> 16) & 0xFF,
+ (ne >> 8) & 0xFF,
+ ne & 0xFF,
+ port
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrIn {
+ fn from(addr: net::SocketAddrV4) -> Self {
+ Self(libc::sockaddr_in {
+ #[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"
+ ))]
+ sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: addr.port().to_be(), // network byte order
+ sin_addr: ipv4addr_to_libc(*addr.ip()),
+ ..unsafe { mem::zeroed() }
+ })
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn> for net::SocketAddrV4 {
+ fn from(addr: SockaddrIn) -> Self {
+ net::SocketAddrV4::new(
+ net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()),
+ u16::from_be(addr.0.sin_port),
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV4::from_str(s).map(SockaddrIn::from)
+ }
+}
+
+/// An IPv6 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn6(libc::sockaddr_in6);
+
+#[cfg(feature = "net")]
+impl SockaddrIn6 {
+ /// 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
+ }
+}
+
+#[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<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET6 {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in6> for SockaddrIn6 {
+ fn as_ref(&self) -> &libc::sockaddr_in6 {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn6 {
+ 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<net::SocketAddrV6> for SockaddrIn6 {
+ 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::<libc::sockaddr_in6>() 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<SockaddrIn6> 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")]
+impl std::str::FromStr for SockaddrIn6 {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV6::from_str(s).map(SockaddrIn6::from)
+ }
+}
+
+/// A container for any sockaddr type
+///
+/// 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.
+///
+/// # 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(),
+/// 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());
+/// ```
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
+pub union SockaddrStorage {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ #[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" ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ vsock: VsockAddr,
+}
+impl private::SockaddrLikePriv for SockaddrStorage {}
+impl SockaddrLike for SockaddrStorage {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ l: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ 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::<libc::sockaddr_storage>()
+ {
+ None
+ } else {
+ let mut ss: libc::sockaddr_storage = mem::zeroed();
+ let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
+ 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.
+ (*(&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 (*addr).sa_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => {
+ AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET => {
+ SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => {
+ SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
+ }
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => {
+ 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 => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ libc::AF_SYSTEM => {
+ SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos" ))]
+ libc::AF_VSOCK => {
+ VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
+ }
+ _ => None,
+ }
+ }
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ 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) => {
+ addr.set_length(new_length)
+ },
+ None => Err(SocketAddressLengthNotDynamic),
+ }
+ }
+}
+
+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
+ }
+ }
+ };
+}
+
+impl SockaddrStorage {
+ /// Downcast to an immutable `[UnixAddr]` reference.
+ pub fn as_unix_addr(&self) -> Option<&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::<libc::sockaddr_un>()
+ {
+ 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::<libc::sockaddr_un>()
+ {
+ None
+ } else {
+ Some(unsafe { &mut self.su })
+ }
+ }
+
+ #[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",
+ target_os = "ios",
+ target_os = "macos",
+ 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", any(target_os = "ios", target_os = "macos")))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
+ AddressFamily::System, libc::sockaddr_ctl, sctl}
+
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
+ AddressFamily::Vsock, libc::sockaddr_vm, vsock}
+}
+
+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 fmt::Display for SockaddrStorage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ 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",
+ target_os = "ios",
+ target_os = "macos",
+ 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(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ libc::AF_SYSTEM => self.sctl.fmt(f),
+ libc::AF_UNIX => self.su.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ libc::AF_VSOCK => self.vsock.fmt(f),
+ _ => "<Address family unspecified>".fmt(f),
+ }
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> 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<net::SocketAddrV6> for SockaddrStorage {
+ fn from(s: net::SocketAddrV6) -> Self {
+ unsafe {
+ let mut ss: Self = mem::zeroed();
+ ss.sin6 = SockaddrIn6::from(s);
+ ss
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddr> for SockaddrStorage {
+ fn from(s: net::SocketAddr) -> Self {
+ match s {
+ net::SocketAddr::V4(sa4) => Self::from(sa4),
+ net::SocketAddr::V6(sa6) => Self::from(sa6),
+ }
+ }
+}
+
+impl Hash for SockaddrStorage {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ 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",
+ target_os = "ios",
+ target_os = "macos",
+ 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(any(target_os = "ios", target_os = "macos"))]
+ #[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),
+ }
+ }
+ }
+}
+
+impl PartialEq for SockaddrStorage {
+ fn eq(&self, other: &Self) -> bool {
+ 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",
+ target_os = "ios",
+ target_os = "macos",
+ 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(any(target_os = "ios", target_os = "macos"))]
+ #[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,
+ }
+ }
+ }
+}
+
+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
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod netlink {
+ use super::*;
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{sa_family_t, sockaddr_nl};
+ use std::{fmt, mem};
+
+ /// Address for the Linux kernel user interface device.
+ ///
+ /// # References
+ ///
+ /// [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);
+
+ impl NetlinkAddr {
+ /// Construct a new socket address from its port ID and multicast groups
+ /// mask.
+ pub fn new(pid: u32, groups: u32) -> NetlinkAddr {
+ let mut addr: sockaddr_nl = unsafe { mem::zeroed() };
+ addr.nl_family = AddressFamily::Netlink as sa_family_t;
+ addr.nl_pid = pid;
+ addr.nl_groups = groups;
+
+ NetlinkAddr(addr)
+ }
+
+ /// Return the socket's port ID.
+ pub const fn pid(&self) -> u32 {
+ self.0.nl_pid
+ }
+
+ /// Return the socket's multicast groups mask
+ pub const fn groups(&self) -> u32 {
+ self.0.nl_groups
+ }
+ }
+
+ impl private::SockaddrLikePriv for NetlinkAddr {}
+ impl SockaddrLike for NetlinkAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_NETLINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_nl> for NetlinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_nl {
+ &self.0
+ }
+ }
+
+ impl fmt::Display for NetlinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "pid: {} groups: {}", self.pid(), self.groups())
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod alg {
+ use super::*;
+ use libc::{c_char, sockaddr_alg, AF_ALG};
+ use std::ffi::CStr;
+ use std::hash::{Hash, Hasher};
+ use std::{fmt, mem, str};
+
+ /// 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<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = l {
+ if l != mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t
+ {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_ALG {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_alg> for AlgAddr {
+ fn as_ref(&self) -> &libc::sockaddr_alg {
+ &self.0
+ }
+ }
+
+ // , PartialEq, Eq, Debug, Hash
+ impl PartialEq for AlgAddr {
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ ) == (
+ other.salg_family,
+ &other.salg_type[..],
+ other.salg_feat,
+ other.salg_mask,
+ &other.salg_name[..],
+ )
+ }
+ }
+
+ impl Eq for AlgAddr {}
+
+ impl Hash for AlgAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ )
+ .hash(s);
+ }
+ }
+
+ impl AlgAddr {
+ /// Construct an `AF_ALG` socket from its cipher name and type.
+ pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr {
+ let mut addr: sockaddr_alg = unsafe { mem::zeroed() };
+ addr.salg_family = AF_ALG as u16;
+ addr.salg_type[..alg_type.len()]
+ .copy_from_slice(alg_type.to_string().as_bytes());
+ addr.salg_name[..alg_name.len()]
+ .copy_from_slice(alg_name.to_string().as_bytes());
+
+ AlgAddr(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() as *const c_char)
+ }
+ }
+
+ /// 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() as *const c_char)
+ }
+ }
+ }
+
+ impl fmt::Display for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "type: {} alg: {}",
+ self.alg_name().to_string_lossy(),
+ self.alg_type().to_string_lossy()
+ )
+ }
+ }
+
+ impl fmt::Debug for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+feature! {
+#![feature = "ioctl"]
+#[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::os::unix::io::RawFd;
+ use crate::{Errno, Result};
+ use super::{private, SockaddrLike};
+
+ // FIXME: Move type into `libc`
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ #[allow(missing_debug_implementations)]
+ pub struct ctl_ioc_info {
+ pub ctl_id: u32,
+ pub ctl_name: [c_uchar; MAX_KCTL_NAME],
+ }
+
+ const CTL_IOC_MAGIC: u8 = b'N';
+ const CTL_IOC_INFO: u8 = 3;
+ const MAX_KCTL_NAME: usize = 96;
+
+ ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info);
+
+ /// Apple system control socket
+ ///
+ /// # References
+ ///
+ /// <https://developer.apple.com/documentation/kernel/sockaddr_ctl>
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct SysControlAddr(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<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_SYSTEM {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ctl> for SysControlAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ctl {
+ &self.0
+ }
+ }
+
+ impl SysControlAddr {
+ /// Construct a new `SysControlAddr` from its kernel unique identifier
+ /// and unit number.
+ pub const fn new(id: u32, unit: u32) -> SysControlAddr {
+ let addr = libc::sockaddr_ctl {
+ sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
+ sc_family: AddressFamily::System as c_uchar,
+ ss_sysaddr: libc::AF_SYS_CONTROL as u16,
+ sc_id: id,
+ sc_unit: unit,
+ sc_reserved: [0; 5]
+ };
+
+ SysControlAddr(addr)
+ }
+
+ /// Construct a new `SysControlAddr` from its human readable name and
+ /// unit number.
+ pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
+ if name.len() > MAX_KCTL_NAME {
+ return Err(Errno::ENAMETOOLONG);
+ }
+
+ let mut ctl_name = [0; MAX_KCTL_NAME];
+ ctl_name[..name.len()].clone_from_slice(name.as_bytes());
+ let mut info = ctl_ioc_info { ctl_id: 0, ctl_name };
+
+ unsafe { ctl_info(sockfd, &mut info)?; }
+
+ Ok(SysControlAddr::new(info.ctl_id, unit))
+ }
+
+ /// Return the kernel unique identifier
+ pub const fn id(&self) -> u32 {
+ self.0.sc_id
+ }
+
+ /// Return the kernel controller private unit number.
+ pub const fn unit(&self) -> u32 {
+ self.0.sc_unit
+ }
+ }
+
+ impl fmt::Display for SysControlAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(self, f)
+ }
+ }
+}
+}
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll);
+
+ impl LinkAddr {
+ /// Physical-layer protocol
+ pub fn protocol(&self) -> u16 {
+ self.0.sll_protocol
+ }
+
+ /// Interface number
+ pub fn ifindex(&self) -> usize {
+ self.0.sll_ifindex as usize
+ }
+
+ /// ARP hardware type
+ pub fn hatype(&self) -> u16 {
+ self.0.sll_hatype
+ }
+
+ /// Packet type
+ pub fn pkttype(&self) -> u8 {
+ self.0.sll_pkttype
+ }
+
+ /// Length of MAC address
+ pub fn halen(&self) -> usize {
+ self.0.sll_halen as usize
+ }
+
+ /// Physical-layer address (MAC)
+ // Returns an Option just for cross-platform compatibility
+ pub fn addr(&self) -> Option<[u8; 6]> {
+ Some([
+ self.0.sll_addr[0],
+ self.0.sll_addr[1],
+ self.0.sll_addr[2],
+ self.0.sll_addr[3],
+ self.0.sll_addr[4],
+ self.0.sll_addr[5],
+ ])
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ 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(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_PACKET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ll> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ll {
+ &self.0
+ }
+ }
+
+ }
+}
+
+#[cfg(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"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(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
+ }
+
+ /// Datalink type
+ #[cfg(not(target_os = "haiku"))]
+ pub fn datalink_type(&self) -> u8 {
+ self.0.sdl_type
+ }
+
+ /// MAC address start position
+ pub fn nlen(&self) -> usize {
+ self.0.sdl_nlen as usize
+ }
+
+ /// link level address length
+ pub fn alen(&self) -> usize {
+ self.0.sdl_alen as usize
+ }
+
+ /// link layer selector length
+ #[cfg(not(target_os = "haiku"))]
+ pub fn slen(&self) -> usize {
+ self.0.sdl_slen as usize
+ }
+
+ /// 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();
+
+ alen == 0 || nlen + alen >= data_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;
+
+ 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,
+ ])
+ }
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ 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(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_LINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_dl> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_dl {
+ &self.0
+ }
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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};
+
+ /// Socket address for VMWare VSockets protocol
+ ///
+ /// # References
+ ///
+ /// [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<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_vm>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_VSOCK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_vm> for VsockAddr {
+ fn as_ref(&self) -> &libc::sockaddr_vm {
+ &self.0
+ }
+ }
+
+ impl PartialEq for VsockAddr {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (inner.svm_family, inner.svm_cid, inner.svm_port)
+ == (other.svm_family, other.svm_cid, other.svm_port)
+ }
+ #[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)
+ }
+ }
+
+ impl Eq for VsockAddr {}
+
+ impl Hash for VsockAddr {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s);
+ }
+ #[cfg(target_os = "macos")]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.svm_family, inner.svm_cid, inner.svm_port, inner.svm_len).hash(s);
+ }
+ }
+
+ /// VSOCK Address
+ ///
+ /// 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 {
+ /// Construct a `VsockAddr` from its raw fields.
+ pub fn new(cid: u32, port: u32) -> VsockAddr {
+ let mut addr: sockaddr_vm = unsafe { mem::zeroed() };
+ addr.svm_family = AddressFamily::Vsock as sa_family_t;
+ addr.svm_cid = cid;
+ addr.svm_port = port;
+
+ #[cfg(target_os = "macos")]
+ {
+ addr.svm_len = std::mem::size_of::<sockaddr_vm>() as u8;
+ }
+ VsockAddr(addr)
+ }
+
+ /// Context Identifier (CID)
+ pub fn cid(&self) -> u32 {
+ self.0.svm_cid
+ }
+
+ /// Port number
+ pub fn port(&self) -> u32 {
+ self.0.svm_port
+ }
+ }
+
+ impl fmt::Display for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "cid: {} port: {}", self.cid(), self.port())
+ }
+ }
+
+ impl fmt::Debug for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ mod types {
+ use super::*;
+
+ #[test]
+ fn test_ipv4addr_to_libc() {
+ let s = std::net::Ipv4Addr::new(1, 2, 3, 4);
+ let l = ipv4addr_to_libc(s);
+ assert_eq!(l.s_addr, u32::to_be(0x01020304));
+ }
+
+ #[test]
+ fn test_ipv6addr_to_libc() {
+ let s = std::net::Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
+ let l = ipv6addr_to_libc(&s);
+ assert_eq!(
+ l.s6_addr,
+ [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
+ );
+ }
+ }
+
+ #[cfg(not(target_os = "redox"))]
+ mod link {
+ #![allow(clippy::cast_ptr_alignment)]
+
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos"
+ ))]
+ use super::super::super::socklen_t;
+ use super::*;
+
+ /// Don't panic when trying to display an empty datalink address
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[test]
+ fn test_datalink_display() {
+ use super::super::LinkAddr;
+ use std::mem;
+
+ let la = LinkAddr(libc::sockaddr_dl {
+ sdl_len: 56,
+ sdl_family: 18,
+ sdl_index: 5,
+ sdl_type: 24,
+ sdl_nlen: 3,
+ sdl_alen: 0,
+ sdl_slen: 0,
+ ..unsafe { mem::zeroed() }
+ });
+ 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() as *const libc::sockaddr;
+ 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(any(target_os = "ios", target_os = "macos"))]
+ #[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() as *const libc::sockaddr;
+ 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(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,
+ ];
+ 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).unwrap() };
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+ match sock_addr.as_link_addr() {
+ Some(dl) => {
+ assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176]))
+ }
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(target_os = "illumos")]
+ #[test]
+ fn illumos_tap() {
+ 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) };
+
+ assert!(_sock_addr.is_some());
+
+ let sock_addr = _sock_addr.unwrap();
+
+ assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link);
+
+ assert_eq!(
+ sock_addr.as_link_addr().unwrap().addr(),
+ Some([24u8, 101, 144, 221, 76, 176])
+ );
+ }
+
+ #[test]
+ fn size() {
+ #[cfg(any(
+ target_os = "aix",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd",
+ target_os = "haiku"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_dl>();
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_ll>();
+ assert_eq!(LinkAddr::size() as usize, l);
+ }
+ }
+
+ mod sockaddr_in {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "127.0.0.1:8080";
+ let addr = SockaddrIn::from_str(s).unwrap();
+ assert_eq!(s, format!("{addr}"));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in>(),
+ SockaddrIn::size() as usize
+ );
+ }
+ }
+
+ mod sockaddr_in6 {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+ let addr = SockaddrIn6::from_str(s).unwrap();
+ assert_eq!(s, format!("{addr}"));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in6>(),
+ SockaddrIn6::size() as usize
+ );
+ }
+
+ #[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();
+ nix_sin6.0.sin6_flowinfo = 0x12345678;
+ nix_sin6.0.sin6_scope_id = 0x9abcdef0;
+
+ let std_sin6: std::net::SocketAddrV6 = nix_sin6.into();
+ assert_eq!(nix_sin6, std_sin6.into());
+ }
+ }
+
+ mod sockaddr_storage {
+ use super::*;
+
+ #[test]
+ fn from_sockaddr_un_named() {
+ let ua = UnixAddr::new("/var/run/mysock").unwrap();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ 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() as *const libc::sockaddr;
+ 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() as *const libc::sockaddr;
+ let ss = unsafe { SockaddrStorage::from_raw(ptr, Some(ua.len())) }
+ .unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+ }
+
+ mod unixaddr {
+ 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 sun_path1 =
+ unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+ let sun_path2 = [
+ 0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
+ 116, 101, 115, 116,
+ ];
+ assert_eq!(sun_path1, sun_path2);
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_un>(),
+ UnixAddr::size() as usize
+ );
+ }
+ }
+}
diff --git a/vendor/nix/src/sys/socket/mod.rs b/vendor/nix/src/sys/socket/mod.rs
new file mode 100644
index 000000000..78dd617c5
--- /dev/null
+++ b/vendor/nix/src/sys/socket/mod.rs
@@ -0,0 +1,2465 @@
+//! Socket interface functions
+//!
+//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeSpec;
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeVal;
+use crate::{errno::Errno, Result};
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, size_t, socklen_t};
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+use libc::{
+ iovec, CMSG_DATA, CMSG_FIRSTHDR, CMSG_LEN, CMSG_NXTHDR, CMSG_SPACE,
+};
+#[cfg(not(target_os = "redox"))]
+use std::io::{IoSlice, IoSliceMut};
+#[cfg(feature = "net")]
+use std::net;
+use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, RawFd, OwnedFd};
+use std::{mem, ptr};
+
+#[deny(missing_docs)]
+mod addr;
+#[deny(missing_docs)]
+pub mod sockopt;
+
+/*
+ *
+ * ===== Re-exports =====
+ *
+ */
+
+pub use self::addr::{SockaddrLike, SockaddrStorage};
+
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+pub use self::addr::{AddressFamily, UnixAddr};
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+pub use self::addr::{AddressFamily, UnixAddr};
+#[cfg(not(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+)))]
+#[cfg(feature = "net")]
+pub use self::addr::{LinkAddr, SockaddrIn, SockaddrIn6};
+#[cfg(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku",
+ target_os = "redox",
+))]
+#[cfg(feature = "net")]
+pub use self::addr::{SockaddrIn, SockaddrIn6};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "ioctl")]
+pub use crate::sys::socket::addr::sys_control::SysControlAddr;
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+pub use crate::sys::socket::addr::vsock::VsockAddr;
+
+#[cfg(all(feature = "uio", not(target_os = "redox")))]
+pub use libc::{cmsghdr, msghdr};
+pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
+#[cfg(feature = "net")]
+pub use libc::{sockaddr_in, sockaddr_in6};
+
+#[cfg(feature = "net")]
+use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
+
+/// These constants are used to specify the communication semantics
+/// when creating a socket with [`socket()`](fn.socket.html)
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum SockType {
+ /// Provides sequenced, reliable, two-way, connection-
+ /// based byte streams. An out-of-band data transmission
+ /// mechanism may be supported.
+ Stream = libc::SOCK_STREAM,
+ /// Supports datagrams (connectionless, unreliable
+ /// messages of a fixed maximum length).
+ Datagram = libc::SOCK_DGRAM,
+ /// Provides a sequenced, reliable, two-way connection-
+ /// based data transmission path for datagrams of fixed
+ /// maximum length; a consumer is required to read an
+ /// entire packet with each input system call.
+ SeqPacket = libc::SOCK_SEQPACKET,
+ /// Provides raw network protocol access.
+ #[cfg(not(target_os = "redox"))]
+ Raw = libc::SOCK_RAW,
+ /// Provides a reliable datagram layer that does not
+ /// guarantee ordering.
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ Rdm = libc::SOCK_RDM,
+}
+// The TryFrom impl could've been derived using libc_enum!. But for
+// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
+// keep the old variant names.
+impl TryFrom<i32> for SockType {
+ type Error = crate::Error;
+
+ fn try_from(x: i32) -> Result<Self> {
+ match x {
+ libc::SOCK_STREAM => Ok(Self::Stream),
+ libc::SOCK_DGRAM => Ok(Self::Datagram),
+ libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
+ #[cfg(not(target_os = "redox"))]
+ libc::SOCK_RAW => Ok(Self::Raw),
+ #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
+ libc::SOCK_RDM => Ok(Self::Rdm),
+ _ => Err(Errno::EINVAL),
+ }
+ }
+}
+
+/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
+/// to specify the protocol to use.
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum SockProtocol {
+ /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Tcp = libc::IPPROTO_TCP,
+ /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Udp = libc::IPPROTO_UDP,
+ /// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html))
+ Raw = libc::IPPROTO_RAW,
+ /// Allows applications and other KEXTs to be notified when certain kernel events occur
+ /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ KextEvent = libc::SYSPROTO_EVENT,
+ /// Allows applications to configure and control a KEXT
+ /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ KextControl = libc::SYSPROTO_CONTROL,
+ /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link
+ // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRoute = libc::NETLINK_ROUTE,
+ /// Reserved for user-mode socket protocols
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkUserSock = libc::NETLINK_USERSOCK,
+ /// Query information about sockets of various protocol families from the kernel
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
+ /// Netfilter/iptables ULOG.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkNFLOG = libc::NETLINK_NFLOG,
+ /// SELinux event notifications.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSELinux = libc::NETLINK_SELINUX,
+ /// Open-iSCSI
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkISCSI = libc::NETLINK_ISCSI,
+ /// Auditing
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkAudit = libc::NETLINK_AUDIT,
+ /// Access to FIB lookup from user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
+ /// Netfilter subsystem
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkNetFilter = libc::NETLINK_NETFILTER,
+ /// SCSI Transports
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
+ /// Infiniband RDMA
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRDMA = libc::NETLINK_RDMA,
+ /// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
+ /// DECnet routing messages
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
+ /// Kernel messages to user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
+ /// Generic netlink family for simplified netlink usage.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkGeneric = libc::NETLINK_GENERIC,
+ /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow
+ /// configuration of the kernel crypto API.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkCrypto = libc::NETLINK_CRYPTO,
+ /// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols
+ /// defined in the interface to be received.
+ /// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html))
+ // The protocol number is fed into the socket syscall in network byte order.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EthAll = (libc::ETH_P_ALL as u16).to_be() as i32,
+ /// The Controller Area Network raw socket protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CanRaw = libc::CAN_RAW,
+}
+
+impl SockProtocol {
+ /// The Controller Area Network broadcast manager protocol
+ /// ([ref](https://docs.kernel.org/networking/can.html#how-to-use-socketcan))
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(non_upper_case_globals)]
+ pub const CanBcm: SockProtocol = SockProtocol::NetlinkUserSock; // Matches libc::CAN_BCM
+}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+libc_bitflags! {
+ /// Configuration flags for `SO_TIMESTAMPING` interface
+ ///
+ /// For use with [`Timestamping`][sockopt::Timestamping].
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ pub struct TimestampingFlag: libc::c_uint {
+ /// Report any software timestamps when available.
+ SOF_TIMESTAMPING_SOFTWARE;
+ /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ /// Collect transmitting timestamps as reported by hardware
+ SOF_TIMESTAMPING_TX_HARDWARE;
+ /// Collect transmitting timestamps as reported by software
+ SOF_TIMESTAMPING_TX_SOFTWARE;
+ /// Collect receiving timestamps as reported by hardware
+ SOF_TIMESTAMPING_RX_HARDWARE;
+ /// Collect receiving timestamps as reported by software
+ SOF_TIMESTAMPING_RX_SOFTWARE;
+ /// Generate a unique identifier along with each transmitted packet
+ SOF_TIMESTAMPING_OPT_ID;
+ /// Return transmit timestamps alongside an empty packet instead of the original packet
+ SOF_TIMESTAMPING_OPT_TSONLY;
+ }
+}
+
+libc_bitflags! {
+ /// Additional socket options
+ pub struct SockFlag: c_int {
+ /// Set non-blocking mode on the new socket
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_NONBLOCK;
+ /// Set close-on-exec on the new descriptor
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_CLOEXEC;
+ /// Return `EPIPE` instead of raising `SIGPIPE`
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_NOSIGPIPE;
+ /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)`
+ /// to the DNS port (typically 53)
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_DNS;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for send/recv and their relatives
+ pub struct MsgFlags: c_int {
+ /// Sends or requests out-of-band data on sockets that support this notion
+ /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also
+ /// support out-of-band data.
+ MSG_OOB;
+ /// Peeks at an incoming message. The data is treated as unread and the next
+ /// [`recv()`](fn.recv.html)
+ /// or similar function shall still return this data.
+ MSG_PEEK;
+ /// Receive operation blocks until the full amount of data can be
+ /// returned. The function may return smaller amount of data if a signal
+ /// is caught, an error or disconnect occurs.
+ MSG_WAITALL;
+ /// Enables nonblocking operation; if the operation would block,
+ /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar
+ /// behavior to setting the `O_NONBLOCK` flag
+ /// (via the [`fcntl`](../../fcntl/fn.fcntl.html)
+ /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per-
+ /// call option, whereas `O_NONBLOCK` is a setting on the open file
+ /// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)),
+ /// which will affect all threads in
+ /// the calling process and as well as other processes that hold
+ /// file descriptors referring to the same open file description.
+ #[cfg(not(target_os = "aix"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_DONTWAIT;
+ /// Receive flags: Control Data was discarded (buffer too small)
+ MSG_CTRUNC;
+ /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram
+ /// (since Linux 2.4.27/2.6.8),
+ /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4)
+ /// sockets: return the real length of the packet or datagram, even
+ /// when it was longer than the passed buffer. Not implemented for UNIX
+ /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets.
+ ///
+ /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp).
+ MSG_TRUNC;
+ /// Terminates a record (when this notion is supported, as for
+ /// sockets of type [`SeqPacket`](enum.SockType.html)).
+ MSG_EOR;
+ /// This flag specifies that queued errors should be received from
+ /// the socket error queue. (For more details, see
+ /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_ERRQUEUE;
+ /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain
+ /// file descriptor using the `SCM_RIGHTS` operation (described in
+ /// [unix(7)](https://linux.die.net/man/7/unix)).
+ /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of
+ /// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html).
+ ///
+ /// Only used in [`recvmsg`](fn.recvmsg.html) function.
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_CMSG_CLOEXEC;
+ /// Requests not to send `SIGPIPE` errors when the other end breaks the connection.
+ /// (For more details, see [send(2)](https://linux.die.net/man/2/send)).
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_NOSIGNAL;
+ /// Turns on [`MSG_DONTWAIT`] after the first message has been received (only for
+ /// `recvmmsg()`).
+ #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MSG_WAITFORONE;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SO_PEERCRED` ancillary message
+ /// and the `SCM_CREDENTIALS` control message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::ucred);
+
+ impl UnixCredentials {
+ /// Creates a new instance with the credentials of the current process
+ pub fn new() -> Self {
+ // Safe because these FFI functions are inherently safe
+ unsafe {
+ UnixCredentials(libc::ucred {
+ pid: libc::getpid(),
+ uid: libc::getuid(),
+ gid: libc::getgid()
+ })
+ }
+ }
+
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.pid
+ }
+
+ /// Returns the user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.uid
+ }
+
+ /// Returns the group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.gid
+ }
+ }
+
+ impl Default for UnixCredentials {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ impl From<libc::ucred> for UnixCredentials {
+ fn from(cred: libc::ucred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+
+ impl From<UnixCredentials> for libc::ucred {
+ fn from(uc: UnixCredentials) -> Self {
+ uc.0
+ }
+ }
+ } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::cmsgcred);
+
+ impl UnixCredentials {
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.cmcred_pid
+ }
+
+ /// Returns the real user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cmcred_uid
+ }
+
+ /// Returns the effective user identifier
+ pub fn euid(&self) -> libc::uid_t {
+ self.0.cmcred_euid
+ }
+
+ /// Returns the real group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.cmcred_gid
+ }
+
+ /// Returns a list group identifiers (the first one being the effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ unsafe {
+ std::slice::from_raw_parts(
+ self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
+ self.0.cmcred_ngroups as _
+ )
+ }
+ }
+ }
+
+ impl From<libc::cmsgcred> for UnixCredentials {
+ fn from(cred: libc::cmsgcred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct XuCred(libc::xucred);
+
+ impl XuCred {
+ /// Structure layout version
+ pub fn version(&self) -> u32 {
+ self.0.cr_version
+ }
+
+ /// Effective user ID
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cr_uid
+ }
+
+ /// Returns a list of group identifiers (the first one being the
+ /// effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ &self.0.cr_groups
+ }
+ }
+ }
+}
+
+feature! {
+#![feature = "net"]
+/// Request for multicast socket operations
+///
+/// This is a wrapper type around `ip_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct IpMembershipRequest(libc::ip_mreq);
+
+impl IpMembershipRequest {
+ /// Instantiate a new `IpMembershipRequest`
+ ///
+ /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface.
+ pub fn new(group: net::Ipv4Addr, interface: Option<net::Ipv4Addr>)
+ -> Self
+ {
+ let imr_addr = match interface {
+ None => net::Ipv4Addr::UNSPECIFIED,
+ Some(addr) => addr
+ };
+ IpMembershipRequest(libc::ip_mreq {
+ imr_multiaddr: ipv4addr_to_libc(group),
+ imr_interface: ipv4addr_to_libc(imr_addr)
+ })
+ }
+}
+
+/// Request for ipv6 multicast socket operations
+///
+/// This is a wrapper type around `ipv6_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
+
+impl Ipv6MembershipRequest {
+ /// Instantiate a new `Ipv6MembershipRequest`
+ pub const fn new(group: net::Ipv6Addr) -> Self {
+ Ipv6MembershipRequest(libc::ipv6_mreq {
+ ipv6mr_multiaddr: ipv6addr_to_libc(&group),
+ ipv6mr_interface: 0,
+ })
+ }
+}
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+#![feature = "uio"]
+
+/// Create a buffer large enough for storing some control messages as returned
+/// by [`recvmsg`](fn.recvmsg.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # use nix::sys::time::TimeVal;
+/// # use std::os::unix::io::RawFd;
+/// # fn main() {
+/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(TimeVal);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // with two file descriptors
+/// let _ = cmsg_space!([RawFd; 2]);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // and a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(RawFd, TimeVal);
+/// # }
+/// ```
+// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a
+// stack-allocated array.
+#[macro_export]
+macro_rules! cmsg_space {
+ ( $( $x:ty ),* ) => {
+ {
+ let space = 0 $(+ $crate::sys::socket::cmsg_space::<$x>())*;
+ Vec::<u8>::with_capacity(space)
+ }
+ }
+}
+
+#[inline]
+#[doc(hidden)]
+pub fn cmsg_space<T>() -> usize {
+ // SAFETY: CMSG_SPACE is always safe
+ unsafe { libc::CMSG_SPACE(mem::size_of::<T>() as libc::c_uint) as usize }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// Contains outcome of sending or receiving a message
+///
+/// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and
+/// [`iovs`][RecvMsg::iovs`] to access underlying io slices.
+pub struct RecvMsg<'a, 's, S> {
+ pub bytes: usize,
+ cmsghdr: Option<&'a cmsghdr>,
+ pub address: Option<S>,
+ pub flags: MsgFlags,
+ iobufs: std::marker::PhantomData<& 's()>,
+ mhdr: msghdr,
+}
+
+impl<'a, S> RecvMsg<'a, '_, S> {
+ /// Iterate over the valid control messages pointed to by this
+ /// msghdr.
+ pub fn cmsgs(&self) -> CmsgIterator {
+ CmsgIterator {
+ cmsghdr: self.cmsghdr,
+ mhdr: &self.mhdr
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct CmsgIterator<'a> {
+ /// Control message buffer to decode from. Must adhere to cmsg alignment.
+ cmsghdr: Option<&'a cmsghdr>,
+ mhdr: &'a msghdr
+}
+
+impl<'a> Iterator for CmsgIterator<'a> {
+ type Item = ControlMessageOwned;
+
+ fn next(&mut self) -> Option<ControlMessageOwned> {
+ match self.cmsghdr {
+ None => None, // No more messages
+ Some(hdr) => {
+ // Get the data.
+ // Safe if cmsghdr points to valid data returned by recvmsg(2)
+ let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))};
+ // Advance the internal pointer. Safe if mhdr and cmsghdr point
+ // to valid data returned by recvmsg(2)
+ self.cmsghdr = unsafe {
+ let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _);
+ p.as_ref()
+ };
+ cm
+ }
+ }
+ }
+}
+
+/// A type-safe wrapper around a single control message, as used with
+/// [`recvmsg`](#fn.recvmsg).
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and
+// sendmsg. However, on some platforms the messages returned by recvmsg may be
+// unaligned. ControlMessageOwned takes those messages by copy, obviating any
+// alignment issues.
+//
+// See https://github.com/nix-rust/nix/issues/999
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessageOwned {
+ /// Received version of [`ControlMessage::ScmRights`]
+ ScmRights(Vec<RawFd>),
+ /// Received version of [`ControlMessage::ScmCredentials`]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(UnixCredentials),
+ /// Received version of [`ControlMessage::ScmCreds`]
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds(UnixCredentials),
+ /// A message of type `SCM_TIMESTAMP`, containing the time the
+ /// packet was received by the kernel.
+ ///
+ /// See the kernel's explanation in "SO_TIMESTAMP" of
+ /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// # use nix::sys::socket::*;
+ /// # use nix::sys::time::*;
+ /// # use std::io::{IoSlice, IoSliceMut};
+ /// # use std::time::*;
+ /// # use std::str::FromStr;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # fn main() {
+ /// // Set up
+ /// let message = "Ohayō!".as_bytes();
+ /// let in_socket = socket(
+ /// AddressFamily::Inet,
+ /// SockType::Datagram,
+ /// SockFlag::empty(),
+ /// None).unwrap();
+ /// setsockopt(&in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
+ /// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ /// bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ /// let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ /// // Get initial time
+ /// let time0 = SystemTime::now();
+ /// // Send the message
+ /// let iov = [IoSlice::new(message)];
+ /// let flags = MsgFlags::empty();
+ /// let l = sendmsg(in_socket.as_raw_fd(), &iov, &[], flags, Some(&address)).unwrap();
+ /// assert_eq!(message.len(), l);
+ /// // Receive the message
+ /// let mut buffer = vec![0u8; message.len()];
+ /// let mut cmsgspace = cmsg_space!(TimeVal);
+ /// let mut iov = [IoSliceMut::new(&mut buffer)];
+ /// let r = recvmsg::<SockaddrIn>(in_socket.as_raw_fd(), &mut iov, Some(&mut cmsgspace), flags)
+ /// .unwrap();
+ /// let rtime = match r.cmsgs().next() {
+ /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime,
+ /// Some(_) => panic!("Unexpected control message"),
+ /// None => panic!("No control message")
+ /// };
+ /// // Check the final time
+ /// let time1 = SystemTime::now();
+ /// // the packet's received timestamp should lie in-between the two system
+ /// // times, unless the system clock was adjusted in the meantime.
+ /// let rduration = Duration::new(rtime.tv_sec() as u64,
+ /// rtime.tv_usec() as u32 * 1000);
+ /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+ /// // Close socket
+ /// # }
+ /// ```
+ ScmTimestamp(TimeVal),
+ /// A set of nanosecond resolution timestamps
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ScmTimestampsns(Timestamps),
+ /// Nanoseconds resolution timestamp
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmTimestampns(TimeSpec),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(libc::in_pktinfo),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(libc::in6_pktinfo),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvIf(libc::sockaddr_dl),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvDstAddr(libc::in_addr),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4OrigDstAddr(libc::sockaddr_in),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6OrigDstAddr(libc::sockaddr_in6),
+
+ /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
+ /// packets from a single sender.
+ /// Fixed-size payloads are following one by one in a receive buffer.
+ /// This Control Message indicates the size of all smaller packets,
+ /// except, maybe, the last one.
+ ///
+ /// `UdpGroSegment` socket option should be enabled on a socket
+ /// to allow receiving GRO packets.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGroSegments(u16),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ ///
+ /// `RxqOvfl` socket option should be enabled on a socket
+ /// to allow receiving the drop counter.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(u32),
+
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
+
+ /// Catch-all variant for unimplemented cmsg types.
+ #[doc(hidden)]
+ Unknown(UnknownCmsg),
+}
+
+/// For representing packet timestamps via `SO_TIMESTAMPING` interface
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Timestamps {
+ /// software based timestamp, usually one containing data
+ pub system: TimeSpec,
+ /// legacy timestamp, usually empty
+ pub hw_trans: TimeSpec,
+ /// hardware based timestamp
+ pub hw_raw: TimeSpec,
+}
+
+impl ControlMessageOwned {
+ /// Decodes a `ControlMessageOwned` from raw bytes.
+ ///
+ /// This is only safe to call if the data is correct for the message type
+ /// specified in the header. Normally, the kernel ensures that this is the
+ /// case. "Correct" in this case includes correct length, alignment and
+ /// actual content.
+ // Clippy complains about the pointer alignment of `p`, not understanding
+ // that it's being fed to a function that can handle that.
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
+ {
+ let p = CMSG_DATA(header);
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let len = header as *const _ as usize + header.cmsg_len as usize
+ - p as usize;
+ match (header.cmsg_level, header.cmsg_type) {
+ (libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
+ let n = len / mem::size_of::<RawFd>();
+ let mut fds = Vec::with_capacity(n);
+ for i in 0..n {
+ let fdp = (p as *const RawFd).add(i);
+ fds.push(ptr::read_unaligned(fdp));
+ }
+ ControlMessageOwned::ScmRights(fds)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
+ let cred: libc::ucred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCredentials(cred.into())
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDS) => {
+ let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCreds(cred.into())
+ }
+ #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
+ let tv: libc::timeval = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
+ let ts: libc::timespec = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
+ let tp = p as *const libc::timespec;
+ let ts: libc::timespec = ptr::read_unaligned(tp);
+ let system = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
+ let hw_trans = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
+ let hw_raw = TimeSpec::from(ts);
+ let timestamping = Timestamps { system, hw_trans, hw_raw };
+ ControlMessageOwned::ScmTimestampsns(timestamping)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
+ ControlMessageOwned::Ipv6PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
+ ControlMessageOwned::Ipv4PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVIF) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
+ ControlMessageOwned::Ipv4RecvIf(dl)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::in_addr);
+ ControlMessageOwned::Ipv4RecvDstAddr(dl)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+ ControlMessageOwned::Ipv4OrigDstAddr(dl)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ (libc::SOL_UDP, libc::UDP_GRO) => {
+ let gso_size: u16 = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::UdpGroSegments(gso_size)
+ },
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
+ let drop_counter = ptr::read_unaligned(p as *const u32);
+ ControlMessageOwned::RxqOvfl(drop_counter)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
+ ControlMessageOwned::Ipv4RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+ ControlMessageOwned::Ipv6RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+ ControlMessageOwned::Ipv6OrigDstAddr(dl)
+ },
+ (_, _) => {
+ let sl = std::slice::from_raw_parts(p, len);
+ let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
+ ControlMessageOwned::Unknown(ucmsg)
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[allow(clippy::cast_ptr_alignment)] // False positive
+ unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
+ let ee = p as *const libc::sock_extended_err;
+ let err = ptr::read_unaligned(ee);
+
+ // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
+ // CMSG_DATA buffer. For local errors, there is no address included in the control
+ // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
+ // validate that the address object is in-bounds before we attempt to copy it.
+ let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
+
+ if addrp.offset(1) as usize - (p as usize) > len {
+ (err, None)
+ } else {
+ (err, Some(ptr::read_unaligned(addrp)))
+ }
+ }
+}
+
+/// A type-safe zero-copy wrapper around a single control message, as used wih
+/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not
+/// exhaustively pattern-match it.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessage<'a> {
+ /// A message of type `SCM_RIGHTS`, containing an array of file
+ /// descriptors passed between processes.
+ ///
+ /// See the description in the "Ancillary messages" section of the
+ /// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html).
+ ///
+ /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't
+ /// recommended since it causes platform-dependent behaviour: It might
+ /// swallow all but the first `ScmRights` message or fail with `EINVAL`.
+ /// Instead, you can put all fds to be passed into a single `ScmRights`
+ /// message.
+ ScmRights(&'a [RawFd]),
+ /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket option `SO_PEERCRED`, but requires a
+ /// process to explicitly send its credentials. A process running as root is
+ /// allowed to specify any credentials, while credentials sent by other
+ /// processes are verified by the kernel.
+ ///
+ /// For further information, please refer to the
+ /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(&'a UnixCredentials),
+ /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
+ /// requires a process to explicitly send its credentials.
+ ///
+ /// Credentials are always overwritten by the kernel, so this variant does have
+ /// any data, unlike the receive-side
+ /// [`ControlMessageOwned::ScmCreds`].
+ ///
+ /// For further information, please refer to the
+ /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds,
+
+ /// Set IV for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetIv(&'a [u8]),
+ /// Set crypto operation for `AF_ALG` crypto API. It may be one of
+ /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT`
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetOp(&'a libc::c_int),
+ /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms)
+ /// for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetAeadAssoclen(&'a u32),
+
+ /// UDP GSO makes it possible for applications to generate network packets
+ /// for a virtual MTU much greater than the real one.
+ /// The length of the send data no longer matches the expected length on
+ /// the wire.
+ /// The size of the datagram payload as it should appear on the wire may be
+ /// passed through this control message.
+ /// Send buffer should consist of multiple fixed-size wire payloads
+ /// following one by one, and the last, possibly smaller one.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGsoSegments(&'a u16),
+
+ /// Configure the sending addressing and interface for v4
+ ///
+ /// For further information, please refer to the
+ /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(&'a libc::in_pktinfo),
+
+ /// Configure the sending addressing and interface for v6
+ ///
+ /// For further information, please refer to the
+ /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(&'a libc::in6_pktinfo),
+
+ /// Configure the IPv4 source address with `IP_SENDSRCADDR`.
+ #[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4SendSrcAddr(&'a libc::in_addr),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(&'a u32),
+
+ /// Configure the transmission time of packets.
+ ///
+ /// For further information, please refer to the
+ /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man
+ /// page.
+ #[cfg(target_os = "linux")]
+ TxTime(&'a u64),
+}
+
+// An opaque structure used to prevent cmsghdr from being a public type
+#[doc(hidden)]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UnknownCmsg(cmsghdr, Vec<u8>);
+
+impl<'a> ControlMessage<'a> {
+ /// The value of CMSG_SPACE on this message.
+ /// Safe because CMSG_SPACE is always safe
+ fn space(&self) -> usize {
+ unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize}
+ }
+
+ /// The value of CMSG_LEN on this message.
+ /// Safe because CMSG_LEN is always safe
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl"))))]
+ fn cmsg_len(&self) -> usize {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize}
+ }
+
+ #[cfg(not(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl")))))]
+ fn cmsg_len(&self) -> libc::c_uint {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint)}
+ }
+
+ /// Return a reference to the payload data as a byte pointer
+ fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) {
+ let data_ptr = match *self {
+ ControlMessage::ScmRights(fds) => {
+ fds as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(creds) => {
+ &creds.0 as *const libc::ucred as *const u8
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ // The kernel overwrites the data, we just zero it
+ // to make sure it's not uninitialized memory
+ unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
+ return
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ #[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501
+ let af_alg_iv = libc::af_alg_iv {
+ ivlen: iv.len() as u32,
+ iv: [0u8; 0],
+ };
+
+ let size = mem::size_of_val(&af_alg_iv);
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &af_alg_iv as *const _ as *const u8,
+ cmsg_data,
+ size,
+ );
+ ptr::copy_nonoverlapping(
+ iv.as_ptr(),
+ cmsg_data.add(size),
+ iv.len()
+ );
+ };
+
+ return
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ op as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ len as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ gso_size as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ drop_count as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ tx_time as *const _ as *const u8
+ },
+ };
+ unsafe {
+ ptr::copy_nonoverlapping(
+ data_ptr,
+ cmsg_data,
+ self.len()
+ )
+ };
+ }
+
+ /// The size of the payload, excluding its cmsghdr
+ fn len(&self) -> usize {
+ match *self {
+ ControlMessage::ScmRights(fds) => {
+ mem::size_of_val(fds)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(creds) => {
+ mem::size_of_val(creds)
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ mem::size_of::<libc::cmsgcred>()
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ mem::size_of::<&[u8]>() + iv.len()
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ mem::size_of_val(op)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ mem::size_of_val(len)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ mem::size_of_val(gso_size)
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ mem::size_of_val(drop_count)
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ mem::size_of_val(tx_time)
+ },
+ }
+ }
+
+ /// Returns the value to put into the `cmsg_level` field of the header.
+ fn cmsg_level(&self) -> libc::c_int {
+ match *self {
+ ControlMessage::ScmRights(_) => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
+ ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => libc::SOL_SOCKET,
+ }
+ }
+
+ /// Returns the value to put into the `cmsg_type` field of the header.
+ fn cmsg_type(&self) -> libc::c_int {
+ match *self {
+ ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SCM_CREDS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) => {
+ libc::ALG_SET_IV
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(_) => {
+ libc::ALG_SET_OP
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(_) => {
+ libc::ALG_SET_AEAD_ASSOCLEN
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => {
+ libc::UDP_SEGMENT
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => {
+ libc::SO_RXQ_OVFL
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => {
+ libc::SCM_TXTIME
+ },
+ }
+ }
+
+ // Unsafe: cmsg must point to a valid cmsghdr with enough space to
+ // encode self.
+ unsafe fn encode_into(&self, cmsg: *mut cmsghdr) {
+ (*cmsg).cmsg_level = self.cmsg_level();
+ (*cmsg).cmsg_type = self.cmsg_type();
+ (*cmsg).cmsg_len = self.cmsg_len();
+ self.copy_to_cmsg_data(CMSG_DATA(cmsg));
+ }
+}
+
+
+/// Send data in scatter-gather vectors to a socket, possibly accompanied
+/// by ancillary data. Optionally direct the message at the given address,
+/// as with sendto.
+///
+/// Allocates if cmsgs is nonempty.
+///
+/// # Examples
+/// When not directing to any specific address, use `()` for the generic type
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::os::unix::io::AsRawFd;
+/// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+/// SockFlag::empty())
+/// .unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg::<()>(fd1.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
+/// ```
+/// When directing to a specific address, the generic type will be inferred.
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::str::FromStr;
+/// # use std::os::unix::io::AsRawFd;
+/// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(),
+/// None).unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg(fd.as_raw_fd(), &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
+/// ```
+pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
+ flags: MsgFlags, addr: Option<&S>) -> Result<usize>
+ where S: SockaddrLike
+{
+ let capacity = cmsgs.iter().map(|c| c.space()).sum();
+
+ // First size the buffer needed to hold the cmsgs. It must be zeroed,
+ // because subsequent code will not clear the padding bytes.
+ let mut cmsg_buffer = vec![0u8; capacity];
+
+ let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
+
+ let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+
+/// An extension of `sendmsg` that allows the caller to transmit multiple
+/// messages on a socket using a single system call. This has performance
+/// benefits for some applications.
+///
+/// Allocations are performed for cmsgs and to build `msghdr` buffer
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # Returns
+/// `Vec` with numbers of sent bytes on each sent message.
+///
+/// # References
+/// [`sendmsg`](fn.sendmsg.html)
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn sendmmsg<'a, XS, AS, C, I, S>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ // one address per group of slices
+ addrs: AS,
+ // shared across all the messages
+ cmsgs: C,
+ flags: MsgFlags
+) -> crate::Result<MultiResults<'a, S>>
+ where
+ XS: IntoIterator<Item = &'a I>,
+ AS: AsRef<[Option<S>]>,
+ I: AsRef<[IoSlice<'a>]> + 'a,
+ C: AsRef<[ControlMessage<'a>]> + 'a,
+ S: SockaddrLike + 'a
+{
+
+ let mut count = 0;
+
+
+ for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
+ let p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+
+ p.msg_namelen = addr.as_ref().map_or(0, S::len);
+ p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
+ }
+
+ count = i+1;
+ }
+
+ let sent = Errno::result(unsafe {
+ libc::sendmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received: sent
+ })
+
+}
+
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions
+pub struct MultiHeaders<S> {
+ // preallocated boxed slice of mmsghdr
+ items: Box<[libc::mmsghdr]>,
+ addresses: Box<[mem::MaybeUninit<S>]>,
+ // while we are not using it directly - this is used to store control messages
+ // and we retain pointers to them inside items array
+ _cmsg_buffers: Option<Box<[u8]>>,
+ msg_controllen: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<S> MultiHeaders<S> {
+ /// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate
+ ///
+ /// `cmsg_buffer` should be created with [`cmsg_space!`] if needed
+ pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
+ where
+ S: Copy + SockaddrLike,
+ {
+ // we will be storing pointers to addresses inside mhdr - convert it into boxed
+ // slice so it can'be changed later by pushing anything into self.addresses
+ let mut addresses = vec![std::mem::MaybeUninit::<S>::uninit(); num_slices].into_boxed_slice();
+
+ let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
+
+ // we'll need a cmsg_buffer for each slice, we preallocate a vector and split
+ // it into "slices" parts
+ let mut cmsg_buffers =
+ cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
+
+ let items = addresses
+ .iter_mut()
+ .enumerate()
+ .map(|(ix, address)| {
+ let (ptr, cap) = match &mut cmsg_buffers {
+ Some(v) => ((&mut v[ix * msg_controllen] as *mut u8), msg_controllen),
+ None => (std::ptr::null_mut(), 0),
+ };
+ let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null_mut(), 0, ptr, cap, address.as_mut_ptr()) };
+ libc::mmsghdr {
+ msg_hdr,
+ msg_len: 0,
+ }
+ })
+ .collect::<Vec<_>>();
+
+ Self {
+ items: items.into_boxed_slice(),
+ addresses,
+ _cmsg_buffers: cmsg_buffers,
+ msg_controllen,
+ }
+ }
+}
+
+/// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call.
+///
+/// This has performance benefits for some applications.
+///
+/// This method performs no allocations.
+///
+/// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce
+/// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and
+/// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs].
+///
+/// # Bugs (in underlying implementation, at least in Linux)
+/// The timeout argument does not work as intended. The timeout is checked only after the receipt
+/// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires,
+/// but then no further datagrams are received, the call will block forever.
+///
+/// If an error occurs after at least one message has been received, the call succeeds, and returns
+/// the number of messages received. The error code is expected to be returned on a subsequent
+/// call to recvmmsg(). In the current implementation, however, the error code can be
+/// overwritten in the meantime by an unrelated network event on a socket, for example an
+/// incoming ICMP packet.
+
+// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
+// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
+// details
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn recvmmsg<'a, XS, S, I>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ flags: MsgFlags,
+ mut timeout: Option<crate::sys::time::TimeSpec>,
+) -> crate::Result<MultiResults<'a, S>>
+where
+ XS: IntoIterator<Item = &'a I>,
+ I: AsRef<[IoSliceMut<'a>]> + 'a,
+{
+ let mut count = 0;
+ for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
+ let p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+ count = i + 1;
+ }
+
+ let timeout_ptr = timeout
+ .as_mut()
+ .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
+
+ let received = Errno::result(unsafe {
+ libc::recvmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _,
+ timeout_ptr,
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received,
+ })
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
+///
+///
+pub struct MultiResults<'a, S> {
+ // preallocated structures
+ rmm: &'a MultiHeaders<S>,
+ current_index: usize,
+ received: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<'a, S> Iterator for MultiResults<'a, S>
+where
+ S: Copy + SockaddrLike,
+{
+ type Item = RecvMsg<'a, 'a, S>;
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current_index >= self.received {
+ return None;
+ }
+ let mmsghdr = self.rmm.items[self.current_index];
+
+ // as long as we are not reading past the index writen by recvmmsg - address
+ // will be initialized
+ let address = unsafe { self.rmm.addresses[self.current_index].assume_init() };
+
+ self.current_index += 1;
+ Some(unsafe {
+ read_mhdr(
+ mmsghdr.msg_hdr,
+ mmsghdr.msg_len as isize,
+ self.rmm.msg_controllen,
+ address,
+ )
+ })
+ }
+}
+
+impl<'a, S> RecvMsg<'_, 'a, S> {
+ /// Iterate over the filled io slices pointed by this msghdr
+ pub fn iovs(&self) -> IoSliceIterator<'a> {
+ IoSliceIterator {
+ index: 0,
+ remaining: self.bytes,
+ slices: unsafe {
+ // safe for as long as mgdr is properly initialized and references are valid.
+ // for multi messages API we initialize it with an empty
+ // slice and replace with a concrete buffer
+ // for single message API we hold a lifetime reference to ioslices
+ std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _)
+ },
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct IoSliceIterator<'a> {
+ index: usize,
+ remaining: usize,
+ slices: &'a [IoSlice<'a>],
+}
+
+impl<'a> Iterator for IoSliceIterator<'a> {
+ type Item = &'a [u8];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.index >= self.slices.len() {
+ return None;
+ }
+ let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())];
+ self.remaining -= slice.len();
+ self.index += 1;
+ if slice.is_empty() {
+ return None;
+ }
+
+ Some(slice)
+ }
+}
+
+// test contains both recvmmsg and timestaping which is linux only
+// there are existing tests for recvmmsg only in tests/
+#[cfg(target_os = "linux")]
+#[cfg(test)]
+mod test {
+ use crate::sys::socket::{AddressFamily, ControlMessageOwned};
+ use crate::*;
+ use std::str::FromStr;
+ use std::os::unix::io::AsRawFd;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recvmm2() -> crate::Result<()> {
+ use crate::sys::socket::{
+ sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
+ SockaddrIn, TimestampingFlag,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )?;
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::SOCK_NONBLOCK,
+ None,
+ )?;
+
+ crate::sys::socket::bind(rsock.as_raw_fd(), &sock_addr)?;
+
+ setsockopt(&rsock, Timestamping, &TimestampingFlag::all())?;
+
+ let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
+
+ let mut recv_buf = vec![0; 1024];
+
+ let mut recv_iovs = Vec::new();
+ let mut pkt_iovs = Vec::new();
+
+ for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
+ pkt_iovs.push(IoSliceMut::new(chunk));
+ if ix % 2 == 1 {
+ recv_iovs.push(pkt_iovs);
+ pkt_iovs = Vec::new();
+ }
+ }
+ drop(pkt_iovs);
+
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+
+ let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
+ sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
+
+ let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
+
+ let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
+
+ let recv = super::recvmmsg(rsock.as_raw_fd(), &mut data, recv_iovs.iter(), flags, Some(t))?;
+
+ for rmsg in recv {
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ let mut saw_time = false;
+ let mut recvd = 0;
+ for cmsg in rmsg.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
+ let ts = timestamps.system;
+
+ let sys_time =
+ crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ {
+ saw_time = true;
+ }
+ }
+ }
+
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ assert!(saw_time);
+
+ for iov in rmsg.iovs() {
+ recvd += iov.len();
+ }
+ assert_eq!(recvd, 400);
+ }
+
+ Ok(())
+ }
+}
+unsafe fn read_mhdr<'a, 'i, S>(
+ mhdr: msghdr,
+ r: isize,
+ msg_controllen: usize,
+ mut address: S,
+) -> RecvMsg<'a, 'i, S>
+ where S: SockaddrLike
+{
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let cmsghdr = {
+ if mhdr.msg_controllen > 0 {
+ debug_assert!(!mhdr.msg_control.is_null());
+ debug_assert!(msg_controllen >= mhdr.msg_controllen as usize);
+ CMSG_FIRSTHDR(&mhdr as *const msghdr)
+ } else {
+ ptr::null()
+ }.as_ref()
+ };
+
+ // Ignore errors if this socket address has statically-known length
+ //
+ // This is to ensure that unix socket addresses have their length set appropriately.
+ let _ = address.set_length(mhdr.msg_namelen as usize);
+
+ RecvMsg {
+ bytes: r as usize,
+ cmsghdr,
+ address: Some(address),
+ flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
+ mhdr,
+ iobufs: std::marker::PhantomData,
+ }
+}
+
+/// Pack pointers to various structures into into msghdr
+///
+/// # Safety
+/// `iov_buffer` and `iov_buffer_len` must point to a slice
+/// of `IoSliceMut` and number of available elements or be a null pointer and 0
+///
+/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used
+/// to store control headers later or be a null pointer and 0 if control
+/// headers are not used
+///
+/// Buffers must remain valid for the whole lifetime of msghdr
+unsafe fn pack_mhdr_to_receive<S>(
+ iov_buffer: *mut IoSliceMut,
+ iov_buffer_len: usize,
+ cmsg_buffer: *mut u8,
+ cmsg_capacity: usize,
+ address: *mut S,
+) -> msghdr
+ where
+ S: SockaddrLike
+{
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = address as *mut c_void;
+ (*p).msg_namelen = S::size();
+ (*p).msg_iov = iov_buffer as *mut iovec;
+ (*p).msg_iovlen = iov_buffer_len as _;
+ (*p).msg_control = cmsg_buffer as *mut c_void;
+ (*p).msg_controllen = cmsg_capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+}
+
+fn pack_mhdr_to_send<'a, I, C, S>(
+ cmsg_buffer: &mut [u8],
+ iov: I,
+ cmsgs: C,
+ addr: Option<&S>
+) -> msghdr
+ where
+ I: AsRef<[IoSlice<'a>]>,
+ C: AsRef<[ControlMessage<'a>]>,
+ S: SockaddrLike + 'a
+{
+ let capacity = cmsg_buffer.len();
+
+ // The message header must be initialized before the individual cmsgs.
+ let cmsg_ptr = if capacity > 0 {
+ cmsg_buffer.as_mut_ptr() as *mut c_void
+ } else {
+ ptr::null_mut()
+ };
+
+ let mhdr = unsafe {
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _;
+ (*p).msg_namelen = addr.map(S::len).unwrap_or(0);
+ // transmute iov into a mutable pointer. sendmsg doesn't really mutate
+ // the buffer, but the standard says that it takes a mutable pointer
+ (*p).msg_iov = iov.as_ref().as_ptr() as *mut _;
+ (*p).msg_iovlen = iov.as_ref().len() as _;
+ (*p).msg_control = cmsg_ptr;
+ (*p).msg_controllen = capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+ };
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) };
+ }
+
+ mhdr
+}
+
+/// Receive message in scatter-gather vectors from a socket, and
+/// optionally receive ancillary data into the provided buffer.
+/// If no ancillary data is desired, use () as the type parameter.
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `iov`: Scatter-gather list of buffers to receive the message
+/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by
+/// [`cmsg_space!`](../../macro.cmsg_space.html)
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # References
+/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
+pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>],
+ mut cmsg_buffer: Option<&'a mut Vec<u8>>,
+ flags: MsgFlags) -> Result<RecvMsg<'a, 'outer, S>>
+ where S: SockaddrLike + 'a,
+ 'inner: 'outer
+{
+ let mut address = mem::MaybeUninit::uninit();
+
+ let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
+ .map(|v| (v.as_mut_ptr(), v.capacity()))
+ .unwrap_or((ptr::null_mut(), 0));
+ let mut mhdr = unsafe {
+ pack_mhdr_to_receive(iov.as_mut().as_mut_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
+ };
+
+ let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
+
+ let r = Errno::result(ret)?;
+
+ Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
+}
+}
+
+/// Create an endpoint for communication
+///
+/// The `protocol` specifies a particular protocol to be used with the
+/// socket. Normally only a single protocol exists to support a
+/// particular socket type within a given protocol family, in which case
+/// protocol can be specified as `None`. However, it is possible that many
+/// protocols may exist, in which case a particular protocol must be
+/// specified in this manner.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
+pub fn socket<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ flags: SockFlag,
+ protocol: T,
+) -> Result<OwnedFd> {
+ let protocol = match protocol.into() {
+ None => 0,
+ Some(p) => p as c_int,
+ };
+
+ // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
+ // little easier to understand by separating it out. So we have to merge these bitfields
+ // here.
+ let mut ty = ty as c_int;
+ ty |= flags.bits();
+
+ let res = unsafe { libc::socket(domain as c_int, ty, protocol) };
+
+ match res {
+ -1 => Err(Errno::last()),
+ fd => {
+ // Safe because libc::socket returned success
+ unsafe { Ok(OwnedFd::from_raw_fd(fd)) }
+ }
+ }
+}
+
+/// Create a pair of connected sockets
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
+pub fn socketpair<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ protocol: T,
+ flags: SockFlag,
+) -> Result<(OwnedFd, OwnedFd)> {
+ let protocol = match protocol.into() {
+ None => 0,
+ Some(p) => p as c_int,
+ };
+
+ // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
+ // little easier to understand by separating it out. So we have to merge these bitfields
+ // here.
+ let mut ty = ty as c_int;
+ ty |= flags.bits();
+
+ let mut fds = [-1, -1];
+
+ let res = unsafe {
+ libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr())
+ };
+ Errno::result(res)?;
+
+ // Safe because socketpair returned success.
+ unsafe {
+ Ok((OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])))
+ }
+}
+
+/// Listen for connections on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
+pub fn listen<F: AsFd>(sock: &F, backlog: usize) -> Result<()> {
+ let fd = sock.as_fd().as_raw_fd();
+ let res = unsafe { libc::listen(fd, backlog as c_int) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Bind a name to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
+pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::bind(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Accept a connection on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html)
+pub fn accept(sockfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
+
+ Errno::result(res)
+}
+
+/// Accept a connection on a socket
+///
+/// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html)
+#[cfg(any(
+ all(
+ target_os = "android",
+ any(
+ target_arch = "aarch64",
+ target_arch = "x86",
+ target_arch = "x86_64"
+ )
+ ),
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
+ let res = unsafe {
+ libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits())
+ };
+
+ Errno::result(res)
+}
+
+/// Initiate a connection on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
+pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::connect(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Receive data from a connection-oriented socket. Returns the number of
+/// bytes read
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
+pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
+ unsafe {
+ let ret = libc::recv(
+ sockfd,
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ );
+
+ Errno::result(ret).map(|r| r as usize)
+ }
+}
+
+/// Receive data from a connectionless or connection-oriented socket. Returns
+/// the number of bytes read and, for connectionless sockets, the socket
+/// address of the sender.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
+pub fn recvfrom<T: SockaddrLike>(
+ sockfd: RawFd,
+ buf: &mut [u8],
+) -> Result<(usize, Option<T>)> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = mem::size_of_val(&addr) as socklen_t;
+
+ let ret = Errno::result(libc::recvfrom(
+ sockfd,
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len() as size_t,
+ 0,
+ addr.as_mut_ptr() as *mut sockaddr,
+ &mut len as *mut socklen_t,
+ ))? as usize;
+
+ Ok((
+ ret,
+ T::from_raw(
+ addr.assume_init().as_ptr(),
+ Some(len),
+ ),
+ ))
+ }
+}
+
+/// Send a message to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
+pub fn sendto(
+ fd: RawFd,
+ buf: &[u8],
+ addr: &dyn SockaddrLike,
+ flags: MsgFlags,
+) -> Result<usize> {
+ let ret = unsafe {
+ libc::sendto(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ addr.as_ptr(),
+ addr.len(),
+ )
+ };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/// Send data to a connection-oriented socket. Returns the number of bytes read
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
+pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
+ let ret = unsafe {
+ libc::send(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ )
+ };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/*
+ *
+ * ===== Socket Options =====
+ *
+ */
+
+/// Represents a socket option that can be retrieved.
+pub trait GetSockOpt: Copy {
+ type Val;
+
+ /// Look up the value of this socket option on the given socket.
+ fn get<F: AsFd>(&self, fd: &F) -> Result<Self::Val>;
+}
+
+/// Represents a socket option that can be set.
+pub trait SetSockOpt: Clone {
+ type Val;
+
+ /// Set the value of this socket option on the given socket.
+ fn set<F: AsFd>(&self, fd: &F, val: &Self::Val) -> Result<()>;
+}
+
+/// Get the current value for the requested socket option
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
+pub fn getsockopt<F: AsFd, O: GetSockOpt>(fd: &F, opt: O) -> Result<O::Val> {
+ opt.get(fd)
+}
+
+/// Sets the value for the requested socket option
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html)
+///
+/// # Examples
+///
+/// ```
+/// use nix::sys::socket::setsockopt;
+/// use nix::sys::socket::sockopt::KeepAlive;
+/// use std::net::TcpListener;
+///
+/// let listener = TcpListener::bind("0.0.0.0:0").unwrap();
+/// let fd = listener;
+/// let res = setsockopt(&fd, KeepAlive, &true);
+/// assert!(res.is_ok());
+/// ```
+pub fn setsockopt<F: AsFd, O: SetSockOpt>(
+ fd: &F,
+ opt: O,
+ val: &O::Val,
+) -> Result<()> {
+ opt.set(fd, val)
+}
+
+/// Get the address of the peer connected to the socket `fd`.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
+pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret =
+ libc::getpeername(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+/// Get the current address to which the socket `fd` is bound.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
+pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret =
+ libc::getsockname(fd, addr.as_mut_ptr() as *mut sockaddr, &mut len);
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum Shutdown {
+ /// Further receptions will be disallowed.
+ Read,
+ /// Further transmissions will be disallowed.
+ Write,
+ /// Further receptions and transmissions will be disallowed.
+ Both,
+}
+
+/// Shut down part of a full-duplex connection.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html)
+pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
+ unsafe {
+ use libc::shutdown;
+
+ let how = match how {
+ Shutdown::Read => libc::SHUT_RD,
+ Shutdown::Write => libc::SHUT_WR,
+ Shutdown::Both => libc::SHUT_RDWR,
+ };
+
+ Errno::result(shutdown(df, how)).map(drop)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[cfg(not(target_os = "redox"))]
+ #[test]
+ fn can_use_cmsg_space() {
+ let _ = cmsg_space!(u8);
+ }
+
+ #[cfg(not(any(
+ target_os = "redox",
+ target_os = "linux",
+ target_os = "android"
+ )))]
+ #[test]
+ fn can_open_routing_socket() {
+ let _ = super::socket(
+ super::AddressFamily::Route,
+ super::SockType::Raw,
+ super::SockFlag::empty(),
+ None,
+ )
+ .expect("Failed to open routing socket");
+ }
+}
diff --git a/vendor/nix/src/sys/socket/sockopt.rs b/vendor/nix/src/sys/socket/sockopt.rs
new file mode 100644
index 000000000..44f3ebbc1
--- /dev/null
+++ b/vendor/nix/src/sys/socket/sockopt.rs
@@ -0,0 +1,1470 @@
+//! Socket options as used by `setsockopt` and `getsockopt`.
+use super::{GetSockOpt, SetSockOpt};
+use crate::errno::Errno;
+use crate::sys::time::TimeVal;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, socklen_t};
+use std::ffi::{OsStr, OsString};
+use std::mem::{self, MaybeUninit};
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsFd, AsRawFd};
+
+// Constants
+// TCP_CA_NAME_MAX isn't defined in user space include files
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+const TCP_CA_NAME_MAX: usize = 16;
+
+/// Helper for implementing `SetSockOpt` for a given socket option. See
+/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
+///
+/// This macro aims to help implementing `SetSockOpt` for different socket options that accept
+/// different kinds of data to be used with `setsockopt`.
+///
+/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
+/// you are implementing represents a simple type.
+///
+/// # Arguments
+///
+/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `setsockopt` call.
+/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
+/// to the `setsockopt` call.
+/// * Type of the value that you are going to set.
+/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
+/// `bool`, `SetUsize` for `usize`, etc.).
+macro_rules! setsockopt_impl {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
+ impl SetSockOpt for $name {
+ type Val = $ty;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &$ty) -> Result<()> {
+ unsafe {
+ let setter: $setter = Set::new(val);
+
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_fd(),
+ $level,
+ $flag,
+ setter.ffi_ptr(),
+ setter.ffi_len(),
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+ }
+ };
+}
+
+/// Helper for implementing `GetSockOpt` for a given socket option. See
+/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html).
+///
+/// This macro aims to help implementing `GetSockOpt` for different socket options that accept
+/// different kinds of data to be use with `getsockopt`.
+///
+/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
+/// you are implementing represents a simple type.
+///
+/// # Arguments
+///
+/// * Name of the type you want to implement `GetSockOpt` for.
+/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip
+/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
+/// to your system manual for more options. Will be passed as the second argument (`level`) to
+/// the `getsockopt` call.
+/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
+/// the `getsockopt` call.
+/// * Type of the value that you are going to get.
+/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
+/// `bool`, `GetUsize` for `usize`, etc.).
+macro_rules! getsockopt_impl {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
+ impl GetSockOpt for $name {
+ type Val = $ty;
+
+ fn get<F: AsFd>(&self, fd: &F) -> Result<$ty> {
+ unsafe {
+ let mut getter: $getter = Get::uninit();
+
+ let res = libc::getsockopt(
+ fd.as_fd().as_raw_fd(),
+ $level,
+ $flag,
+ getter.ffi_ptr(),
+ getter.ffi_len(),
+ );
+ Errno::result(res)?;
+
+ match <$ty>::try_from(getter.assume_init()) {
+ Err(_) => Err(Errno::EINVAL),
+ Ok(r) => Ok(r),
+ }
+ }
+ }
+ }
+ };
+}
+
+/// Helper to generate the sockopt accessors. See
+/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and
+/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
+///
+/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options
+/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively.
+///
+/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and
+/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros.
+///
+/// # Arguments
+///
+/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
+/// both of them.
+/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `getsockopt`/`setsockopt` call.
+/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
+/// to the `setsockopt`/`getsockopt` call.
+/// * `$ty:ty`: type of the value that will be get/set.
+/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
+/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
+// Some targets don't use all rules.
+#[allow(unused_macro_rules)]
+macro_rules! sockopt_impl {
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, bool, GetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, usize, GetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, bool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, usize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, bool, GetBool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, u8, GetU8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, usize, GetUsize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
+ OsString<$array:ty>) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, OsString, GetOsString<$array>,
+ SetOsString);
+ };
+
+ /*
+ * Matchers with generic getter types must be placed at the end, so
+ * they'll only match _after_ specialized matchers fail
+ */
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
+ $getter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
+ $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
+ $getter:ty, $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, $ty, GetStruct<$ty>,
+ SetStruct<$ty>);
+ };
+}
+
+/*
+ *
+ * ===== Define sockopts =====
+ *
+ */
+
+sockopt_impl!(
+ /// Enables local address reuse
+ ReuseAddr,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEADDR,
+ bool
+);
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+sockopt_impl!(
+ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
+ /// identical socket address.
+ ReusePort,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEPORT,
+ bool
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Under most circumstances, TCP sends data when it is presented; when
+ /// outstanding data has not yet been acknowledged, it gathers small amounts
+ /// of output to be sent in a single packet once an acknowledgement is
+ /// received. For a small number of clients, such as window systems that
+ /// send a stream of mouse events which receive no replies, this
+ /// packetization may cause significant delays. The boolean option
+ /// TCP_NODELAY defeats this algorithm.
+ TcpNoDelay,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_NODELAY,
+ bool
+);
+sockopt_impl!(
+ /// When enabled, a close(2) or shutdown(2) will not return until all
+ /// queued messages for the socket have been successfully sent or the
+ /// linger timeout has been reached.
+ Linger,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_LINGER,
+ libc::linger
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join a multicast group
+ IpAddMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_ADD_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave a multicast group.
+ IpDropMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_DROP_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
+ } else if #[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"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
+ }
+}
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read the time-to-live value of outgoing multicast packets for
+ /// this socket.
+ IpMulticastTtl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_TTL,
+ u8
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read a boolean integer argument that determines whether sent
+ /// multicast packets should be looped back to the local sockets.
+ IpMulticastLoop,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_LOOP,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set the protocol-defined priority for all packets to be
+ /// sent on this socket
+ Priority,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PRIORITY,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or receive the Type-Of-Service (TOS) field that is
+ /// sent with every IP packet originating from this socket
+ IpTos,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TOS,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Traffic class associated with outgoing packets
+ Ipv6TClass,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_TCLASS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// If enabled, this boolean option allows binding to an IP address that
+ /// is nonlocal or does not (yet) exist.
+ IpFreebind,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_FREEBIND,
+ bool
+);
+sockopt_impl!(
+ /// Specify the receiving timeout until reporting an error.
+ ReceiveTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Specify the sending timeout until reporting an error.
+ SendTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Set or get the broadcast flag.
+ Broadcast,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BROADCAST,
+ bool
+);
+sockopt_impl!(
+ /// If this option is enabled, out-of-band data is directly placed into
+ /// the receive data stream.
+ OobInline,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_OOBINLINE,
+ bool
+);
+sockopt_impl!(
+ /// Get and clear the pending socket error.
+ SocketError,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ERROR,
+ i32
+);
+sockopt_impl!(
+ /// Set or get the don't route flag.
+ DontRoute,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_DONTROUTE,
+ bool
+);
+sockopt_impl!(
+ /// Enable sending of keep-alive messages on connection-oriented sockets.
+ KeepAlive,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_KEEPALIVE,
+ bool
+);
+#[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.
+ LocalPeerCred,
+ GetOnly,
+ 0,
+ libc::LOCAL_PEERCRED,
+ super::XuCred
+);
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+sockopt_impl!(
+ /// Get the PID of the peer process of a connected unix domain socket.
+ LocalPeerPid,
+ GetOnly,
+ 0,
+ libc::LOCAL_PEERPID,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Return the credentials of the foreign process connected to this socket.
+ PeerCredentials,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_PEERCRED,
+ super::UnixCredentials
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specify the amount of time, in seconds, that the connection must be idle
+ /// before keepalive probes (if enabled) are sent.
+ TcpKeepAlive,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPALIVE,
+ u32
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) the connection needs to remain idle before TCP
+ /// starts sending keepalive probes
+ TcpKeepIdle,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPIDLE,
+ u32
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ } else if #[cfg(not(target_os = "redox"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ }
+}
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The maximum number of keepalive probes TCP should send before
+ /// dropping the connection.
+ TcpKeepCount,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPCNT,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ TcpRepair,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_REPAIR,
+ u32
+);
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "redox")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) between individual keepalive probes.
+ TcpKeepInterval,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPINTVL,
+ u32
+);
+#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specifies the maximum amount of time in milliseconds that transmitted
+ /// data may remain unacknowledged before TCP will forcibly close the
+ /// corresponding connection
+ TcpUserTimeout,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_USER_TIMEOUT,
+ u32
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket receive buffer in bytes.
+ RcvBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUF,
+ usize
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket send buffer in bytes.
+ SndBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUF,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
+ /// overridden.
+ RcvBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUFFORCE,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
+ /// overridden.
+ SndBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUFFORCE,
+ usize
+);
+sockopt_impl!(
+ /// Gets the socket type as an integer.
+ SockType,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_TYPE,
+ super::SockType,
+ GetStruct<i32>
+);
+sockopt_impl!(
+ /// Returns a value indicating whether or not this socket has been marked to
+ /// accept connections with `listen(2)`.
+ AcceptConn,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ACCEPTCONN,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Bind this socket to a particular device like “eth0”.
+ BindToDevice,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDTODEVICE,
+ OsString<[u8; libc::IFNAMSIZ]>
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ OriginalDst,
+ GetOnly,
+ libc::SOL_IP,
+ libc::SO_ORIGINAL_DST,
+ libc::sockaddr_in
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ Ip6tOriginalDst,
+ GetOnly,
+ libc::SOL_IPV6,
+ libc::IP6T_SO_ORIGINAL_DST,
+ libc::sockaddr_in6
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Specifies exact type of timestamping information collected by the kernel
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ Timestamping,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPING,
+ super::TimestampingFlag
+);
+#[cfg(not(any(target_os = "aix", target_os = "haiku", target_os = "redox")))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
+ ReceiveTimestamp,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMP,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
+ ReceiveTimestampns,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPNS,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Sets a specific timestamp format instead of the classic `SCM_TIMESTAMP`,
+ /// to follow up after `SO_TIMESTAMP` is set.
+ TsClock,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TS_CLOCK,
+ i32
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Setting this boolean option enables transparent proxying on this socket.
+ IpTransparent,
+ Both,
+ libc::SOL_IP,
+ libc::IP_TRANSPARENT,
+ bool
+);
+#[cfg(target_os = "openbsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Allows the socket to be bound to addresses which are not local to the
+ /// machine, so it can be used to make a transparent proxy.
+ BindAny,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDANY,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Can `bind(2)` to any address, even one not bound to any available
+ /// network interface in the system.
+ BindAny,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_BINDANY,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Set the route table (FIB) for this socket up to the `net.fibs` OID limit
+ /// (more specific than the setfib command line/call which are process based).
+ Fib,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_SETFIB,
+ i32
+);
+#[cfg(target_os = "freebsd")]
+sockopt_impl!(
+ /// Set `so_user_cookie` for this socket allowing network traffic based
+ /// upon it, similar to Linux's netfilter MARK.
+ UserCookie,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_USER_COOKIE,
+ u32
+);
+#[cfg(target_os = "openbsd")]
+sockopt_impl!(
+ /// Set the route table for this socket, needs a privileged user if
+ /// the process/socket had been set to the non default route.
+ Rtable,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_RTABLE,
+ i32
+);
+#[cfg(any(target_os = "freebsd", target_os = "netbsd"))]
+sockopt_impl!(
+ /// Get/set a filter on this socket before accepting connections similarly
+ /// to Linux's TCP_DEFER_ACCEPT but after the listen's call.
+ AcceptFilter,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_ACCEPTFILTER,
+ libc::accept_filter_arg
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Set the mark for each packet sent through this socket (similar to the
+ /// netfilter MARK target but socket-based).
+ Mark,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_MARK,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
+ /// message.
+ PassCred,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PASSCRED,
+ bool
+);
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// This option allows the caller to set the TCP congestion control
+ /// algorithm to be used, on a per-socket basis.
+ TcpCongestion,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_CONGESTION,
+ OsString<[u8; TCP_CA_NAME_MAX]>
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
+ /// structure that supplies some information about the incoming packet.
+ Ipv4PacketInfo,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_PKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set delivery of the `IPV6_PKTINFO` control message on incoming
+ /// datagrams.
+ Ipv6RecvPacketInfo,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVPKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
+ /// the interface on which the packet was received.
+ Ipv4RecvIf,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVIF,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4RecvDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4OrigDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_ORIGDSTADDR,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGsoSegment,
+ Both,
+ libc::SOL_UDP,
+ libc::UDP_SEGMENT,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGroSegment,
+ Both,
+ libc::IPPROTO_UDP,
+ libc::UDP_GRO,
+ bool
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Configures the behavior of time-based transmission of packets, for use
+ /// with the `TxTime` control message.
+ TxTime,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TXTIME,
+ libc::sock_txtime
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
+ /// be attached to received skbs indicating the number of packets dropped by
+ /// the socket since its creation.
+ RxqOvfl,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RXQ_OVFL,
+ libc::c_int
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The socket is restricted to sending and receiving IPv6 packets only.
+ Ipv6V6Only,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_V6ONLY,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable extended reliable error message passing.
+ Ipv4RecvErr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Control receiving of asynchronous error options.
+ Ipv6RecvErr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Fetch the current system-estimated Path MTU.
+ IpMtu,
+ GetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_MTU,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set or retrieve the current time-to-live field that is used in every
+ /// packet sent from this socket.
+ Ipv4Ttl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TTL,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set the unicast hop limit for the socket.
+ Ipv6Ttl,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_UNICAST_HOPS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv6OrigDstAddr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_ORIGDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+sockopt_impl!(
+ /// Set "don't fragment packet" flag on the IP packet.
+ IpDontFrag,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_DONTFRAG,
+ bool
+);
+#[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,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_DONTFRAG,
+ bool
+);
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Copy, Clone, Debug)]
+pub struct AlgSetAeadAuthSize;
+
+// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len`
+// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl SetSockOpt for AlgSetAeadAuthSize {
+ type Val = usize;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &usize) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_fd(),
+ libc::SOL_ALG,
+ libc::ALG_SET_AEAD_AUTHSIZE,
+ ::std::ptr::null(),
+ *val as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Clone, Debug)]
+pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> Default for AlgSetKey<T> {
+ fn default() -> Self {
+ AlgSetKey(Default::default())
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> SetSockOpt for AlgSetKey<T>
+where
+ T: AsRef<[u8]> + Clone,
+{
+ type Val = T;
+
+ fn set<F: AsFd>(&self, fd: &F, val: &T) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd.as_fd().as_raw_fd(),
+ libc::SOL_ALG,
+ libc::ALG_SET_KEY,
+ val.as_ref().as_ptr() as *const _,
+ val.as_ref().len() as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+/*
+ *
+ * ===== Accessor helpers =====
+ *
+ */
+
+/// Helper trait that describes what is expected from a `GetSockOpt` getter.
+trait Get<T> {
+ /// Returns an uninitialized value.
+ fn uninit() -> Self;
+ /// Returns a pointer to the stored value. This pointer will be passed to the system's
+ /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
+ fn ffi_ptr(&mut self) -> *mut c_void;
+ /// Returns length of the stored value. This pointer will be passed to the system's
+ /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`).
+ fn ffi_len(&mut self) -> *mut socklen_t;
+ /// Returns the hopefully initialized inner value.
+ unsafe fn assume_init(self) -> T;
+}
+
+/// Helper trait that describes what is expected from a `SetSockOpt` setter.
+trait Set<'a, T> {
+ /// Initialize the setter with a given value.
+ fn new(val: &'a T) -> Self;
+ /// Returns a pointer to the stored value. This pointer will be passed to the system's
+ /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`).
+ fn ffi_ptr(&self) -> *const c_void;
+ /// Returns length of the stored value. This pointer will be passed to the system's
+ /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`).
+ fn ffi_len(&self) -> socklen_t;
+}
+
+/// Getter for an arbitrary `struct`.
+struct GetStruct<T> {
+ len: socklen_t,
+ val: MaybeUninit<T>,
+}
+
+impl<T> Get<T> for GetStruct<T> {
+ fn uninit() -> Self {
+ GetStruct {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> T {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<T>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an arbitrary `struct`.
+struct SetStruct<'a, T: 'static> {
+ ptr: &'a T,
+}
+
+impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
+ fn new(ptr: &'a T) -> SetStruct<'a, T> {
+ SetStruct { ptr }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ self.ptr as *const T as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<T>() as socklen_t
+ }
+}
+
+/// Getter for a boolean value.
+struct GetBool {
+ len: socklen_t,
+ val: MaybeUninit<c_int>,
+}
+
+impl Get<bool> for GetBool {
+ fn uninit() -> Self {
+ GetBool {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> bool {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() != 0
+ }
+}
+
+/// Setter for a boolean value.
+struct SetBool {
+ val: c_int,
+}
+
+impl<'a> Set<'a, bool> for SetBool {
+ fn new(val: &'a bool) -> SetBool {
+ SetBool {
+ val: i32::from(*val),
+ }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const c_int as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for an `u8` value.
+struct GetU8 {
+ len: socklen_t,
+ val: MaybeUninit<u8>,
+}
+
+impl Get<u8> for GetU8 {
+ fn uninit() -> Self {
+ GetU8 {
+ len: mem::size_of::<u8>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> u8 {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<u8>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an `u8` value.
+struct SetU8 {
+ val: u8,
+}
+
+impl<'a> Set<'a, u8> for SetU8 {
+ fn new(val: &'a u8) -> SetU8 {
+ SetU8 { val: *val }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const u8 as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for an `usize` value.
+struct GetUsize {
+ len: socklen_t,
+ val: MaybeUninit<c_int>,
+}
+
+impl Get<usize> for GetUsize {
+ fn uninit() -> Self {
+ GetUsize {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> usize {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() as usize
+ }
+}
+
+/// Setter for an `usize` value.
+struct SetUsize {
+ val: c_int,
+}
+
+impl<'a> Set<'a, usize> for SetUsize {
+ fn new(val: &'a usize) -> SetUsize {
+ SetUsize { val: *val as c_int }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const c_int as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for a `OsString` value.
+struct GetOsString<T: AsMut<[u8]>> {
+ len: socklen_t,
+ val: MaybeUninit<T>,
+}
+
+impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+ fn uninit() -> Self {
+ GetOsString {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> OsString {
+ let len = self.len as usize;
+ let mut v = self.val.assume_init();
+ OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
+ }
+}
+
+/// Setter for a `OsString` value.
+struct SetOsString<'a> {
+ val: &'a OsStr,
+}
+
+impl<'a> Set<'a, OsString> for SetOsString<'a> {
+ fn new(val: &'a OsString) -> SetOsString {
+ SetOsString {
+ val: val.as_os_str(),
+ }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ self.val.as_bytes().as_ptr() as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ self.val.len() as socklen_t
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn can_get_peercred_on_unix_socket() {
+ use super::super::*;
+
+ let (a, b) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let a_cred = getsockopt(&a, super::PeerCredentials).unwrap();
+ let b_cred = getsockopt(&b, super::PeerCredentials).unwrap();
+ assert_eq!(a_cred, b_cred);
+ assert_ne!(a_cred.pid(), 0);
+ }
+
+ #[test]
+ fn is_socket_type_unix() {
+ use super::super::*;
+
+ let (a, _b) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let a_type = getsockopt(&a, super::SockType).unwrap();
+ assert_eq!(a_type, SockType::Stream);
+ }
+
+ #[test]
+ fn is_socket_type_dgram() {
+ use super::super::*;
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let s_type = getsockopt(&s, super::SockType).unwrap();
+ assert_eq!(s_type, SockType::Datagram);
+ }
+
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ #[test]
+ fn can_get_listen_on_tcp_socket() {
+ use super::super::*;
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let s_listening = getsockopt(&s, super::AcceptConn).unwrap();
+ assert!(!s_listening);
+ listen(&s, 10).unwrap();
+ let s_listening2 = getsockopt(&s, super::AcceptConn).unwrap();
+ assert!(s_listening2);
+ }
+}