From 45df4d0d9b577fecee798d672695fe24ff57fb1b Mon Sep 17 00:00:00 2001 From: mo khan Date: Tue, 15 Jul 2025 16:37:08 -0600 Subject: feat: migrate from Cedar to SpiceDB authorization system This is a major architectural change that replaces the Cedar policy-based authorization system with SpiceDB's relation-based authorization. Key changes: - Migrate from Rust to Go implementation - Replace Cedar policies with SpiceDB schema and relationships - Switch from envoy `ext_authz` with Cedar to SpiceDB permission checks - Update build system and dependencies for Go ecosystem - Maintain Envoy integration for external authorization This change enables more flexible permission modeling through SpiceDB's Google Zanzibar inspired relation-based system, supporting complex hierarchical permissions that were difficult to express in Cedar. Breaking change: Existing Cedar policies and Rust-based configuration will no longer work and need to be migrated to SpiceDB schema. --- vendor/socket2/src/sys/unix.rs | 3305 ------------------------------------- vendor/socket2/src/sys/windows.rs | 1065 ------------ 2 files changed, 4370 deletions(-) delete mode 100644 vendor/socket2/src/sys/unix.rs delete mode 100644 vendor/socket2/src/sys/windows.rs (limited to 'vendor/socket2/src/sys') diff --git a/vendor/socket2/src/sys/unix.rs b/vendor/socket2/src/sys/unix.rs deleted file mode 100644 index fbe4cc3a..00000000 --- a/vendor/socket2/src/sys/unix.rs +++ /dev/null @@ -1,3305 +0,0 @@ -// Copyright 2015 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cmp::min; -use std::ffi::OsStr; -#[cfg(not(target_os = "redox"))] -use std::io::IoSlice; -use std::marker::PhantomData; -use std::mem::{self, size_of, MaybeUninit}; -use std::net::Shutdown; -use std::net::{Ipv4Addr, Ipv6Addr}; -#[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "illumos", - target_os = "solaris", - ) -))] -use std::num::NonZeroU32; -#[cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) -))] -use std::num::NonZeroUsize; -use std::os::unix::ffi::OsStrExt; -#[cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) -))] -use std::os::unix::io::RawFd; -use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -#[cfg(feature = "all")] -use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; -use std::path::Path; -use std::ptr; -use std::time::{Duration, Instant}; -use std::{io, slice}; - -#[cfg(not(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "cygwin", -)))] -use libc::ssize_t; -use libc::{in6_addr, in_addr}; - -use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type}; -#[cfg(not(target_os = "redox"))] -use crate::{MsgHdr, MsgHdrMut, RecvFlags}; - -pub(crate) use libc::c_int; - -// Used in `Domain`. -pub(crate) use libc::{AF_INET, AF_INET6, AF_UNIX}; -// Used in `Type`. -#[cfg(all(feature = "all", target_os = "linux"))] -pub(crate) use libc::SOCK_DCCP; -#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] -pub(crate) use libc::SOCK_RAW; -#[cfg(all(feature = "all", not(target_os = "espidf")))] -pub(crate) use libc::SOCK_SEQPACKET; -pub(crate) use libc::{SOCK_DGRAM, SOCK_STREAM}; -// Used in `Protocol`. -#[cfg(all(feature = "all", target_os = "linux"))] -pub(crate) use libc::IPPROTO_DCCP; -#[cfg(target_os = "linux")] -pub(crate) use libc::IPPROTO_MPTCP; -#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] -pub(crate) use libc::IPPROTO_SCTP; -#[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) -))] -pub(crate) use libc::IPPROTO_UDPLITE; -pub(crate) use libc::{IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP}; -// Used in `SockAddr`. -#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))] -pub(crate) use libc::IPPROTO_DIVERT; -pub(crate) use libc::{ - sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, -}; -// Used in `RecvFlags`. -#[cfg(not(any(target_os = "redox", target_os = "espidf")))] -pub(crate) use libc::MSG_TRUNC; -#[cfg(not(target_os = "redox"))] -pub(crate) use libc::SO_OOBINLINE; -// Used in `Socket`. -#[cfg(not(target_os = "nto"))] -pub(crate) use libc::ipv6_mreq as Ipv6Mreq; -#[cfg(all( - feature = "all", - not(any( - target_os = "dragonfly", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "haiku", - target_os = "espidf", - target_os = "vita", - target_os = "cygwin", - )) -))] -pub(crate) use libc::IPV6_RECVHOPLIMIT; -#[cfg(not(any( - target_os = "dragonfly", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "haiku", - target_os = "espidf", - target_os = "vita", -)))] -pub(crate) use libc::IPV6_RECVTCLASS; -#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] -pub(crate) use libc::IP_HDRINCL; -#[cfg(not(any( - target_os = "aix", - target_os = "dragonfly", - target_os = "fuchsia", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "haiku", - target_os = "hurd", - target_os = "nto", - target_os = "espidf", - target_os = "vita", - target_os = "cygwin", -)))] -pub(crate) use libc::IP_RECVTOS; -#[cfg(not(any( - target_os = "fuchsia", - target_os = "redox", - target_os = "solaris", - target_os = "haiku", - target_os = "illumos", -)))] -pub(crate) use libc::IP_TOS; -#[cfg(not(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", -)))] -pub(crate) use libc::SO_LINGER; -#[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", -))] -pub(crate) use libc::SO_LINGER_SEC as SO_LINGER; -#[cfg(any(target_os = "linux", target_os = "cygwin"))] -pub(crate) use libc::SO_PASSCRED; -pub(crate) use libc::{ - ip_mreq as IpMreq, linger, IPPROTO_IP, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, - IPV6_MULTICAST_LOOP, IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, - IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, MSG_OOB, MSG_PEEK, SOL_SOCKET, - SO_BROADCAST, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, - SO_SNDTIMEO, SO_TYPE, TCP_NODELAY, -}; -#[cfg(not(any( - target_os = "dragonfly", - target_os = "haiku", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "fuchsia", - target_os = "nto", - target_os = "espidf", - target_os = "vita", -)))] -pub(crate) use libc::{ - ip_mreq_source as IpMreqSource, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, -}; -#[cfg(not(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "haiku", - target_os = "illumos", - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "netbsd", - target_os = "nto", - target_os = "openbsd", - target_os = "solaris", - target_os = "tvos", - target_os = "watchos", -)))] -pub(crate) use libc::{IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP}; -#[cfg(any( - target_os = "dragonfly", - target_os = "freebsd", - target_os = "haiku", - target_os = "illumos", - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris", - target_os = "tvos", - target_os = "watchos", -))] -pub(crate) use libc::{ - IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP, IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP, -}; -#[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "tvos", - target_os = "watchos", - target_os = "cygwin", - ) -))] -pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL}; - -// See this type in the Windows file. -pub(crate) type Bool = c_int; - -#[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "nto", - target_os = "tvos", - target_os = "watchos", -))] -use libc::TCP_KEEPALIVE as KEEPALIVE_TIME; -#[cfg(not(any( - target_os = "haiku", - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "nto", - target_os = "openbsd", - target_os = "tvos", - target_os = "watchos", - target_os = "vita", -)))] -use libc::TCP_KEEPIDLE as KEEPALIVE_TIME; - -/// Helper macro to execute a system call that returns an `io::Result`. -macro_rules! syscall { - ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ - #[allow(unused_unsafe)] - let res = unsafe { libc::$fn($($arg, )*) }; - if res == -1 { - Err(std::io::Error::last_os_error()) - } else { - Ok(res) - } - }}; -} - -/// Maximum size of a buffer passed to system call like `recv` and `send`. -#[cfg(not(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "cygwin", -)))] -const MAX_BUF_LEN: usize = ssize_t::MAX as usize; - -// The maximum read limit on most posix-like systems is `SSIZE_MAX`, with the -// man page quoting that if the count of bytes to read is greater than -// `SSIZE_MAX` the result is "unspecified". -// -// On macOS, however, apparently the 64-bit libc is either buggy or -// intentionally showing odd behavior by rejecting any read with a size larger -// than or equal to INT_MAX. To handle both of these the read size is capped on -// both platforms. -#[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "cygwin", -))] -const MAX_BUF_LEN: usize = c_int::MAX as usize - 1; - -// TCP_CA_NAME_MAX isn't defined in user space include files(not in libc) -#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] -const TCP_CA_NAME_MAX: usize = 16; - -#[cfg(any( - all( - target_os = "linux", - any( - target_env = "gnu", - all(target_env = "uclibc", target_pointer_width = "64") - ) - ), - target_os = "android", -))] -type IovLen = usize; - -#[cfg(any( - all( - target_os = "linux", - any( - target_env = "musl", - target_env = "ohos", - all(target_env = "uclibc", target_pointer_width = "32") - ) - ), - target_os = "aix", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "haiku", - target_os = "hurd", - target_os = "illumos", - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "netbsd", - target_os = "nto", - target_os = "openbsd", - target_os = "solaris", - target_os = "tvos", - target_os = "watchos", - target_os = "espidf", - target_os = "vita", - target_os = "cygwin", -))] -type IovLen = c_int; - -/// Unix only API. -impl Domain { - /// Domain for low-level packet interface, corresponding to `AF_PACKET`. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub const PACKET: Domain = Domain(libc::AF_PACKET); - - /// Domain for low-level VSOCK interface, corresponding to `AF_VSOCK`. - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub const VSOCK: Domain = Domain(libc::AF_VSOCK); -} - -impl_debug!( - Domain, - libc::AF_INET, - libc::AF_INET6, - libc::AF_UNIX, - #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] - #[cfg_attr( - docsrs, - doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))) - )] - libc::AF_PACKET, - #[cfg(any(target_os = "android", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(any(target_os = "android", target_os = "linux"))))] - libc::AF_VSOCK, - libc::AF_UNSPEC, // = 0. -); - -/// Unix only API. -impl Type { - /// Set `SOCK_NONBLOCK` on the `Type`. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd" - ) - ))) - )] - pub const fn nonblocking(self) -> Type { - Type(self.0 | libc::SOCK_NONBLOCK) - } - - /// Set `SOCK_CLOEXEC` on the `Type`. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - ) - ))) - )] - pub const fn cloexec(self) -> Type { - self._cloexec() - } - - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "cygwin", - ))] - pub(crate) const fn _cloexec(self) -> Type { - Type(self.0 | libc::SOCK_CLOEXEC) - } -} - -impl_debug!( - Type, - libc::SOCK_STREAM, - libc::SOCK_DGRAM, - #[cfg(all(feature = "all", target_os = "linux"))] - libc::SOCK_DCCP, - #[cfg(not(any(target_os = "redox", target_os = "espidf")))] - libc::SOCK_RAW, - #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "espidf")))] - libc::SOCK_RDM, - #[cfg(not(target_os = "espidf"))] - libc::SOCK_SEQPACKET, - /* TODO: add these optional bit OR-ed flags: - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd" - ))] - libc::SOCK_NONBLOCK, - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd" - ))] - libc::SOCK_CLOEXEC, - */ -); - -impl_debug!( - Protocol, - libc::IPPROTO_ICMP, - libc::IPPROTO_ICMPV6, - libc::IPPROTO_TCP, - libc::IPPROTO_UDP, - #[cfg(target_os = "linux")] - libc::IPPROTO_MPTCP, - #[cfg(all(feature = "all", target_os = "linux"))] - libc::IPPROTO_DCCP, - #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] - libc::IPPROTO_SCTP, - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))] - libc::IPPROTO_UDPLITE, - #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "openbsd")))] - libc::IPPROTO_DIVERT, -); - -/// Unix-only API. -#[cfg(not(target_os = "redox"))] -impl RecvFlags { - /// Check if the message terminates a record. - /// - /// Not all socket types support the notion of records. For socket types - /// that do support it (such as [`SEQPACKET`]), a record is terminated by - /// sending a message with the end-of-record flag set. - /// - /// On Unix this corresponds to the `MSG_EOR` flag. - /// - /// [`SEQPACKET`]: Type::SEQPACKET - #[cfg(not(target_os = "espidf"))] - pub const fn is_end_of_record(self) -> bool { - self.0 & libc::MSG_EOR != 0 - } - - /// Check if the message contains out-of-band data. - /// - /// This is useful for protocols where you receive out-of-band data - /// mixed in with the normal data stream. - /// - /// On Unix this corresponds to the `MSG_OOB` flag. - pub const fn is_out_of_band(self) -> bool { - self.0 & libc::MSG_OOB != 0 - } - - /// Check if the confirm flag is set. - /// - /// This is used by SocketCAN to indicate a frame was sent via the - /// socket it is received on. This flag can be interpreted as a - /// 'transmission confirmation'. - /// - /// On Unix this corresponds to the `MSG_CONFIRM` flag. - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub const fn is_confirm(self) -> bool { - self.0 & libc::MSG_CONFIRM != 0 - } - - /// Check if the don't route flag is set. - /// - /// This is used by SocketCAN to indicate a frame was created - /// on the local host. - /// - /// On Unix this corresponds to the `MSG_DONTROUTE` flag. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "linux", target_os = "cygwin"), - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "linux", target_os = "cygwin") - ))) - )] - pub const fn is_dontroute(self) -> bool { - self.0 & libc::MSG_DONTROUTE != 0 - } -} - -#[cfg(not(target_os = "redox"))] -impl std::fmt::Debug for RecvFlags { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut s = f.debug_struct("RecvFlags"); - #[cfg(not(target_os = "espidf"))] - s.field("is_end_of_record", &self.is_end_of_record()); - s.field("is_out_of_band", &self.is_out_of_band()); - #[cfg(not(target_os = "espidf"))] - s.field("is_truncated", &self.is_truncated()); - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - s.field("is_confirm", &self.is_confirm()); - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "linux", target_os = "cygwin"), - ))] - s.field("is_dontroute", &self.is_dontroute()); - s.finish() - } -} - -#[repr(transparent)] -pub struct MaybeUninitSlice<'a> { - vec: libc::iovec, - _lifetime: PhantomData<&'a mut [MaybeUninit]>, -} - -unsafe impl<'a> Send for MaybeUninitSlice<'a> {} - -unsafe impl<'a> Sync for MaybeUninitSlice<'a> {} - -impl<'a> MaybeUninitSlice<'a> { - pub(crate) fn new(buf: &'a mut [MaybeUninit]) -> MaybeUninitSlice<'a> { - MaybeUninitSlice { - vec: libc::iovec { - iov_base: buf.as_mut_ptr().cast(), - iov_len: buf.len(), - }, - _lifetime: PhantomData, - } - } - - pub(crate) fn as_slice(&self) -> &[MaybeUninit] { - unsafe { slice::from_raw_parts(self.vec.iov_base.cast(), self.vec.iov_len) } - } - - pub(crate) fn as_mut_slice(&mut self) -> &mut [MaybeUninit] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base.cast(), self.vec.iov_len) } - } -} - -/// Returns the offset of the `sun_path` member of the passed unix socket address. -pub(crate) fn offset_of_path(storage: &libc::sockaddr_un) -> usize { - let base = storage as *const _ as usize; - let path = ptr::addr_of!(storage.sun_path) as usize; - path - base -} - -#[allow(unsafe_op_in_unsafe_fn)] -pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { - // SAFETY: a `sockaddr_storage` of all zeros is valid. - let mut storage = unsafe { mem::zeroed::() }; - let len = { - let storage = unsafe { &mut *ptr::addr_of_mut!(storage).cast::() }; - - let bytes = path.as_os_str().as_bytes(); - let too_long = match bytes.first() { - None => false, - // linux abstract namespaces aren't null-terminated - Some(&0) => bytes.len() > storage.sun_path.len(), - Some(_) => bytes.len() >= storage.sun_path.len(), - }; - if too_long { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "path must be shorter than SUN_LEN", - )); - } - - storage.sun_family = libc::AF_UNIX as sa_family_t; - // SAFETY: `bytes` and `addr.sun_path` are not overlapping and - // both point to valid memory. - // `storage` was initialized to zero above, so the path is - // already NULL terminated. - unsafe { - ptr::copy_nonoverlapping( - bytes.as_ptr(), - storage.sun_path.as_mut_ptr().cast(), - bytes.len(), - ); - } - - let sun_path_offset = offset_of_path(storage); - sun_path_offset - + bytes.len() - + match bytes.first() { - Some(&0) | None => 0, - Some(_) => 1, - } - }; - Ok(unsafe { SockAddr::new(storage, len as socklen_t) }) -} - -// Used in `MsgHdr`. -#[cfg(not(target_os = "redox"))] -pub(crate) use libc::msghdr; - -#[cfg(not(target_os = "redox"))] -pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) { - msg.msg_name = name.as_ptr() as *mut _; - msg.msg_namelen = name.len(); -} - -#[cfg(not(target_os = "redox"))] -#[allow(clippy::unnecessary_cast)] // IovLen type can be `usize`. -pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut libc::iovec, len: usize) { - msg.msg_iov = ptr; - msg.msg_iovlen = min(len, IovLen::MAX as usize) as IovLen; -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut libc::c_void, len: usize) { - msg.msg_control = ptr; - msg.msg_controllen = len as _; -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: libc::c_int) { - msg.msg_flags = flags; -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags { - RecvFlags(msg.msg_flags) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn msghdr_control_len(msg: &msghdr) -> usize { - msg.msg_controllen as _ -} - -/// Unix only API. -impl SockAddr { - /// Constructs a `SockAddr` with the family `AF_VSOCK` and the provided CID/port. - /// - /// # Errors - /// - /// This function can never fail. In a future version of this library it will be made - /// infallible. - #[allow(unsafe_op_in_unsafe_fn)] - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub fn vsock(cid: u32, port: u32) -> SockAddr { - // SAFETY: a `sockaddr_storage` of all zeros is valid. - let mut storage = unsafe { mem::zeroed::() }; - { - let storage: &mut libc::sockaddr_vm = - unsafe { &mut *((&mut storage as *mut sockaddr_storage).cast()) }; - storage.svm_family = libc::AF_VSOCK as sa_family_t; - storage.svm_cid = cid; - storage.svm_port = port; - } - unsafe { SockAddr::new(storage, mem::size_of::() as socklen_t) } - } - - /// Returns this address VSOCK CID/port if it is in the `AF_VSOCK` family, - /// otherwise return `None`. - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub fn as_vsock_address(&self) -> Option<(u32, u32)> { - if self.family() == libc::AF_VSOCK as sa_family_t { - // Safety: if the ss_family field is AF_VSOCK then storage must be a sockaddr_vm. - let addr = unsafe { &*(self.as_ptr() as *const libc::sockaddr_vm) }; - Some((addr.svm_cid, addr.svm_port)) - } else { - None - } - } - - /// Returns true if this address is an unnamed address from the `AF_UNIX` family (for local - /// interprocess communication), false otherwise. - pub fn is_unnamed(&self) -> bool { - self.as_sockaddr_un() - .map(|storage| { - self.len() == offset_of_path(storage) as _ - // On some non-linux platforms a zeroed path is returned for unnamed. - // Abstract addresses only exist on Linux. - // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. - // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 - || (cfg!(not(any(target_os = "linux", target_os = "android", target_os = "cygwin"))) - && storage.sun_path[0] == 0) - }) - .unwrap_or_default() - } - - /// Returns the underlying `sockaddr_un` object if this addres is from the `AF_UNIX` family, - /// otherwise returns `None`. - pub(crate) fn as_sockaddr_un(&self) -> Option<&libc::sockaddr_un> { - self.is_unix().then(|| { - // SAFETY: if unix socket, i.e. the `ss_family` field is `AF_UNIX` then storage must be - // a `sockaddr_un`. - unsafe { &*self.as_ptr().cast::() } - }) - } - - /// Get the length of the path bytes of the address, not including the terminating or initial - /// (for abstract names) null byte. - /// - /// Should not be called on unnamed addresses. - fn path_len(&self, storage: &libc::sockaddr_un) -> usize { - debug_assert!(!self.is_unnamed()); - self.len() as usize - offset_of_path(storage) - 1 - } - - /// Get a u8 slice for the bytes of the pathname or abstract name. - /// - /// Should not be called on unnamed addresses. - fn path_bytes(&self, storage: &libc::sockaddr_un, abstract_name: bool) -> &[u8] { - debug_assert!(!self.is_unnamed()); - // SAFETY: the pointed objects of type `i8` have the same memory layout as `u8`. The path is - // the last field in the storage and so its length is equal to - // TOTAL_LENGTH - OFFSET_OF_PATH -1 - // Where the 1 is either a terminating null if we have a pathname address, or the initial - // null byte, if it's an abstract name address. In the latter case, the path bytes start - // after the initial null byte, hence the `offset`. - // There is no safe way to convert a `&[i8]` to `&[u8]` - unsafe { - slice::from_raw_parts( - (storage.sun_path.as_ptr() as *const u8).offset(abstract_name as isize), - self.path_len(storage), - ) - } - } - - /// Returns this address as Unix `SocketAddr` if it is an `AF_UNIX` pathname - /// address, otherwise returns `None`. - pub fn as_unix(&self) -> Option { - let path = self.as_pathname()?; - // SAFETY: we can represent this as a valid pathname, then so can the - // standard library. - Some(std::os::unix::net::SocketAddr::from_pathname(path).unwrap()) - } - - /// Returns this address as a `Path` reference if it is an `AF_UNIX` - /// pathname address, otherwise returns `None`. - pub fn as_pathname(&self) -> Option<&Path> { - self.as_sockaddr_un().and_then(|storage| { - (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] != 0).then(|| { - let path_slice = self.path_bytes(storage, false); - Path::new::(OsStrExt::from_bytes(path_slice)) - }) - }) - } - - /// Returns this address as a slice of bytes representing an abstract address if it is an - /// `AF_UNIX` abstract address, otherwise returns `None`. - /// - /// Abstract addresses are a Linux extension, so this method returns `None` on all non-Linux - /// platforms. - pub fn as_abstract_namespace(&self) -> Option<&[u8]> { - // NOTE: although Fuchsia does define `AF_UNIX` it's not actually implemented. - // See https://github.com/rust-lang/socket2/pull/403#discussion_r1123557978 - #[cfg(any(target_os = "linux", target_os = "android", target_os = "cygwin"))] - { - self.as_sockaddr_un().and_then(|storage| { - (self.len() > offset_of_path(storage) as _ && storage.sun_path[0] == 0) - .then(|| self.path_bytes(storage, true)) - }) - } - #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "cygwin")))] - None - } -} - -pub(crate) type Socket = c_int; - -pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner { - crate::socket::Inner::from_raw_fd(socket) -} - -pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket { - socket.as_raw_fd() -} - -pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket { - socket.into_raw_fd() -} - -pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result { - syscall!(socket(family, ty, protocol)) -} - -#[cfg(all(feature = "all", unix))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))] -pub(crate) fn socketpair(family: c_int, ty: c_int, protocol: c_int) -> io::Result<[Socket; 2]> { - let mut fds = [0, 0]; - syscall!(socketpair(family, ty, protocol, fds.as_mut_ptr())).map(|_| fds) -} - -pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> { - syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ()) -} - -pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> { - syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ()) -} - -pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> { - let start = Instant::now(); - - let mut pollfd = libc::pollfd { - fd: socket.as_raw(), - events: libc::POLLIN | libc::POLLOUT, - revents: 0, - }; - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::ErrorKind::TimedOut.into()); - } - - let timeout = (timeout - elapsed).as_millis(); - let timeout = timeout.clamp(1, c_int::MAX as u128) as c_int; - - match syscall!(poll(&mut pollfd, 1, timeout)) { - Ok(0) => return Err(io::ErrorKind::TimedOut.into()), - Ok(_) => { - // Error or hang up indicates an error (or failure to connect). - if (pollfd.revents & libc::POLLHUP) != 0 || (pollfd.revents & libc::POLLERR) != 0 { - match socket.take_error() { - Ok(Some(err)) | Err(err) => return Err(err), - Ok(None) => { - return Err(io::Error::new( - io::ErrorKind::Other, - "no error set after POLLHUP", - )) - } - } - } - return Ok(()); - } - // Got interrupted, try again. - Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, - Err(err) => return Err(err), - } - } -} - -pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> { - syscall!(listen(fd, backlog)).map(|_| ()) -} - -pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> { - // Safety: `accept` initialises the `SockAddr` for us. - unsafe { SockAddr::try_init(|storage, len| syscall!(accept(fd, storage.cast(), len))) } -} - -pub(crate) fn getsockname(fd: Socket) -> io::Result { - // Safety: `accept` initialises the `SockAddr` for us. - unsafe { SockAddr::try_init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) } - .map(|(_, addr)| addr) -} - -pub(crate) fn getpeername(fd: Socket) -> io::Result { - // Safety: `accept` initialises the `SockAddr` for us. - unsafe { SockAddr::try_init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) } - .map(|(_, addr)| addr) -} - -pub(crate) fn try_clone(fd: Socket) -> io::Result { - syscall!(fcntl(fd, libc::F_DUPFD_CLOEXEC, 0)) -} - -#[cfg(all(feature = "all", unix, not(target_os = "vita")))] -pub(crate) fn nonblocking(fd: Socket) -> io::Result { - let file_status_flags = fcntl_get(fd, libc::F_GETFL)?; - Ok((file_status_flags & libc::O_NONBLOCK) != 0) -} - -#[cfg(all(feature = "all", target_os = "vita"))] -pub(crate) fn nonblocking(fd: Socket) -> io::Result { - unsafe { - getsockopt::(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK).map(|non_block| non_block != 0) - } -} - -#[cfg(not(target_os = "vita"))] -pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { - if nonblocking { - fcntl_add(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK) - } else { - fcntl_remove(fd, libc::F_GETFL, libc::F_SETFL, libc::O_NONBLOCK) - } -} - -#[cfg(target_os = "vita")] -pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { - unsafe { - setsockopt( - fd, - libc::SOL_SOCKET, - libc::SO_NONBLOCK, - nonblocking as libc::c_int, - ) - } -} - -pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - syscall!(shutdown(fd, how)).map(|_| ()) -} - -pub(crate) fn recv(fd: Socket, buf: &mut [MaybeUninit], flags: c_int) -> io::Result { - syscall!(recv( - fd, - buf.as_mut_ptr().cast(), - min(buf.len(), MAX_BUF_LEN), - flags, - )) - .map(|n| n as usize) -} - -pub(crate) fn recv_from( - fd: Socket, - buf: &mut [MaybeUninit], - flags: c_int, -) -> io::Result<(usize, SockAddr)> { - // Safety: `recvfrom` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|addr, addrlen| { - syscall!(recvfrom( - fd, - buf.as_mut_ptr().cast(), - min(buf.len(), MAX_BUF_LEN), - flags, - addr.cast(), - addrlen - )) - .map(|n| n as usize) - }) - } -} - -pub(crate) fn peek_sender(fd: Socket) -> io::Result { - // Unix-like platforms simply truncate the returned data, so this implementation is trivial. - // However, for Windows this requires suppressing the `WSAEMSGSIZE` error, - // so that requires a different approach. - // NOTE: macOS does not populate `sockaddr` if you pass a zero-sized buffer. - let (_, sender) = recv_from(fd, &mut [MaybeUninit::uninit(); 8], MSG_PEEK)?; - Ok(sender) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn recv_vectored( - fd: Socket, - bufs: &mut [crate::MaybeUninitSlice<'_>], - flags: c_int, -) -> io::Result<(usize, RecvFlags)> { - let mut msg = MsgHdrMut::new().with_buffers(bufs); - let n = recvmsg(fd, &mut msg, flags)?; - Ok((n, msg.flags())) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn recv_from_vectored( - fd: Socket, - bufs: &mut [crate::MaybeUninitSlice<'_>], - flags: c_int, -) -> io::Result<(usize, RecvFlags, SockAddr)> { - let mut msg = MsgHdrMut::new().with_buffers(bufs); - // SAFETY: `recvmsg` initialises the address storage and we set the length - // manually. - let (n, addr) = unsafe { - SockAddr::try_init(|storage, len| { - msg.inner.msg_name = storage.cast(); - msg.inner.msg_namelen = *len; - let n = recvmsg(fd, &mut msg, flags)?; - // Set the correct address length. - *len = msg.inner.msg_namelen; - Ok(n) - })? - }; - Ok((n, msg.flags(), addr)) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn recvmsg( - fd: Socket, - msg: &mut MsgHdrMut<'_, '_, '_>, - flags: c_int, -) -> io::Result { - syscall!(recvmsg(fd, &mut msg.inner, flags)).map(|n| n as usize) -} - -pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result { - syscall!(send( - fd, - buf.as_ptr().cast(), - min(buf.len(), MAX_BUF_LEN), - flags, - )) - .map(|n| n as usize) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn send_vectored(fd: Socket, bufs: &[IoSlice<'_>], flags: c_int) -> io::Result { - let msg = MsgHdr::new().with_buffers(bufs); - sendmsg(fd, &msg, flags) -} - -pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result { - syscall!(sendto( - fd, - buf.as_ptr().cast(), - min(buf.len(), MAX_BUF_LEN), - flags, - addr.as_ptr(), - addr.len(), - )) - .map(|n| n as usize) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn send_to_vectored( - fd: Socket, - bufs: &[IoSlice<'_>], - addr: &SockAddr, - flags: c_int, -) -> io::Result { - let msg = MsgHdr::new().with_addr(addr).with_buffers(bufs); - sendmsg(fd, &msg, flags) -} - -#[cfg(not(target_os = "redox"))] -pub(crate) fn sendmsg(fd: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result { - syscall!(sendmsg(fd, &msg.inner, flags)).map(|n| n as usize) -} - -/// Wrapper around `getsockopt` to deal with platform specific timeouts. -pub(crate) fn timeout_opt(fd: Socket, opt: c_int, val: c_int) -> io::Result> { - unsafe { getsockopt(fd, opt, val).map(from_timeval) } -} - -const fn from_timeval(duration: libc::timeval) -> Option { - if duration.tv_sec == 0 && duration.tv_usec == 0 { - None - } else { - let sec = duration.tv_sec as u64; - let nsec = (duration.tv_usec as u32) * 1000; - Some(Duration::new(sec, nsec)) - } -} - -/// Wrapper around `setsockopt` to deal with platform specific timeouts. -pub(crate) fn set_timeout_opt( - fd: Socket, - opt: c_int, - val: c_int, - duration: Option, -) -> io::Result<()> { - let duration = into_timeval(duration); - unsafe { setsockopt(fd, opt, val, duration) } -} - -fn into_timeval(duration: Option) -> libc::timeval { - match duration { - // https://github.com/rust-lang/libc/issues/1848 - #[cfg_attr(target_env = "musl", allow(deprecated))] - Some(duration) => libc::timeval { - tv_sec: min(duration.as_secs(), libc::time_t::MAX as u64) as libc::time_t, - tv_usec: duration.subsec_micros() as libc::suseconds_t, - }, - None => libc::timeval { - tv_sec: 0, - tv_usec: 0, - }, - } -} - -#[cfg(all( - feature = "all", - not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita")) -))] -#[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - not(any(target_os = "haiku", target_os = "openbsd", target_os = "vita")) - ))) -)] -pub(crate) fn keepalive_time(fd: Socket) -> io::Result { - unsafe { - getsockopt::(fd, IPPROTO_TCP, KEEPALIVE_TIME) - .map(|secs| Duration::from_secs(secs as u64)) - } -} - -#[allow(unused_variables)] -pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> { - #[cfg(not(any( - target_os = "haiku", - target_os = "openbsd", - target_os = "nto", - target_os = "vita" - )))] - if let Some(time) = keepalive.time { - let secs = into_secs(time); - unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? } - } - - #[cfg(any( - target_os = "aix", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "hurd", - target_os = "illumos", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "tvos", - target_os = "watchos", - target_os = "cygwin", - ))] - { - if let Some(interval) = keepalive.interval { - let secs = into_secs(interval); - unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? } - } - - if let Some(retries) = keepalive.retries { - unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? } - } - } - - #[cfg(target_os = "nto")] - if let Some(time) = keepalive.time { - let secs = into_timeval(Some(time)); - unsafe { setsockopt(fd, libc::IPPROTO_TCP, KEEPALIVE_TIME, secs)? } - } - - Ok(()) -} - -#[cfg(not(any( - target_os = "haiku", - target_os = "openbsd", - target_os = "nto", - target_os = "vita" -)))] -fn into_secs(duration: Duration) -> c_int { - min(duration.as_secs(), c_int::MAX as u64) as c_int -} - -/// Get the flags using `cmd`. -#[cfg(not(target_os = "vita"))] -fn fcntl_get(fd: Socket, cmd: c_int) -> io::Result { - syscall!(fcntl(fd, cmd)) -} - -/// Add `flag` to the current set flags of `F_GETFD`. -#[cfg(not(target_os = "vita"))] -fn fcntl_add(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> { - let previous = fcntl_get(fd, get_cmd)?; - let new = previous | flag; - if new != previous { - syscall!(fcntl(fd, set_cmd, new)).map(|_| ()) - } else { - // Flag was already set. - Ok(()) - } -} - -/// Remove `flag` to the current set flags of `F_GETFD`. -#[cfg(not(target_os = "vita"))] -fn fcntl_remove(fd: Socket, get_cmd: c_int, set_cmd: c_int, flag: c_int) -> io::Result<()> { - let previous = fcntl_get(fd, get_cmd)?; - let new = previous & !flag; - if new != previous { - syscall!(fcntl(fd, set_cmd, new)).map(|_| ()) - } else { - // Flag was already set. - Ok(()) - } -} - -/// Caller must ensure `T` is the correct type for `opt` and `val`. -pub(crate) unsafe fn getsockopt(fd: Socket, opt: c_int, val: c_int) -> io::Result { - let mut payload: MaybeUninit = MaybeUninit::uninit(); - let mut len = size_of::() as libc::socklen_t; - syscall!(getsockopt( - fd, - opt, - val, - payload.as_mut_ptr().cast(), - &mut len, - )) - .map(|_| { - debug_assert_eq!(len as usize, size_of::()); - // Safety: `getsockopt` initialised `payload` for us. - payload.assume_init() - }) -} - -/// Caller must ensure `T` is the correct type for `opt` and `val`. -pub(crate) unsafe fn setsockopt( - fd: Socket, - opt: c_int, - val: c_int, - payload: T, -) -> io::Result<()> { - let payload = ptr::addr_of!(payload).cast(); - syscall!(setsockopt( - fd, - opt, - val, - payload, - mem::size_of::() as libc::socklen_t, - )) - .map(|_| ()) -} - -pub(crate) const fn to_in_addr(addr: &Ipv4Addr) -> in_addr { - // `s_addr` is stored as BE on all machines, and the array is in BE order. - // So the native endian conversion method is used so that it's never - // swapped. - in_addr { - s_addr: u32::from_ne_bytes(addr.octets()), - } -} - -pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr { - Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()) -} - -pub(crate) const fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr { - in6_addr { - s6_addr: addr.octets(), - } -} - -pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr { - Ipv6Addr::from(addr.s6_addr) -} - -#[cfg(not(any( - target_os = "aix", - target_os = "haiku", - target_os = "illumos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "redox", - target_os = "solaris", - target_os = "nto", - target_os = "espidf", - target_os = "vita", - target_os = "cygwin", -)))] -pub(crate) const fn to_mreqn( - multiaddr: &Ipv4Addr, - interface: &crate::socket::InterfaceIndexOrAddress, -) -> libc::ip_mreqn { - match interface { - crate::socket::InterfaceIndexOrAddress::Index(interface) => libc::ip_mreqn { - imr_multiaddr: to_in_addr(multiaddr), - imr_address: to_in_addr(&Ipv4Addr::UNSPECIFIED), - imr_ifindex: *interface as _, - }, - crate::socket::InterfaceIndexOrAddress::Address(interface) => libc::ip_mreqn { - imr_multiaddr: to_in_addr(multiaddr), - imr_address: to_in_addr(interface), - imr_ifindex: 0, - }, - } -} - -#[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") -))] -pub(crate) fn original_dst(fd: Socket) -> io::Result { - // Safety: `getsockopt` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!(getsockopt( - fd, - libc::SOL_IP, - libc::SO_ORIGINAL_DST, - storage.cast(), - len - )) - }) - } - .map(|(_, addr)| addr) -} - -/// Get the value for the `IP6T_SO_ORIGINAL_DST` option on this socket. -/// -/// This value contains the original destination IPv6 address of the connection -/// redirected using `ip6tables` `REDIRECT` or `TPROXY`. -#[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] -pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result { - // Safety: `getsockopt` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!(getsockopt( - fd, - libc::SOL_IPV6, - libc::IP6T_SO_ORIGINAL_DST, - storage.cast(), - len - )) - }) - } - .map(|(_, addr)| addr) -} - -/// Unix only API. -impl crate::Socket { - /// Accept a new incoming connection from this listener. - /// - /// This function directly corresponds to the `accept4(2)` function. - /// - /// This function will block the calling thread until a new connection is - /// established. When established, the corresponding `Socket` and the remote - /// peer's address will be returned. - #[doc = man_links!(unix: accept4(2))] - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - ) - ))) - )] - pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { - self._accept4(flags) - } - - #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "illumos", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - ))] - pub(crate) fn _accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { - // Safety: `accept4` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!(accept4(self.as_raw(), storage.cast(), len, flags)) - .map(crate::Socket::from_raw) - }) - } - } - - /// Sets `CLOEXEC` on the socket. - /// - /// # Notes - /// - /// On supported platforms you can use [`Type::cloexec`]. - #[cfg_attr( - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos" - ), - allow(rustdoc::broken_intra_doc_links) - )] - #[cfg(all(feature = "all", not(target_os = "vita")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix))))] - pub fn set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { - self._set_cloexec(close_on_exec) - } - - #[cfg(not(target_os = "vita"))] - pub(crate) fn _set_cloexec(&self, close_on_exec: bool) -> io::Result<()> { - if close_on_exec { - fcntl_add( - self.as_raw(), - libc::F_GETFD, - libc::F_SETFD, - libc::FD_CLOEXEC, - ) - } else { - fcntl_remove( - self.as_raw(), - libc::F_GETFD, - libc::F_SETFD, - libc::FD_CLOEXEC, - ) - } - } - - /// Sets `SO_PEERCRED` to null on the socket. - /// - /// This is a Cygwin extension. - /// - /// Normally the Unix domain sockets of Cygwin are implemented by TCP sockets, - /// so it performs a handshake on `connect` and `accept` to verify the remote - /// connection and exchange peer cred info. At the time of writing, this - /// means that `connect` on a Unix domain socket will block until the server - /// calls `accept` on Cygwin. This behavior is inconsistent with most other - /// platforms, and this option can be used to disable that. - /// - /// See also: the [mailing list](https://inbox.sourceware.org/cygwin/TYCPR01MB10926FF8926CA63704867ADC8F8AA2@TYCPR01MB10926.jpnprd01.prod.outlook.com/) - #[cfg(target_os = "cygwin")] - #[cfg(any(doc, target_os = "cygwin"))] - pub fn set_no_peercred(&self) -> io::Result<()> { - syscall!(setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_PEERCRED, - ptr::null_mut(), - 0, - )) - .map(|_| ()) - } - - /// Sets `SO_NOSIGPIPE` on the socket. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> { - self._set_nosigpipe(nosigpipe) - } - - #[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ))] - pub(crate) fn _set_nosigpipe(&self, nosigpipe: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_NOSIGPIPE, - nosigpipe as c_int, - ) - } - } - - /// Gets the value of the `TCP_MAXSEG` option on this socket. - /// - /// For more information about this option, see [`set_mss`]. - /// - /// [`set_mss`]: crate::Socket::set_mss - #[cfg(all(feature = "all", not(target_os = "redox")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))] - pub fn mss(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_MAXSEG) - .map(|mss| mss as u32) - } - } - - /// Sets the value of the `TCP_MAXSEG` option on this socket. - /// - /// The `TCP_MAXSEG` option denotes the TCP Maximum Segment Size and is only - /// available on TCP sockets. - #[cfg(all(feature = "all", not(target_os = "redox")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", unix, not(target_os = "redox")))))] - pub fn set_mss(&self, mss: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_MAXSEG, - mss as c_int, - ) - } - } - - /// Returns `true` if `listen(2)` was called on this socket by checking the - /// `SO_ACCEPTCONN` option on this socket. - #[cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))) - )] - pub fn is_listener(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN) - .map(|v| v != 0) - } - } - - /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option - /// on this socket. - #[cfg(all( - feature = "all", - any( - target_os = "android", - // TODO: add FreeBSD. - // target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))] - #[cfg_attr(docsrs, doc(cfg(all( - feature = "all", - any( - target_os = "android", - // TODO: add FreeBSD. - // target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))))] - pub fn domain(&self) -> io::Result { - unsafe { getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) } - } - - /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL` - /// option on this socket. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - ) - ))) - )] - pub fn protocol(&self) -> io::Result> { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v - { - 0 => None, - p => Some(Protocol(p)), - }) - } - } - - /// Gets the value for the `SO_MARK` option on this socket. - /// - /// This value gets the socket mark field for each packet sent through - /// this socket. - /// - /// On Linux this function requires the `CAP_NET_ADMIN` capability. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn mark(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_MARK) - .map(|mark| mark as u32) - } - } - - /// Sets the value for the `SO_MARK` option on this socket. - /// - /// This value sets the socket mark field for each packet sent through - /// this socket. Changing the mark can be used for mark-based routing - /// without netfilter or for packet filtering. - /// - /// On Linux this function requires the `CAP_NET_ADMIN` capability. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn set_mark(&self, mark: u32) -> io::Result<()> { - unsafe { - setsockopt::( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_MARK, - mark as c_int, - ) - } - } - - /// Get the value of the `TCP_CORK` option on this socket. - /// - /// For more information about this option, see [`set_cork`]. - /// - /// [`set_cork`]: crate::Socket::set_cork - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn cork(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_CORK) - .map(|cork| cork != 0) - } - } - - /// Set the value of the `TCP_CORK` option on this socket. - /// - /// If set, don't send out partial frames. All queued partial frames are - /// sent when the option is cleared again. There is a 200 millisecond ceiling on - /// the time for which output is corked by `TCP_CORK`. If this ceiling is reached, - /// then queued data is automatically transmitted. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn set_cork(&self, cork: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_CORK, - cork as c_int, - ) - } - } - - /// Get the value of the `TCP_QUICKACK` option on this socket. - /// - /// For more information about this option, see [`set_quickack`]. - /// - /// [`set_quickack`]: crate::Socket::set_quickack - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin" - ) - ))) - )] - pub fn quickack(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_QUICKACK) - .map(|quickack| quickack != 0) - } - } - - /// Set the value of the `TCP_QUICKACK` option on this socket. - /// - /// If set, acks are sent immediately, rather than delayed if needed in accordance to normal - /// TCP operation. This flag is not permanent, it only enables a switch to or from quickack mode. - /// Subsequent operation of the TCP protocol will once again enter/leave quickack mode depending on - /// internal protocol processing and factors such as delayed ack timeouts occurring and data transfer. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin" - ) - ))) - )] - pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_QUICKACK, - quickack as c_int, - ) - } - } - - /// Get the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket. - /// - /// For more information about this option, see [`set_thin_linear_timeouts`]. - /// - /// [`set_thin_linear_timeouts`]: crate::Socket::set_thin_linear_timeouts - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn thin_linear_timeouts(&self) -> io::Result { - unsafe { - getsockopt::( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_THIN_LINEAR_TIMEOUTS, - ) - .map(|timeouts| timeouts != 0) - } - } - - /// Set the value of the `TCP_THIN_LINEAR_TIMEOUTS` option on this socket. - /// - /// If set, the kernel will dynamically detect a thin-stream connection if there are less than four packets in flight. - /// With less than four packets in flight the normal TCP fast retransmission will not be effective. - /// The kernel will modify the retransmission to avoid the very high latencies that thin stream suffer because of exponential backoff. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn set_thin_linear_timeouts(&self, timeouts: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_THIN_LINEAR_TIMEOUTS, - timeouts as c_int, - ) - } - } - - /// Gets the value for the `SO_BINDTODEVICE` option on this socket. - /// - /// This value gets the socket binded device's interface name. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn device(&self) -> io::Result>> { - // TODO: replace with `MaybeUninit::uninit_array` once stable. - let mut buf: [MaybeUninit; libc::IFNAMSIZ] = - unsafe { MaybeUninit::uninit().assume_init() }; - let mut len = buf.len() as libc::socklen_t; - syscall!(getsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_BINDTODEVICE, - buf.as_mut_ptr().cast(), - &mut len, - ))?; - if len == 0 { - Ok(None) - } else { - let buf = &buf[..len as usize - 1]; - // TODO: use `MaybeUninit::slice_assume_init_ref` once stable. - Ok(Some(unsafe { &*(buf as *const [_] as *const [u8]) }.into())) - } - } - - /// Sets the value for the `SO_BINDTODEVICE` option on this socket. - /// - /// If a socket is bound to an interface, only packets received from that - /// particular interface are processed by the socket. Note that this only - /// works for some socket types, particularly `AF_INET` sockets. - /// - /// If `interface` is `None` or an empty string it removes the binding. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> { - let (value, len) = if let Some(interface) = interface { - (interface.as_ptr(), interface.len()) - } else { - (ptr::null(), 0) - }; - syscall!(setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_BINDTODEVICE, - value.cast(), - len as libc::socklen_t, - )) - .map(|_| ()) - } - - /// Sets the value for the `SO_SETFIB` option on this socket. - /// - /// Bind socket to the specified forwarding table (VRF) on a FreeBSD. - #[cfg(all(feature = "all", target_os = "freebsd"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "freebsd"))))] - pub fn set_fib(&self, fib: u32) -> io::Result<()> { - syscall!(setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_SETFIB, - (&fib as *const u32).cast(), - mem::size_of::() as libc::socklen_t, - )) - .map(|_| ()) - } - - /// This method is deprecated, use [`crate::Socket::bind_device_by_index_v4`]. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - #[deprecated = "Use `Socket::bind_device_by_index_v4` instead"] - pub fn bind_device_by_index(&self, interface: Option) -> io::Result<()> { - self.bind_device_by_index_v4(interface) - } - - /// Sets the value for `IP_BOUND_IF` option on this socket. - /// - /// If a socket is bound to an interface, only packets received from that - /// particular interface are processed by the socket. - /// - /// If `interface` is `None`, the binding is removed. If the `interface` - /// index is not valid, an error is returned. - /// - /// One can use [`libc::if_nametoindex`] to convert an interface alias to an - /// index. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "illumos", - target_os = "solaris", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn bind_device_by_index_v4(&self, interface: Option) -> io::Result<()> { - let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) } - } - - /// Sets the value for `IPV6_BOUND_IF` option on this socket. - /// - /// If a socket is bound to an interface, only packets received from that - /// particular interface are processed by the socket. - /// - /// If `interface` is `None`, the binding is removed. If the `interface` - /// index is not valid, an error is returned. - /// - /// One can use [`libc::if_nametoindex`] to convert an interface alias to an - /// index. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "illumos", - target_os = "solaris", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn bind_device_by_index_v6(&self, interface: Option) -> io::Result<()> { - let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) } - } - - /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index - /// for the interface to which the socket is bound. - /// - /// Returns `None` if the socket is not bound to any interface, otherwise - /// returns an interface index. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "illumos", - target_os = "solaris", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn device_index_v4(&self) -> io::Result> { - let index = - unsafe { getsockopt::(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? }; - Ok(NonZeroU32::new(index)) - } - - /// This method is deprecated, use [`crate::Socket::device_index_v4`]. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - #[deprecated = "Use `Socket::device_index_v4` instead"] - pub fn device_index(&self) -> io::Result> { - self.device_index_v4() - } - - /// Gets the value for `IPV6_BOUND_IF` option on this socket, i.e. the index - /// for the interface to which the socket is bound. - /// - /// Returns `None` if the socket is not bound to any interface, otherwise - /// returns an interface index. - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - target_os = "illumos", - target_os = "solaris", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn device_index_v6(&self) -> io::Result> { - let index = unsafe { - getsockopt::(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)? - }; - Ok(NonZeroU32::new(index)) - } - - /// Get the value of the `SO_INCOMING_CPU` option on this socket. - /// - /// For more information about this option, see [`set_cpu_affinity`]. - /// - /// [`set_cpu_affinity`]: crate::Socket::set_cpu_affinity - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn cpu_affinity(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_INCOMING_CPU) - .map(|cpu| cpu as usize) - } - } - - /// Set value for the `SO_INCOMING_CPU` option on this socket. - /// - /// Sets the CPU affinity of the socket. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_cpu_affinity(&self, cpu: usize) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_INCOMING_CPU, - cpu as c_int, - ) - } - } - - /// Get the value of the `SO_REUSEPORT` option on this socket. - /// - /// For more information about this option, see [`set_reuse_port`]. - /// - /// [`set_reuse_port`]: crate::Socket::set_reuse_port - #[cfg(all( - feature = "all", - not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - unix, - not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) - ))) - )] - pub fn reuse_port(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT) - .map(|reuse| reuse != 0) - } - } - - /// Set value for the `SO_REUSEPORT` option on this socket. - /// - /// This indicates that further calls to `bind` may allow reuse of local - /// addresses. For IPv4 sockets this means that a socket may bind even when - /// there's a socket already listening on this port. - #[cfg(all( - feature = "all", - not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - unix, - not(any(target_os = "solaris", target_os = "illumos", target_os = "cygwin")) - ))) - )] - pub fn set_reuse_port(&self, reuse: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_REUSEPORT, - reuse as c_int, - ) - } - } - - /// Get the value of the `SO_REUSEPORT_LB` option on this socket. - /// - /// For more information about this option, see [`set_reuse_port_lb`]. - /// - /// [`set_reuse_port_lb`]: crate::Socket::set_reuse_port_lb - #[cfg(all(feature = "all", target_os = "freebsd"))] - pub fn reuse_port_lb(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_REUSEPORT_LB) - .map(|reuse| reuse != 0) - } - } - - /// Set value for the `SO_REUSEPORT_LB` option on this socket. - /// - /// This allows multiple programs or threads to bind to the same port and - /// incoming connections will be load balanced using a hash function. - #[cfg(all(feature = "all", target_os = "freebsd"))] - pub fn set_reuse_port_lb(&self, reuse: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_REUSEPORT_LB, - reuse as c_int, - ) - } - } - - /// Get the value of the `IP_FREEBIND` option on this socket. - /// - /// For more information about this option, see [`set_freebind`]. - /// - /// [`set_freebind`]: crate::Socket::set_freebind - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn freebind(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_IP, libc::IP_FREEBIND) - .map(|freebind| freebind != 0) - } - } - - /// Set value for the `IP_FREEBIND` option on this socket. - /// - /// If enabled, this boolean option allows binding to an IP address that is - /// nonlocal or does not (yet) exist. This permits listening on a socket, - /// without requiring the underlying network interface or the specified - /// dynamic IP address to be up at the time that the application is trying - /// to bind to it. - #[cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any(target_os = "android", target_os = "fuchsia", target_os = "linux") - ))) - )] - pub fn set_freebind(&self, freebind: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_IP, - libc::IP_FREEBIND, - freebind as c_int, - ) - } - } - - /// Get the value of the `IPV6_FREEBIND` option on this socket. - /// - /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on - /// Android/Linux. For more information about this option, see - /// [`set_freebind`]. - /// - /// [`set_freebind`]: crate::Socket::set_freebind - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub fn freebind_ipv6(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), libc::SOL_IPV6, libc::IPV6_FREEBIND) - .map(|freebind| freebind != 0) - } - } - - /// Set value for the `IPV6_FREEBIND` option on this socket. - /// - /// This is an IPv6 counterpart of `IP_FREEBIND` socket option on - /// Android/Linux. For more information about this option, see - /// [`set_freebind`]. - /// - /// [`set_freebind`]: crate::Socket::set_freebind - /// - /// # Examples - /// - /// On Linux: - /// - /// ``` - /// use socket2::{Domain, Socket, Type}; - /// use std::io::{self, Error, ErrorKind}; - /// - /// fn enable_freebind(socket: &Socket) -> io::Result<()> { - /// match socket.domain()? { - /// Domain::IPV4 => socket.set_freebind(true)?, - /// Domain::IPV6 => socket.set_freebind_ipv6(true)?, - /// _ => return Err(Error::new(ErrorKind::Other, "unsupported domain")), - /// }; - /// Ok(()) - /// } - /// - /// # fn main() -> io::Result<()> { - /// # let socket = Socket::new(Domain::IPV6, Type::STREAM, None)?; - /// # enable_freebind(&socket) - /// # } - /// ``` - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))) - )] - pub fn set_freebind_ipv6(&self, freebind: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_IPV6, - libc::IPV6_FREEBIND, - freebind as c_int, - ) - } - } - - /// Copies data between a `file` and this socket using the `sendfile(2)` - /// system call. Because this copying is done within the kernel, - /// `sendfile()` is more efficient than the combination of `read(2)` and - /// `write(2)`, which would require transferring data to and from user - /// space. - /// - /// Different OSs support different kinds of `file`s, see the OS - /// documentation for what kind of files are supported. Generally *regular* - /// files are supported by all OSs. - #[doc = man_links!(unix: sendfile(2))] - /// - /// The `offset` is the absolute offset into the `file` to use as starting - /// point. - /// - /// Depending on the OS this function *may* change the offset of `file`. For - /// the best results reset the offset of the file before using it again. - /// - /// The `length` determines how many bytes to send, where a length of `None` - /// means it will try to send all bytes. - #[cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "aix", - target_os = "android", - target_os = "freebsd", - target_os = "ios", - target_os = "visionos", - target_os = "linux", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))) - )] - pub fn sendfile( - &self, - file: &F, - offset: usize, - length: Option, - ) -> io::Result - where - F: AsRawFd, - { - self._sendfile(file.as_raw_fd(), offset as _, length) - } - - #[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) - ))] - fn _sendfile( - &self, - file: RawFd, - offset: libc::off_t, - length: Option, - ) -> io::Result { - // On macOS `length` is value-result parameter. It determines the number - // of bytes to write and returns the number of bytes written. - let mut length = match length { - Some(n) => n.get() as libc::off_t, - // A value of `0` means send all bytes. - None => 0, - }; - syscall!(sendfile( - file, - self.as_raw(), - offset, - &mut length, - ptr::null_mut(), - 0, - )) - .map(|_| length as usize) - } - - #[cfg(all(feature = "all", any(target_os = "android", target_os = "linux")))] - fn _sendfile( - &self, - file: RawFd, - offset: libc::off_t, - length: Option, - ) -> io::Result { - let count = match length { - Some(n) => n.get() as libc::size_t, - // The maximum the Linux kernel will write in a single call. - None => 0x7ffff000, // 2,147,479,552 bytes. - }; - let mut offset = offset; - syscall!(sendfile(self.as_raw(), file, &mut offset, count)).map(|n| n as usize) - } - - #[cfg(all(feature = "all", target_os = "freebsd"))] - fn _sendfile( - &self, - file: RawFd, - offset: libc::off_t, - length: Option, - ) -> io::Result { - let nbytes = match length { - Some(n) => n.get() as libc::size_t, - // A value of `0` means send all bytes. - None => 0, - }; - let mut sbytes: libc::off_t = 0; - syscall!(sendfile( - file, - self.as_raw(), - offset, - nbytes, - ptr::null_mut(), - &mut sbytes, - 0, - )) - .map(|_| sbytes as usize) - } - - #[cfg(all(feature = "all", target_os = "aix"))] - fn _sendfile( - &self, - file: RawFd, - offset: libc::off_t, - length: Option, - ) -> io::Result { - let nbytes = match length { - Some(n) => n.get() as i64, - None => -1, - }; - let mut params = libc::sf_parms { - header_data: ptr::null_mut(), - header_length: 0, - file_descriptor: file, - file_size: 0, - file_offset: offset as u64, - file_bytes: nbytes, - trailer_data: ptr::null_mut(), - trailer_length: 0, - bytes_sent: 0, - }; - // AIX doesn't support SF_REUSE, socket will be closed after successful transmission. - syscall!(send_file( - &mut self.as_raw() as *mut _, - &mut params as *mut _, - libc::SF_CLOSE as libc::c_uint, - )) - .map(|_| params.bytes_sent as usize) - } - - /// Set the value of the `TCP_USER_TIMEOUT` option on this socket. - /// - /// If set, this specifies the maximum amount of time that transmitted data may remain - /// unacknowledged or buffered data may remain untransmitted before TCP will forcibly close the - /// corresponding connection. - /// - /// Setting `timeout` to `None` or a zero duration causes the system default timeouts to - /// be used. If `timeout` in milliseconds is larger than `c_uint::MAX`, the timeout is clamped - /// to `c_uint::MAX`. For example, when `c_uint` is a 32-bit value, this limits the timeout to - /// approximately 49.71 days. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin" - ) - ))) - )] - pub fn set_tcp_user_timeout(&self, timeout: Option) -> io::Result<()> { - let timeout = timeout.map_or(0, |to| { - min(to.as_millis(), libc::c_uint::MAX as u128) as libc::c_uint - }); - unsafe { - setsockopt( - self.as_raw(), - libc::IPPROTO_TCP, - libc::TCP_USER_TIMEOUT, - timeout, - ) - } - } - - /// Get the value of the `TCP_USER_TIMEOUT` option on this socket. - /// - /// For more information about this option, see [`set_tcp_user_timeout`]. - /// - /// [`set_tcp_user_timeout`]: crate::Socket::set_tcp_user_timeout - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "fuchsia", - target_os = "linux", - target_os = "cygwin" - ) - ))) - )] - pub fn tcp_user_timeout(&self) -> io::Result> { - unsafe { - getsockopt::(self.as_raw(), libc::IPPROTO_TCP, libc::TCP_USER_TIMEOUT) - .map(|millis| { - if millis == 0 { - None - } else { - Some(Duration::from_millis(millis as u64)) - } - }) - } - } - - /// Attach Berkeley Packet Filter(BPF) on this socket. - /// - /// BPF allows a user-space program to attach a filter onto any socket - /// and allow or disallow certain types of data to come through the socket. - /// - /// For more information about this option, see [filter](https://www.kernel.org/doc/html/v5.12/networking/filter.html) - #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))] - pub fn attach_filter(&self, filters: &[libc::sock_filter]) -> io::Result<()> { - let prog = libc::sock_fprog { - len: filters.len() as u16, - filter: filters.as_ptr() as *mut _, - }; - - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_SOCKET, - libc::SO_ATTACH_FILTER, - prog, - ) - } - } - - /// Detach Berkeley Packet Filter(BPF) from this socket. - /// - /// For more information about this option, see [`attach_filter`] - /// - /// [`attach_filter`]: crate::Socket::attach_filter - #[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))] - pub fn detach_filter(&self) -> io::Result<()> { - unsafe { setsockopt(self.as_raw(), libc::SOL_SOCKET, libc::SO_DETACH_FILTER, 0) } - } - - /// Gets the value for the `SO_COOKIE` option on this socket. - /// - /// The socket cookie is a unique, kernel-managed identifier tied to each socket. - /// Therefore, there is no corresponding `set` helper. - /// - /// For more information about this option, see [Linux patch](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5daab9db7b65df87da26fd8cfa695fb9546a1ddb) - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn cookie(&self) -> io::Result { - unsafe { getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_COOKIE) } - } - - /// Get the value of the `IPV6_TCLASS` option for this socket. - /// - /// For more information about this option, see [`set_tclass_v6`]. - /// - /// [`set_tclass_v6`]: crate::Socket::set_tclass_v6 - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ) - ))) - )] - pub fn tclass_v6(&self) -> io::Result { - unsafe { - getsockopt::(self.as_raw(), IPPROTO_IPV6, libc::IPV6_TCLASS) - .map(|tclass| tclass as u32) - } - } - - /// Set the value of the `IPV6_TCLASS` option for this socket. - /// - /// Specifies the traffic class field that is used in every packets - /// sent from this socket. - #[cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd", - target_os = "cygwin", - ) - ))] - #[cfg_attr( - docsrs, - doc(cfg(all( - feature = "all", - any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "fuchsia", - target_os = "linux", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ) - ))) - )] - pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - IPPROTO_IPV6, - libc::IPV6_TCLASS, - tclass as c_int, - ) - } - } - - /// Get the value of the `TCP_CONGESTION` option for this socket. - /// - /// For more information about this option, see [`set_tcp_congestion`]. - /// - /// [`set_tcp_congestion`]: crate::Socket::set_tcp_congestion - #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))) - )] - pub fn tcp_congestion(&self) -> io::Result> { - let mut payload: [u8; TCP_CA_NAME_MAX] = [0; TCP_CA_NAME_MAX]; - let mut len = payload.len() as libc::socklen_t; - syscall!(getsockopt( - self.as_raw(), - IPPROTO_TCP, - libc::TCP_CONGESTION, - payload.as_mut_ptr().cast(), - &mut len, - )) - .map(|_| payload[..len as usize].to_vec()) - } - - /// Set the value of the `TCP_CONGESTION` option for this socket. - /// - /// Specifies the TCP congestion control algorithm to use for this socket. - /// - /// The value must be a valid TCP congestion control algorithm name of the - /// platform. For example, Linux may supports "reno", "cubic". - #[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))) - )] - pub fn set_tcp_congestion(&self, tcp_ca_name: &[u8]) -> io::Result<()> { - syscall!(setsockopt( - self.as_raw(), - IPPROTO_TCP, - libc::TCP_CONGESTION, - tcp_ca_name.as_ptr() as *const _, - tcp_ca_name.len() as libc::socklen_t, - )) - .map(|_| ()) - } - - /// Set value for the `DCCP_SOCKOPT_SERVICE` option on this socket. - /// - /// Sets the DCCP service. The specification mandates use of service codes. - /// If this socket option is not set, the socket will fall back to 0 (which - /// means that no meaningful service code is present). On active sockets - /// this is set before [`connect`]. On passive sockets up to 32 service - /// codes can be set before calling [`bind`] - /// - /// [`connect`]: crate::Socket::connect - /// [`bind`]: crate::Socket::bind - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_service(&self, code: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_SERVICE, - code, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_SERVICE` option on this socket. - /// - /// For more information about this option see [`set_dccp_service`] - /// - /// [`set_dccp_service`]: crate::Socket::set_dccp_service - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_service(&self) -> io::Result { - unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SERVICE) } - } - - /// Set value for the `DCCP_SOCKOPT_CCID` option on this socket. - /// - /// This option sets both the TX and RX CCIDs at the same time. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_ccid(&self, ccid: u8) -> io::Result<()> { - unsafe { setsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_CCID, ccid) } - } - - /// Get the value of the `DCCP_SOCKOPT_TX_CCID` option on this socket. - /// - /// For more information about this option see [`set_dccp_ccid`]. - /// - /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_tx_ccid(&self) -> io::Result { - unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_TX_CCID) } - } - - /// Get the value of the `DCCP_SOCKOPT_RX_CCID` option on this socket. - /// - /// For more information about this option see [`set_dccp_ccid`]. - /// - /// [`set_dccp_ccid`]: crate::Socket::set_dccp_ccid - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_xx_ccid(&self) -> io::Result { - unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RX_CCID) } - } - - /// Set value for the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket. - /// - /// Enables a listening socket to hold timewait state when closing the - /// connection. This option must be set after `accept` returns. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_server_timewait(&self, hold_timewait: bool) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_SERVER_TIMEWAIT, - hold_timewait as c_int, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_SERVER_TIMEWAIT` option on this socket. - /// - /// For more information see [`set_dccp_server_timewait`] - /// - /// [`set_dccp_server_timewait`]: crate::Socket::set_dccp_server_timewait - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_server_timewait(&self) -> io::Result { - unsafe { - getsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_SERVER_TIMEWAIT, - ) - } - } - - /// Set value for the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket. - /// - /// Both this option and `DCCP_SOCKOPT_RECV_CSCOV` are used for setting the - /// partial checksum coverage. The default is that checksums always cover - /// the entire packet and that only fully covered application data is - /// accepted by the receiver. Hence, when using this feature on the sender, - /// it must be enabled at the receiver too, with suitable choice of CsCov. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_send_cscov(&self, level: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_SEND_CSCOV, - level, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_SEND_CSCOV` option on this socket. - /// - /// For more information on this option see [`set_dccp_send_cscov`]. - /// - /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_send_cscov(&self) -> io::Result { - unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_SEND_CSCOV) } - } - - /// Set the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket. - /// - /// This option is only useful when combined with [`set_dccp_send_cscov`]. - /// - /// [`set_dccp_send_cscov`]: crate::Socket::set_dccp_send_cscov - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_recv_cscov(&self, level: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_RECV_CSCOV, - level, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_RECV_CSCOV` option on this socket. - /// - /// For more information on this option see [`set_dccp_recv_cscov`]. - /// - /// [`set_dccp_recv_cscov`]: crate::Socket::set_dccp_recv_cscov - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_recv_cscov(&self) -> io::Result { - unsafe { getsockopt(self.as_raw(), libc::SOL_DCCP, libc::DCCP_SOCKOPT_RECV_CSCOV) } - } - - /// Set value for the `DCCP_SOCKOPT_QPOLICY_TXQLEN` option on this socket. - /// - /// This option sets the maximum length of the output queue. A zero value is - /// interpreted as unbounded queue length. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn set_dccp_qpolicy_txqlen(&self, length: u32) -> io::Result<()> { - unsafe { - setsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_QPOLICY_TXQLEN, - length, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_QPOLICY_TXQLEN` on this socket. - /// - /// For more information on this option see [`set_dccp_qpolicy_txqlen`]. - /// - /// [`set_dccp_qpolicy_txqlen`]: crate::Socket::set_dccp_qpolicy_txqlen - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_qpolicy_txqlen(&self) -> io::Result { - unsafe { - getsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_QPOLICY_TXQLEN, - ) - } - } - - /// Get the value of the `DCCP_SOCKOPT_AVAILABLE_CCIDS` option on this socket. - /// - /// Returns the list of CCIDs supported by the endpoint. - /// - /// The parameter `N` is used to get the maximum number of supported - /// endpoints. The [documentation] recommends a minimum of four at the time - /// of writing. - /// - /// [documentation]: https://www.kernel.org/doc/html/latest/networking/dccp.html - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_available_ccids(&self) -> io::Result> { - let mut endpoints = [0; N]; - let mut length = endpoints.len() as libc::socklen_t; - syscall!(getsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_AVAILABLE_CCIDS, - endpoints.as_mut_ptr().cast(), - &mut length, - ))?; - Ok(CcidEndpoints { endpoints, length }) - } - - /// Get the value of the `DCCP_SOCKOPT_GET_CUR_MPS` option on this socket. - /// - /// This option retrieves the current maximum packet size (application - /// payload size) in bytes. - #[cfg(all(feature = "all", target_os = "linux"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] - pub fn dccp_cur_mps(&self) -> io::Result { - unsafe { - getsockopt( - self.as_raw(), - libc::SOL_DCCP, - libc::DCCP_SOCKOPT_GET_CUR_MPS, - ) - } - } -} - -/// See [`Socket::dccp_available_ccids`]. -#[cfg(all(feature = "all", target_os = "linux"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] -#[derive(Debug)] -pub struct CcidEndpoints { - endpoints: [u8; N], - length: u32, -} - -#[cfg(all(feature = "all", target_os = "linux"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "all", target_os = "linux"))))] -impl std::ops::Deref for CcidEndpoints { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.endpoints[0..self.length as usize] - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl AsFd for crate::Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - // SAFETY: lifetime is bound by self. - unsafe { BorrowedFd::borrow_raw(self.as_raw()) } - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl AsRawFd for crate::Socket { - fn as_raw_fd(&self) -> c_int { - self.as_raw() - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl From for OwnedFd { - fn from(sock: crate::Socket) -> OwnedFd { - // SAFETY: sock.into_raw() always returns a valid fd. - unsafe { OwnedFd::from_raw_fd(sock.into_raw()) } - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl IntoRawFd for crate::Socket { - fn into_raw_fd(self) -> c_int { - self.into_raw() - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl From for crate::Socket { - fn from(fd: OwnedFd) -> crate::Socket { - // SAFETY: `OwnedFd` ensures the fd is valid. - unsafe { crate::Socket::from_raw_fd(fd.into_raw_fd()) } - } -} - -#[cfg_attr(docsrs, doc(cfg(unix)))] -impl FromRawFd for crate::Socket { - unsafe fn from_raw_fd(fd: c_int) -> crate::Socket { - crate::Socket::from_raw(fd) - } -} - -#[cfg(feature = "all")] -from!(UnixStream, crate::Socket); -#[cfg(feature = "all")] -from!(UnixListener, crate::Socket); -#[cfg(feature = "all")] -from!(UnixDatagram, crate::Socket); -#[cfg(feature = "all")] -from!(crate::Socket, UnixStream); -#[cfg(feature = "all")] -from!(crate::Socket, UnixListener); -#[cfg(feature = "all")] -from!(crate::Socket, UnixDatagram); - -#[test] -fn in_addr_convertion() { - let ip = Ipv4Addr::new(127, 0, 0, 1); - let raw = to_in_addr(&ip); - // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow. - let a = raw.s_addr; - assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1])); - assert_eq!(from_in_addr(raw), ip); - - let ip = Ipv4Addr::new(127, 34, 4, 12); - let raw = to_in_addr(&ip); - let a = raw.s_addr; - assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12])); - assert_eq!(from_in_addr(raw), ip); -} - -#[test] -fn in6_addr_convertion() { - let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7); - let raw = to_in6_addr(&ip); - let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7]; - assert_eq!(raw.s6_addr, want); - assert_eq!(from_in6_addr(raw), ip); -} diff --git a/vendor/socket2/src/sys/windows.rs b/vendor/socket2/src/sys/windows.rs deleted file mode 100644 index 8cf27b53..00000000 --- a/vendor/socket2/src/sys/windows.rs +++ /dev/null @@ -1,1065 +0,0 @@ -// Copyright 2015 The Rust Project Developers. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cmp::min; -use std::io::{self, IoSlice}; -use std::marker::PhantomData; -use std::mem::{self, size_of, MaybeUninit}; -use std::net::{self, Ipv4Addr, Ipv6Addr, Shutdown}; -use std::os::windows::io::{ - AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, -}; -use std::path::Path; -use std::sync::Once; -use std::time::{Duration, Instant}; -use std::{process, ptr, slice}; - -use windows_sys::Win32::Foundation::{SetHandleInformation, HANDLE, HANDLE_FLAG_INHERIT}; -use windows_sys::Win32::Networking::WinSock::{ - self, tcp_keepalive, FIONBIO, IN6_ADDR, IN6_ADDR_0, INVALID_SOCKET, IN_ADDR, IN_ADDR_0, - POLLERR, POLLHUP, POLLRDNORM, POLLWRNORM, SD_BOTH, SD_RECEIVE, SD_SEND, SIO_KEEPALIVE_VALS, - SOCKET_ERROR, WSABUF, WSAEMSGSIZE, WSAESHUTDOWN, WSAPOLLFD, WSAPROTOCOL_INFOW, - WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, -}; -#[cfg(feature = "all")] -use windows_sys::Win32::Networking::WinSock::{ - IP6T_SO_ORIGINAL_DST, SOL_IP, SO_ORIGINAL_DST, SO_PROTOCOL_INFOW, -}; -use windows_sys::Win32::System::Threading::INFINITE; - -use crate::{MsgHdr, RecvFlags, SockAddr, TcpKeepalive, Type}; - -#[allow(non_camel_case_types)] -pub(crate) type c_int = std::os::raw::c_int; - -/// Fake MSG_TRUNC flag for the [`RecvFlags`] struct. -/// -/// The flag is enabled when a `WSARecv[From]` call returns `WSAEMSGSIZE`. The -/// value of the flag is defined by us. -pub(crate) const MSG_TRUNC: c_int = 0x01; - -// Used in `Domain`. -pub(crate) const AF_INET: c_int = windows_sys::Win32::Networking::WinSock::AF_INET as c_int; -pub(crate) const AF_INET6: c_int = windows_sys::Win32::Networking::WinSock::AF_INET6 as c_int; -pub(crate) const AF_UNIX: c_int = windows_sys::Win32::Networking::WinSock::AF_UNIX as c_int; -pub(crate) const AF_UNSPEC: c_int = windows_sys::Win32::Networking::WinSock::AF_UNSPEC as c_int; -// Used in `Type`. -pub(crate) const SOCK_STREAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_STREAM as c_int; -pub(crate) const SOCK_DGRAM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_DGRAM as c_int; -pub(crate) const SOCK_RAW: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RAW as c_int; -const SOCK_RDM: c_int = windows_sys::Win32::Networking::WinSock::SOCK_RDM as c_int; -pub(crate) const SOCK_SEQPACKET: c_int = - windows_sys::Win32::Networking::WinSock::SOCK_SEQPACKET as c_int; -// Used in `Protocol`. -pub(crate) use windows_sys::Win32::Networking::WinSock::{ - IPPROTO_ICMP, IPPROTO_ICMPV6, IPPROTO_TCP, IPPROTO_UDP, -}; -// Used in `SockAddr`. -pub(crate) use windows_sys::Win32::Networking::WinSock::{ - SOCKADDR as sockaddr, SOCKADDR_IN as sockaddr_in, SOCKADDR_IN6 as sockaddr_in6, - SOCKADDR_STORAGE as sockaddr_storage, -}; -#[allow(non_camel_case_types)] -pub(crate) type sa_family_t = windows_sys::Win32::Networking::WinSock::ADDRESS_FAMILY; -#[allow(non_camel_case_types)] -pub(crate) type socklen_t = windows_sys::Win32::Networking::WinSock::socklen_t; -// Used in `Socket`. -#[cfg(feature = "all")] -pub(crate) use windows_sys::Win32::Networking::WinSock::IP_HDRINCL; -pub(crate) use windows_sys::Win32::Networking::WinSock::{ - IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MREQ as Ipv6Mreq, - IPV6_MULTICAST_HOPS, IPV6_MULTICAST_IF, IPV6_MULTICAST_LOOP, IPV6_RECVTCLASS, - IPV6_UNICAST_HOPS, IPV6_V6ONLY, IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, - IP_DROP_MEMBERSHIP, IP_DROP_SOURCE_MEMBERSHIP, IP_MREQ as IpMreq, - IP_MREQ_SOURCE as IpMreqSource, IP_MULTICAST_IF, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, - IP_RECVTOS, IP_TOS, IP_TTL, LINGER as linger, MSG_OOB, MSG_PEEK, SO_BROADCAST, SO_ERROR, - SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVTIMEO, SO_REUSEADDR, SO_SNDBUF, - SO_SNDTIMEO, SO_TYPE, TCP_NODELAY, -}; -pub(crate) const IPPROTO_IP: c_int = windows_sys::Win32::Networking::WinSock::IPPROTO_IP as c_int; -pub(crate) const SOL_SOCKET: c_int = windows_sys::Win32::Networking::WinSock::SOL_SOCKET as c_int; - -/// Type used in set/getsockopt to retrieve the `TCP_NODELAY` option. -/// -/// NOTE: -/// documents that options such as `TCP_NODELAY` and `SO_KEEPALIVE` expect a -/// `BOOL` (alias for `c_int`, 4 bytes), however in practice this turns out to -/// be false (or misleading) as a `BOOLEAN` (`c_uchar`, 1 byte) is returned by -/// `getsockopt`. -pub(crate) type Bool = windows_sys::Win32::Foundation::BOOLEAN; - -/// Maximum size of a buffer passed to system call like `recv` and `send`. -const MAX_BUF_LEN: usize = c_int::MAX as usize; - -/// Helper macro to execute a system call that returns an `io::Result`. -macro_rules! syscall { - ($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{ - #[allow(unused_unsafe)] - let res = unsafe { windows_sys::Win32::Networking::WinSock::$fn($($arg, )*) }; - if $err_test(&res, &$err_value) { - Err(io::Error::last_os_error()) - } else { - Ok(res) - } - }}; -} - -impl_debug!( - crate::Domain, - self::AF_INET, - self::AF_INET6, - self::AF_UNIX, - self::AF_UNSPEC, -); - -/// Windows only API. -impl Type { - /// Our custom flag to set `WSA_FLAG_NO_HANDLE_INHERIT` on socket creation. - /// Trying to mimic `Type::cloexec` on windows. - const NO_INHERIT: c_int = 1 << ((size_of::() * 8) - 1); // Last bit. - - /// Set `WSA_FLAG_NO_HANDLE_INHERIT` on the socket. - #[cfg(feature = "all")] - #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] - pub const fn no_inherit(self) -> Type { - self._no_inherit() - } - - pub(crate) const fn _no_inherit(self) -> Type { - Type(self.0 | Type::NO_INHERIT) - } -} - -impl_debug!( - crate::Type, - self::SOCK_STREAM, - self::SOCK_DGRAM, - self::SOCK_RAW, - self::SOCK_RDM, - self::SOCK_SEQPACKET, -); - -impl_debug!( - crate::Protocol, - WinSock::IPPROTO_ICMP, - WinSock::IPPROTO_ICMPV6, - WinSock::IPPROTO_TCP, - WinSock::IPPROTO_UDP, -); - -impl std::fmt::Debug for RecvFlags { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RecvFlags") - .field("is_truncated", &self.is_truncated()) - .finish() - } -} - -#[repr(transparent)] -pub struct MaybeUninitSlice<'a> { - vec: WSABUF, - _lifetime: PhantomData<&'a mut [MaybeUninit]>, -} - -unsafe impl<'a> Send for MaybeUninitSlice<'a> {} - -unsafe impl<'a> Sync for MaybeUninitSlice<'a> {} - -impl<'a> MaybeUninitSlice<'a> { - pub fn new(buf: &'a mut [MaybeUninit]) -> MaybeUninitSlice<'a> { - assert!(buf.len() <= u32::MAX as usize); - MaybeUninitSlice { - vec: WSABUF { - len: buf.len() as u32, - buf: buf.as_mut_ptr().cast(), - }, - _lifetime: PhantomData, - } - } - - pub fn as_slice(&self) -> &[MaybeUninit] { - unsafe { slice::from_raw_parts(self.vec.buf.cast(), self.vec.len as usize) } - } - - pub fn as_mut_slice(&mut self) -> &mut [MaybeUninit] { - unsafe { slice::from_raw_parts_mut(self.vec.buf.cast(), self.vec.len as usize) } - } -} - -// Used in `MsgHdr`. -pub(crate) use windows_sys::Win32::Networking::WinSock::WSAMSG as msghdr; - -pub(crate) fn set_msghdr_name(msg: &mut msghdr, name: &SockAddr) { - msg.name = name.as_ptr() as *mut _; - msg.namelen = name.len(); -} - -pub(crate) fn set_msghdr_iov(msg: &mut msghdr, ptr: *mut WSABUF, len: usize) { - msg.lpBuffers = ptr; - msg.dwBufferCount = min(len, u32::MAX as usize) as u32; -} - -pub(crate) fn set_msghdr_control(msg: &mut msghdr, ptr: *mut u8, len: usize) { - msg.Control.buf = ptr; - msg.Control.len = len as u32; -} - -pub(crate) fn set_msghdr_flags(msg: &mut msghdr, flags: c_int) { - msg.dwFlags = flags as u32; -} - -pub(crate) fn msghdr_flags(msg: &msghdr) -> RecvFlags { - RecvFlags(msg.dwFlags as c_int) -} - -pub(crate) fn msghdr_control_len(msg: &msghdr) -> usize { - msg.Control.len as _ -} - -fn init() { - static INIT: Once = Once::new(); - - INIT.call_once(|| { - // Initialize winsock through the standard library by just creating a - // dummy socket. Whether this is successful or not we drop the result as - // libstd will be sure to have initialized winsock. - let _ = net::UdpSocket::bind("127.0.0.1:34254"); - }); -} - -pub(crate) type Socket = windows_sys::Win32::Networking::WinSock::SOCKET; - -pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner { - crate::socket::Inner::from_raw_socket(socket as RawSocket) -} - -pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket { - socket.as_raw_socket() as Socket -} - -pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket { - socket.into_raw_socket() as Socket -} - -pub(crate) fn socket(family: c_int, mut ty: c_int, protocol: c_int) -> io::Result { - init(); - - // Check if we set our custom flag. - let flags = if ty & Type::NO_INHERIT != 0 { - ty = ty & !Type::NO_INHERIT; - WSA_FLAG_NO_HANDLE_INHERIT - } else { - 0 - }; - - syscall!( - WSASocketW( - family, - ty, - protocol, - ptr::null_mut(), - 0, - WSA_FLAG_OVERLAPPED | flags, - ), - PartialEq::eq, - INVALID_SOCKET - ) -} - -pub(crate) fn bind(socket: Socket, addr: &SockAddr) -> io::Result<()> { - syscall!(bind(socket, addr.as_ptr(), addr.len()), PartialEq::ne, 0).map(|_| ()) -} - -pub(crate) fn connect(socket: Socket, addr: &SockAddr) -> io::Result<()> { - syscall!(connect(socket, addr.as_ptr(), addr.len()), PartialEq::ne, 0).map(|_| ()) -} - -pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> { - let start = Instant::now(); - - let mut fd_array = WSAPOLLFD { - fd: socket.as_raw(), - events: (POLLRDNORM | POLLWRNORM) as i16, - revents: 0, - }; - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::ErrorKind::TimedOut.into()); - } - - let timeout = (timeout - elapsed).as_millis(); - let timeout = clamp(timeout, 1, c_int::MAX as u128) as c_int; - - match syscall!( - WSAPoll(&mut fd_array, 1, timeout), - PartialEq::eq, - SOCKET_ERROR - ) { - Ok(0) => return Err(io::ErrorKind::TimedOut.into()), - Ok(_) => { - // Error or hang up indicates an error (or failure to connect). - if (fd_array.revents & POLLERR as i16) != 0 - || (fd_array.revents & POLLHUP as i16) != 0 - { - match socket.take_error() { - Ok(Some(err)) => return Err(err), - Ok(None) => { - return Err(io::Error::new( - io::ErrorKind::Other, - "no error set after POLLHUP", - )) - } - Err(err) => return Err(err), - } - } - return Ok(()); - } - // Got interrupted, try again. - Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, - Err(err) => return Err(err), - } - } -} - -// TODO: use clamp from std lib, stable since 1.50. -fn clamp(value: T, min: T, max: T) -> T -where - T: Ord, -{ - if value <= min { - min - } else if value >= max { - max - } else { - value - } -} - -pub(crate) fn listen(socket: Socket, backlog: c_int) -> io::Result<()> { - syscall!(listen(socket, backlog), PartialEq::ne, 0).map(|_| ()) -} - -pub(crate) fn accept(socket: Socket) -> io::Result<(Socket, SockAddr)> { - // Safety: `accept` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - accept(socket, storage.cast(), len), - PartialEq::eq, - INVALID_SOCKET - ) - }) - } -} - -pub(crate) fn getsockname(socket: Socket) -> io::Result { - // Safety: `getsockname` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getsockname(socket, storage.cast(), len), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) -} - -pub(crate) fn getpeername(socket: Socket) -> io::Result { - // Safety: `getpeername` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getpeername(socket, storage.cast(), len), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) -} - -pub(crate) fn try_clone(socket: Socket) -> io::Result { - let mut info: MaybeUninit = MaybeUninit::uninit(); - syscall!( - // NOTE: `process.id` is the same as `GetCurrentProcessId`. - WSADuplicateSocketW(socket, process::id(), info.as_mut_ptr()), - PartialEq::eq, - SOCKET_ERROR - )?; - // Safety: `WSADuplicateSocketW` intialised `info` for us. - let mut info = unsafe { info.assume_init() }; - - syscall!( - WSASocketW( - info.iAddressFamily, - info.iSocketType, - info.iProtocol, - &mut info, - 0, - WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT, - ), - PartialEq::eq, - INVALID_SOCKET - ) -} - -pub(crate) fn set_nonblocking(socket: Socket, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = if nonblocking { 1 } else { 0 }; - ioctlsocket(socket, FIONBIO, &mut nonblocking) -} - -pub(crate) fn shutdown(socket: Socket, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => SD_SEND, - Shutdown::Read => SD_RECEIVE, - Shutdown::Both => SD_BOTH, - } as i32; - syscall!(shutdown(socket, how), PartialEq::eq, SOCKET_ERROR).map(|_| ()) -} - -pub(crate) fn recv(socket: Socket, buf: &mut [MaybeUninit], flags: c_int) -> io::Result { - let res = syscall!( - recv( - socket, - buf.as_mut_ptr().cast(), - min(buf.len(), MAX_BUF_LEN) as c_int, - flags, - ), - PartialEq::eq, - SOCKET_ERROR - ); - match res { - Ok(n) => Ok(n as usize), - Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0), - Err(err) => Err(err), - } -} - -pub(crate) fn recv_vectored( - socket: Socket, - bufs: &mut [crate::MaybeUninitSlice<'_>], - flags: c_int, -) -> io::Result<(usize, RecvFlags)> { - let mut nread = 0; - let mut flags = flags as u32; - let res = syscall!( - WSARecv( - socket, - bufs.as_mut_ptr().cast(), - min(bufs.len(), u32::MAX as usize) as u32, - &mut nread, - &mut flags, - ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ); - match res { - Ok(_) => Ok((nread as usize, RecvFlags(0))), - Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok((0, RecvFlags(0))), - Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => { - Ok((nread as usize, RecvFlags(MSG_TRUNC))) - } - Err(err) => Err(err), - } -} - -pub(crate) fn recv_from( - socket: Socket, - buf: &mut [MaybeUninit], - flags: c_int, -) -> io::Result<(usize, SockAddr)> { - // Safety: `recvfrom` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, addrlen| { - let res = syscall!( - recvfrom( - socket, - buf.as_mut_ptr().cast(), - min(buf.len(), MAX_BUF_LEN) as c_int, - flags, - storage.cast(), - addrlen, - ), - PartialEq::eq, - SOCKET_ERROR - ); - match res { - Ok(n) => Ok(n as usize), - Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => Ok(0), - Err(err) => Err(err), - } - }) - } -} - -pub(crate) fn peek_sender(socket: Socket) -> io::Result { - // Safety: `recvfrom` initialises the `SockAddr` for us. - let ((), sender) = unsafe { - SockAddr::try_init(|storage, addrlen| { - let res = syscall!( - recvfrom( - socket, - // Windows *appears* not to care if you pass a null pointer. - ptr::null_mut(), - 0, - MSG_PEEK, - storage.cast(), - addrlen, - ), - PartialEq::eq, - SOCKET_ERROR - ); - match res { - Ok(_n) => Ok(()), - Err(e) => match e.raw_os_error() { - Some(code) if code == (WSAESHUTDOWN as i32) || code == (WSAEMSGSIZE as i32) => { - Ok(()) - } - _ => Err(e), - }, - } - }) - }?; - - Ok(sender) -} - -pub(crate) fn recv_from_vectored( - socket: Socket, - bufs: &mut [crate::MaybeUninitSlice<'_>], - flags: c_int, -) -> io::Result<(usize, RecvFlags, SockAddr)> { - // Safety: `recvfrom` initialises the `SockAddr` for us. - unsafe { - SockAddr::try_init(|storage, addrlen| { - let mut nread = 0; - let mut flags = flags as u32; - let res = syscall!( - WSARecvFrom( - socket, - bufs.as_mut_ptr().cast(), - min(bufs.len(), u32::MAX as usize) as u32, - &mut nread, - &mut flags, - storage.cast(), - addrlen, - ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ); - match res { - Ok(_) => Ok((nread as usize, RecvFlags(0))), - Err(ref err) if err.raw_os_error() == Some(WSAESHUTDOWN as i32) => { - Ok((nread as usize, RecvFlags(0))) - } - Err(ref err) if err.raw_os_error() == Some(WSAEMSGSIZE as i32) => { - Ok((nread as usize, RecvFlags(MSG_TRUNC))) - } - Err(err) => Err(err), - } - }) - } - .map(|((n, recv_flags), addr)| (n, recv_flags, addr)) -} - -pub(crate) fn send(socket: Socket, buf: &[u8], flags: c_int) -> io::Result { - syscall!( - send( - socket, - buf.as_ptr().cast(), - min(buf.len(), MAX_BUF_LEN) as c_int, - flags, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|n| n as usize) -} - -pub(crate) fn send_vectored( - socket: Socket, - bufs: &[IoSlice<'_>], - flags: c_int, -) -> io::Result { - let mut nsent = 0; - syscall!( - WSASend( - socket, - // FIXME: From the `WSASend` docs [1]: - // > For a Winsock application, once the WSASend function is called, - // > the system owns these buffers and the application may not - // > access them. - // - // So what we're doing is actually UB as `bufs` needs to be `&mut - // [IoSlice<'_>]`. - // - // Tracking issue: https://github.com/rust-lang/socket2-rs/issues/129. - // - // NOTE: `send_to_vectored` has the same problem. - // - // [1] https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend - bufs.as_ptr() as *mut _, - min(bufs.len(), u32::MAX as usize) as u32, - &mut nsent, - flags as u32, - std::ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| nsent as usize) -} - -pub(crate) fn send_to( - socket: Socket, - buf: &[u8], - addr: &SockAddr, - flags: c_int, -) -> io::Result { - syscall!( - sendto( - socket, - buf.as_ptr().cast(), - min(buf.len(), MAX_BUF_LEN) as c_int, - flags, - addr.as_ptr(), - addr.len(), - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|n| n as usize) -} - -pub(crate) fn send_to_vectored( - socket: Socket, - bufs: &[IoSlice<'_>], - addr: &SockAddr, - flags: c_int, -) -> io::Result { - let mut nsent = 0; - syscall!( - WSASendTo( - socket, - // FIXME: Same problem as in `send_vectored`. - bufs.as_ptr() as *mut _, - bufs.len().min(u32::MAX as usize) as u32, - &mut nsent, - flags as u32, - addr.as_ptr(), - addr.len(), - ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| nsent as usize) -} - -pub(crate) fn sendmsg(socket: Socket, msg: &MsgHdr<'_, '_, '_>, flags: c_int) -> io::Result { - let mut nsent = 0; - syscall!( - WSASendMsg( - socket, - &msg.inner, - flags as u32, - &mut nsent, - ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| nsent as usize) -} - -/// Wrapper around `getsockopt` to deal with platform specific timeouts. -pub(crate) fn timeout_opt(fd: Socket, lvl: c_int, name: i32) -> io::Result> { - unsafe { getsockopt(fd, lvl, name).map(from_ms) } -} - -fn from_ms(duration: u32) -> Option { - if duration == 0 { - None - } else { - let secs = duration / 1000; - let nsec = (duration % 1000) * 1000000; - Some(Duration::new(secs as u64, nsec as u32)) - } -} - -/// Wrapper around `setsockopt` to deal with platform specific timeouts. -pub(crate) fn set_timeout_opt( - socket: Socket, - level: c_int, - optname: i32, - duration: Option, -) -> io::Result<()> { - let duration = into_ms(duration); - unsafe { setsockopt(socket, level, optname, duration) } -} - -fn into_ms(duration: Option) -> u32 { - // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the - // timeouts in windows APIs are typically u32 milliseconds. To translate, we - // have two pieces to take care of: - // - // * Nanosecond precision is rounded up - // * Greater than u32::MAX milliseconds (50 days) is rounded up to - // INFINITE (never time out). - duration.map_or(0, |duration| { - min(duration.as_millis(), INFINITE as u128) as u32 - }) -} - -pub(crate) fn set_tcp_keepalive(socket: Socket, keepalive: &TcpKeepalive) -> io::Result<()> { - let mut keepalive = tcp_keepalive { - onoff: 1, - keepalivetime: into_ms(keepalive.time), - keepaliveinterval: into_ms(keepalive.interval), - }; - let mut out = 0; - syscall!( - WSAIoctl( - socket, - SIO_KEEPALIVE_VALS, - &mut keepalive as *mut _ as *mut _, - size_of::() as _, - ptr::null_mut(), - 0, - &mut out, - ptr::null_mut(), - None, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| ()) -} - -/// Caller must ensure `T` is the correct type for `level` and `optname`. -// NOTE: `optname` is actually `i32`, but all constants are `u32`. -pub(crate) unsafe fn getsockopt(socket: Socket, level: c_int, optname: i32) -> io::Result { - let mut optval: MaybeUninit = MaybeUninit::uninit(); - let mut optlen = mem::size_of::() as c_int; - syscall!( - getsockopt( - socket, - level as i32, - optname, - optval.as_mut_ptr().cast(), - &mut optlen, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| { - debug_assert_eq!(optlen as usize, mem::size_of::()); - // Safety: `getsockopt` initialised `optval` for us. - optval.assume_init() - }) -} - -/// Caller must ensure `T` is the correct type for `level` and `optname`. -// NOTE: `optname` is actually `i32`, but all constants are `u32`. -pub(crate) unsafe fn setsockopt( - socket: Socket, - level: c_int, - optname: i32, - optval: T, -) -> io::Result<()> { - syscall!( - setsockopt( - socket, - level as i32, - optname, - (&optval as *const T).cast(), - mem::size_of::() as c_int, - ), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| ()) -} - -fn ioctlsocket(socket: Socket, cmd: i32, payload: &mut u32) -> io::Result<()> { - syscall!( - ioctlsocket(socket, cmd, payload), - PartialEq::eq, - SOCKET_ERROR - ) - .map(|_| ()) -} - -pub(crate) fn to_in_addr(addr: &Ipv4Addr) -> IN_ADDR { - IN_ADDR { - S_un: IN_ADDR_0 { - // `S_un` is stored as BE on all machines, and the array is in BE - // order. So the native endian conversion method is used so that - // it's never swapped. - S_addr: u32::from_ne_bytes(addr.octets()), - }, - } -} - -pub(crate) fn from_in_addr(in_addr: IN_ADDR) -> Ipv4Addr { - Ipv4Addr::from(unsafe { in_addr.S_un.S_addr }.to_ne_bytes()) -} - -pub(crate) fn to_in6_addr(addr: &Ipv6Addr) -> IN6_ADDR { - IN6_ADDR { - u: IN6_ADDR_0 { - Byte: addr.octets(), - }, - } -} - -pub(crate) fn from_in6_addr(addr: IN6_ADDR) -> Ipv6Addr { - Ipv6Addr::from(unsafe { addr.u.Byte }) -} - -pub(crate) fn to_mreqn( - multiaddr: &Ipv4Addr, - interface: &crate::socket::InterfaceIndexOrAddress, -) -> IpMreq { - IpMreq { - imr_multiaddr: to_in_addr(multiaddr), - // Per https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq#members: - // - // imr_interface - // - // The local IPv4 address of the interface or the interface index on - // which the multicast group should be joined or dropped. This value is - // in network byte order. If this member specifies an IPv4 address of - // 0.0.0.0, the default IPv4 multicast interface is used. - // - // To use an interface index of 1 would be the same as an IP address of - // 0.0.0.1. - imr_interface: match interface { - crate::socket::InterfaceIndexOrAddress::Index(interface) => { - to_in_addr(&(*interface).into()) - } - crate::socket::InterfaceIndexOrAddress::Address(interface) => to_in_addr(interface), - }, - } -} - -#[cfg(feature = "all")] -pub(crate) fn original_dst(socket: Socket) -> io::Result { - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getsockopt( - socket, - SOL_IP as i32, - SO_ORIGINAL_DST as i32, - storage.cast(), - len, - ), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) -} - -#[cfg(feature = "all")] -pub(crate) fn original_dst_ipv6(socket: Socket) -> io::Result { - unsafe { - SockAddr::try_init(|storage, len| { - syscall!( - getsockopt( - socket, - SOL_IP as i32, - IP6T_SO_ORIGINAL_DST as i32, - storage.cast(), - len, - ), - PartialEq::eq, - SOCKET_ERROR - ) - }) - } - .map(|(_, addr)| addr) -} - -#[allow(unsafe_op_in_unsafe_fn)] -pub(crate) fn unix_sockaddr(path: &Path) -> io::Result { - // SAFETY: a `sockaddr_storage` of all zeros is valid. - let mut storage = unsafe { mem::zeroed::() }; - let len = { - let storage: &mut windows_sys::Win32::Networking::WinSock::SOCKADDR_UN = - unsafe { &mut *(&mut storage as *mut sockaddr_storage).cast() }; - - // Windows expects a UTF-8 path here even though Windows paths are - // usually UCS-2 encoded. If Rust exposed OsStr's Wtf8 encoded - // buffer, this could be used directly, relying on Windows to - // validate the path, but Rust hides this implementation detail. - // - // See . - let bytes = path - .to_str() - .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? - .as_bytes(); - - // Windows appears to allow non-null-terminated paths, but this is - // not documented, so do not rely on it yet. - // - // See . - if bytes.len() >= storage.sun_path.len() { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "path must be shorter than SUN_LEN", - )); - } - - storage.sun_family = crate::sys::AF_UNIX as sa_family_t; - // `storage` was initialized to zero above, so the path is - // already null terminated. - storage.sun_path[..bytes.len()].copy_from_slice(bytes); - - let base = storage as *const _ as usize; - let path = &storage.sun_path as *const _ as usize; - let sun_path_offset = path - base; - sun_path_offset + bytes.len() + 1 - }; - Ok(unsafe { SockAddr::new(storage, len as socklen_t) }) -} - -/// Windows only API. -impl crate::Socket { - /// Sets `HANDLE_FLAG_INHERIT` using `SetHandleInformation`. - #[cfg(feature = "all")] - #[cfg_attr(docsrs, doc(cfg(all(windows, feature = "all"))))] - pub fn set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { - self._set_no_inherit(no_inherit) - } - - pub(crate) fn _set_no_inherit(&self, no_inherit: bool) -> io::Result<()> { - // NOTE: can't use `syscall!` because it expects the function in the - // `windows_sys::Win32::Networking::WinSock::` path. - let res = unsafe { - SetHandleInformation( - self.as_raw() as HANDLE, - HANDLE_FLAG_INHERIT, - !no_inherit as _, - ) - }; - if res == 0 { - // Zero means error. - Err(io::Error::last_os_error()) - } else { - Ok(()) - } - } - - /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL_INFOW` - /// option on this socket. - /// - /// [`Protocol`]: crate::Protocol - #[cfg(feature = "all")] - pub fn protocol(&self) -> io::Result> { - let info = unsafe { - getsockopt::(self.as_raw(), SOL_SOCKET, SO_PROTOCOL_INFOW)? - }; - match info.iProtocol { - 0 => Ok(None), - p => Ok(Some(crate::Protocol::from(p))), - } - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl AsSocket for crate::Socket { - fn as_socket(&self) -> BorrowedSocket<'_> { - // SAFETY: lifetime is bound by self. - unsafe { BorrowedSocket::borrow_raw(self.as_raw() as RawSocket) } - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl AsRawSocket for crate::Socket { - fn as_raw_socket(&self) -> RawSocket { - self.as_raw() as RawSocket - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl From for OwnedSocket { - fn from(sock: crate::Socket) -> OwnedSocket { - // SAFETY: sock.into_raw() always returns a valid fd. - unsafe { OwnedSocket::from_raw_socket(sock.into_raw() as RawSocket) } - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl IntoRawSocket for crate::Socket { - fn into_raw_socket(self) -> RawSocket { - self.into_raw() as RawSocket - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl From for crate::Socket { - fn from(fd: OwnedSocket) -> crate::Socket { - // SAFETY: `OwnedFd` ensures the fd is valid. - unsafe { crate::Socket::from_raw_socket(fd.into_raw_socket()) } - } -} - -#[cfg_attr(docsrs, doc(cfg(windows)))] -impl FromRawSocket for crate::Socket { - unsafe fn from_raw_socket(socket: RawSocket) -> crate::Socket { - crate::Socket::from_raw(socket as Socket) - } -} - -#[test] -fn in_addr_convertion() { - let ip = Ipv4Addr::new(127, 0, 0, 1); - let raw = to_in_addr(&ip); - assert_eq!(unsafe { raw.S_un.S_addr }, 127 << 0 | 1 << 24); - assert_eq!(from_in_addr(raw), ip); - - let ip = Ipv4Addr::new(127, 34, 4, 12); - let raw = to_in_addr(&ip); - assert_eq!( - unsafe { raw.S_un.S_addr }, - 127 << 0 | 34 << 8 | 4 << 16 | 12 << 24 - ); - assert_eq!(from_in_addr(raw), ip); -} - -#[test] -fn in6_addr_convertion() { - let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7); - let raw = to_in6_addr(&ip); - let want = [ - 0x2000u16.to_be(), - 1u16.to_be(), - 2u16.to_be(), - 3u16.to_be(), - 4u16.to_be(), - 5u16.to_be(), - 6u16.to_be(), - 7u16.to_be(), - ]; - assert_eq!(unsafe { raw.u.Word }, want); - assert_eq!(from_in6_addr(raw), ip); -} -- cgit v1.2.3