diff options
Diffstat (limited to 'vendor/rustix/src/backend/libc/net')
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/addr.rs | 390 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/ext.rs | 137 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/mod.rs | 17 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/msghdr.rs | 188 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/netdevice.rs | 55 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/read_sockaddr.rs | 264 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/send_recv.rs | 153 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/sockopt.rs | 1339 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/syscalls.rs | 438 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/write_sockaddr.rs | 72 |
10 files changed, 3053 insertions, 0 deletions
diff --git a/vendor/rustix/src/backend/libc/net/addr.rs b/vendor/rustix/src/backend/libc/net/addr.rs new file mode 100644 index 00000000..1699ffa9 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/addr.rs @@ -0,0 +1,390 @@ +//! Socket address utilities. + +use crate::backend::c; +use crate::net::AddressFamily; +#[cfg(unix)] +use { + crate::ffi::CStr, + crate::io, + crate::net::addr::SocketAddrLen, + crate::path, + core::cmp::Ordering, + core::fmt, + core::hash::{Hash, Hasher}, + core::slice, +}; +#[cfg(all(unix, feature = "alloc"))] +use {crate::ffi::CString, alloc::borrow::Cow, alloc::vec::Vec}; + +/// `struct sockaddr_un` +#[cfg(unix)] +#[derive(Clone)] +#[doc(alias = "sockaddr_un")] +pub struct SocketAddrUnix { + pub(crate) unix: c::sockaddr_un, + #[cfg(not(any(bsd, target_os = "haiku")))] + len: c::socklen_t, +} + +#[cfg(unix)] +impl SocketAddrUnix { + /// Construct a new Unix-domain address from a filesystem path. + #[inline] + pub fn new<P: path::Arg>(path: P) -> io::Result<Self> { + path.into_with_c_str(Self::_new) + } + + #[inline] + fn _new(path: &CStr) -> io::Result<Self> { + let mut unix = Self::init(); + let mut bytes = path.to_bytes_with_nul(); + if bytes.len() > unix.sun_path.len() { + bytes = path.to_bytes(); // without NUL + if bytes.len() > unix.sun_path.len() { + return Err(io::Errno::NAMETOOLONG); + } + } + for (i, b) in bytes.iter().enumerate() { + unix.sun_path[i] = *b as c::c_char; + } + + #[cfg(any(bsd, target_os = "haiku"))] + { + unix.sun_len = (offsetof_sun_path() + bytes.len()).try_into().unwrap(); + } + + Ok(Self { + unix, + #[cfg(not(any(bsd, target_os = "haiku")))] + len: (offsetof_sun_path() + bytes.len()).try_into().unwrap(), + }) + } + + /// Construct a new abstract Unix-domain address from a byte slice. + #[cfg(linux_kernel)] + #[inline] + pub fn new_abstract_name(name: &[u8]) -> io::Result<Self> { + let mut unix = Self::init(); + if 1 + name.len() > unix.sun_path.len() { + return Err(io::Errno::NAMETOOLONG); + } + unix.sun_path[0] = 0; + for (i, b) in name.iter().enumerate() { + unix.sun_path[1 + i] = *b as c::c_char; + } + let len = offsetof_sun_path() + 1 + name.len(); + let len = len.try_into().unwrap(); + Ok(Self { + unix, + #[cfg(not(any(bsd, target_os = "haiku")))] + len, + }) + } + + /// Construct a new unnamed address. + /// + /// The kernel will assign an abstract Unix-domain address to the socket + /// when you call [`bind`][crate::net::bind]. You can inspect the assigned + /// name with [`getsockname`][crate::net::getsockname]. + /// + /// # References + /// - [Linux] + /// + /// [Linux]: https://www.man7.org/linux/man-pages/man7/unix.7.html + #[cfg(linux_kernel)] + #[inline] + pub fn new_unnamed() -> Self { + Self { + unix: Self::init(), + #[cfg(not(any(bsd, target_os = "haiku")))] + len: offsetof_sun_path() as c::socklen_t, + } + } + + const fn init() -> c::sockaddr_un { + c::sockaddr_un { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "hurd", + ))] + sun_len: 0, + #[cfg(target_os = "vita")] + ss_len: 0, + sun_family: c::AF_UNIX as _, + #[cfg(any(bsd, target_os = "horizon", target_os = "nto"))] + sun_path: [0; 104], + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "horizon", + target_os = "nto" + )))] + sun_path: [0; 108], + #[cfg(target_os = "haiku")] + sun_path: [0; 126], + #[cfg(target_os = "aix")] + sun_path: [0; 1023], + } + } + + /// For a filesystem path address, return the path. + #[inline] + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn path(&self) -> Option<Cow<'_, CStr>> { + let bytes = self.bytes()?; + if !bytes.is_empty() && bytes[0] != 0 { + if self.unix.sun_path.len() == bytes.len() { + // SAFETY: There are no NULs contained in bytes. + unsafe { Self::path_with_termination(bytes) } + } else { + // SAFETY: `from_bytes_with_nul_unchecked` since the string is + // NUL-terminated. + Some(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }.into()) + } + } else { + None + } + } + + /// If the `sun_path` field is not NUL-terminated, terminate it. + /// + /// SAFETY: The input `bytes` must not contain any NULs. + #[cfg(feature = "alloc")] + #[cold] + unsafe fn path_with_termination(bytes: &[u8]) -> Option<Cow<'_, CStr>> { + let mut owned = Vec::with_capacity(bytes.len() + 1); + owned.extend_from_slice(bytes); + owned.push(b'\0'); + // SAFETY: `from_vec_with_nul_unchecked` since the string is + // NUL-terminated and `bytes` does not contain any NULs. + Some(Cow::Owned( + CString::from_vec_with_nul_unchecked(owned).into(), + )) + } + + /// For a filesystem path address, return the path as a byte sequence, + /// excluding the NUL terminator. + #[inline] + pub fn path_bytes(&self) -> Option<&[u8]> { + let bytes = self.bytes()?; + if !bytes.is_empty() && bytes[0] != 0 { + if self.unix.sun_path.len() == self.len() - offsetof_sun_path() { + // There is no NUL terminator. + Some(bytes) + } else { + // Remove the NUL terminator. + Some(&bytes[..bytes.len() - 1]) + } + } else { + None + } + } + + /// For an abstract address, return the identifier. + #[cfg(linux_kernel)] + #[inline] + pub fn abstract_name(&self) -> Option<&[u8]> { + if let [0, bytes @ ..] = self.bytes()? { + Some(bytes) + } else { + None + } + } + + /// `true` if the socket address is unnamed. + #[cfg(linux_kernel)] + #[inline] + pub fn is_unnamed(&self) -> bool { + self.bytes() == Some(&[]) + } + + #[inline] + pub(crate) fn addr_len(&self) -> SocketAddrLen { + #[cfg(not(any(bsd, target_os = "haiku")))] + { + bitcast!(self.len) + } + #[cfg(any(bsd, target_os = "haiku"))] + { + bitcast!(c::socklen_t::from(self.unix.sun_len)) + } + } + + #[inline] + pub(crate) fn len(&self) -> usize { + self.addr_len() as usize + } + + #[inline] + fn bytes(&self) -> Option<&[u8]> { + let len = self.len(); + if len != 0 { + let bytes = &self.unix.sun_path[..len - offsetof_sun_path()]; + // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. + Some(unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len()) }) + } else { + None + } + } +} + +#[cfg(unix)] +impl PartialEq for SocketAddrUnix { + #[inline] + fn eq(&self, other: &Self) -> bool { + let self_len = self.len() - offsetof_sun_path(); + let other_len = other.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].eq(&other.unix.sun_path[..other_len]) + } +} + +#[cfg(unix)] +impl Eq for SocketAddrUnix {} + +#[cfg(unix)] +impl PartialOrd for SocketAddrUnix { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +#[cfg(unix)] +impl Ord for SocketAddrUnix { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + let self_len = self.len() - offsetof_sun_path(); + let other_len = other.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].cmp(&other.unix.sun_path[..other_len]) + } +} + +#[cfg(unix)] +impl Hash for SocketAddrUnix { + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + let self_len = self.len() - offsetof_sun_path(); + self.unix.sun_path[..self_len].hash(state) + } +} + +#[cfg(unix)] +impl fmt::Debug for SocketAddrUnix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(feature = "alloc")] + if let Some(path) = self.path() { + return path.fmt(f); + } + if let Some(bytes) = self.path_bytes() { + if let Ok(s) = core::str::from_utf8(bytes) { + return s.fmt(f); + } + return bytes.fmt(f); + } + #[cfg(linux_kernel)] + if let Some(name) = self.abstract_name() { + return name.fmt(f); + } + "(unnamed)".fmt(f) + } +} + +/// `struct sockaddr_storage` +/// +/// This type is guaranteed to be large enough to hold any encoded socket +/// address. +#[repr(transparent)] +#[derive(Copy, Clone)] +#[doc(alias = "sockaddr_storage")] +pub struct SocketAddrStorage(c::sockaddr_storage); + +impl SocketAddrStorage { + /// Return a socket addr storage initialized to all zero bytes. The + /// `sa_family` is set to [`AddressFamily::UNSPEC`]. + pub fn zeroed() -> Self { + assert_eq!(c::AF_UNSPEC, 0); + // SAFETY: `sockaddr_storage` is meant to be zero-initializable. + unsafe { core::mem::zeroed() } + } + + /// Return the `sa_family` of this socket address. + pub fn family(&self) -> AddressFamily { + // SAFETY: `self.0` is a `sockaddr_storage` so it has enough space. + unsafe { + AddressFamily::from_raw(crate::backend::net::read_sockaddr::read_sa_family( + crate::utils::as_ptr(&self.0).cast::<c::sockaddr>(), + )) + } + } + + /// Clear the `sa_family` of this socket address to + /// [`AddressFamily::UNSPEC`]. + pub fn clear_family(&mut self) { + // SAFETY: `self.0` is a `sockaddr_storage` so it has enough space. + unsafe { + crate::backend::net::read_sockaddr::initialize_family_to_unspec( + crate::utils::as_mut_ptr(&mut self.0).cast::<c::sockaddr>(), + ) + } + } +} + +/// Return the offset of the `sun_path` field of `sockaddr_un`. +#[cfg(not(windows))] +#[inline] +pub(crate) fn offsetof_sun_path() -> usize { + let z = c::sockaddr_un { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "horizon", + target_os = "hurd", + target_os = "nto", + ))] + sun_len: 0_u8, + #[cfg(target_os = "vita")] + ss_len: 0, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + sun_family: 0_u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + )))] + sun_family: 0_u16, + #[cfg(any(bsd, target_os = "horizon", target_os = "nto"))] + sun_path: [0; 104], + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "haiku", + target_os = "horizon", + target_os = "nto" + )))] + sun_path: [0; 108], + #[cfg(target_os = "haiku")] + sun_path: [0; 126], + #[cfg(target_os = "aix")] + sun_path: [0; 1023], + }; + (crate::utils::as_ptr(&z.sun_path) as usize) - (crate::utils::as_ptr(&z) as usize) +} diff --git a/vendor/rustix/src/backend/libc/net/ext.rs b/vendor/rustix/src/backend/libc/net/ext.rs new file mode 100644 index 00000000..efd2b31c --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/ext.rs @@ -0,0 +1,137 @@ +use crate::backend::c; + +/// The windows `sockaddr_in6` type is a union with accessor functions which +/// are not `const fn`. Define our own layout-compatible version so that we +/// can transmute in and out of it. +#[cfg(windows)] +#[repr(C)] +struct sockaddr_in6 { + sin6_family: u16, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 { + addr.s_addr +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in_addr_s_addr(addr: c::in_addr) -> u32 { + // This should be `*addr.S_un.S_addr()`, except that isn't a `const fn`. + unsafe { core::mem::transmute(addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr { + c::in_addr { s_addr } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in_addr_new(s_addr: u32) -> c::in_addr { + unsafe { core::mem::transmute(s_addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] { + addr.s6_addr +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in6_addr_s6_addr(addr: c::in6_addr) -> [u8; 16] { + unsafe { core::mem::transmute(addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr { + c::in6_addr { s6_addr } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn in6_addr_new(s6_addr: [u8; 16]) -> c::in6_addr { + unsafe { core::mem::transmute(s6_addr) } +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 { + addr.sin6_scope_id +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn sockaddr_in6_sin6_scope_id(addr: &c::sockaddr_in6) -> u32 { + let addr: &sockaddr_in6 = unsafe { core::mem::transmute(addr) }; + addr.sin6_scope_id +} + +#[cfg(not(windows))] +#[inline] +pub(crate) const fn sockaddr_in6_new( + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + sin6_len: u8, + sin6_family: c::sa_family_t, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +) -> c::sockaddr_in6 { + c::sockaddr_in6 { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + sin6_len, + sin6_family, + sin6_port, + sin6_flowinfo, + sin6_addr, + sin6_scope_id, + #[cfg(solarish)] + __sin6_src_id: 0, + #[cfg(target_os = "vita")] + sin6_vport: 0, + } +} + +#[cfg(windows)] +#[inline] +pub(crate) const fn sockaddr_in6_new( + sin6_family: u16, + sin6_port: u16, + sin6_flowinfo: u32, + sin6_addr: c::in6_addr, + sin6_scope_id: u32, +) -> c::sockaddr_in6 { + let addr = sockaddr_in6 { + sin6_family, + sin6_port, + sin6_flowinfo, + sin6_addr, + sin6_scope_id, + }; + unsafe { core::mem::transmute(addr) } +} diff --git a/vendor/rustix/src/backend/libc/net/mod.rs b/vendor/rustix/src/backend/libc/net/mod.rs new file mode 100644 index 00000000..da7e1df8 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/mod.rs @@ -0,0 +1,17 @@ +pub(crate) mod addr; +pub(crate) mod ext; +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub(crate) mod msghdr; +#[cfg(linux_kernel)] +pub(crate) mod netdevice; +pub(crate) mod read_sockaddr; +pub(crate) mod send_recv; +pub(crate) mod sockopt; +pub(crate) mod syscalls; +pub(crate) mod write_sockaddr; diff --git a/vendor/rustix/src/backend/libc/net/msghdr.rs b/vendor/rustix/src/backend/libc/net/msghdr.rs new file mode 100644 index 00000000..fe5471b9 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/msghdr.rs @@ -0,0 +1,188 @@ +//! Utilities for dealing with message headers. +//! +//! These take closures rather than returning a `c::msghdr` directly because +//! the message headers may reference stack-local data. + +use crate::backend::c; + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::addr::SocketAddrArg; +use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrBuf}; + +use core::mem::zeroed; + +/// Convert the value to the `msg_iovlen` field of a `msghdr` struct. +#[cfg(all( + not(any(windows, target_os = "espidf", target_os = "wasi")), + any( + target_os = "android", + all( + target_os = "linux", + not(target_env = "musl"), + not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips"))) + ) + ) +))] +#[inline] +fn msg_iov_len(len: usize) -> c::size_t { + len +} + +/// Convert the value to the `msg_iovlen` field of a `msghdr` struct. +#[cfg(all( + not(any( + windows, + target_os = "espidf", + target_os = "redox", + target_os = "vita", + target_os = "wasi" + )), + not(any( + target_os = "android", + all( + target_os = "linux", + not(target_env = "musl"), + not(all(target_env = "uclibc", any(target_arch = "arm", target_arch = "mips"))) + ) + )) +))] +#[inline] +fn msg_iov_len(len: usize) -> c::c_int { + len.try_into().unwrap_or(c::c_int::MAX) +} + +/// Convert the value to a `socklen_t`. +#[cfg(any( + bsd, + solarish, + target_env = "musl", + target_os = "aix", + target_os = "cygwin", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", +))] +#[inline] +fn msg_control_len(len: usize) -> c::socklen_t { + len.try_into().unwrap_or(c::socklen_t::MAX) +} + +/// Convert the value to a `size_t`. +#[cfg(not(any( + bsd, + solarish, + windows, + target_env = "musl", + target_os = "aix", + target_os = "cygwin", + target_os = "emscripten", + target_os = "espidf", + target_os = "fuchsia", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + target_os = "wasi", +)))] +#[inline] +fn msg_control_len(len: usize) -> c::size_t { + len +} + +/// Create a message header intended to receive a datagram. +/// +/// # Safety +/// +/// If `f` dereferences the pointers in the `msghdr`, it must do so only within +/// the bounds indicated by the associated lengths in the `msghdr`. +/// +/// And, if `f` returns `Ok`, it must have updated the `msg_controllen` field +/// of the `msghdr` to indicate how many bytes it initialized. +pub(crate) unsafe fn with_recv_msghdr<R>( + name: &mut SocketAddrBuf, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + f: impl FnOnce(&mut c::msghdr) -> io::Result<R>, +) -> io::Result<R> { + control.clear(); + + let mut msghdr = zero_msghdr(); + msghdr.msg_name = name.storage.as_mut_ptr().cast(); + msghdr.msg_namelen = name.len; + msghdr.msg_iov = iov.as_mut_ptr().cast(); + msghdr.msg_iovlen = msg_iov_len(iov.len()); + msghdr.msg_control = control.as_control_ptr().cast(); + msghdr.msg_controllen = msg_control_len(control.control_len()); + + let res = f(&mut msghdr); + + // Reset the control length. + if res.is_ok() { + // SAFETY: `f` returned `Ok`, so our safety condition requires `f` to + // have initialized `msg_controllen` bytes. + control.set_control_len(msghdr.msg_controllen as usize); + } + + name.len = msghdr.msg_namelen; + + res +} + +/// Create a message header intended to send without an address. +/// +/// The returned `msghdr` will contain raw pointers to the memory +/// referenced by `iov` and `control`. +pub(crate) fn noaddr_msghdr( + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, +) -> c::msghdr { + let mut h = zero_msghdr(); + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + h +} + +/// Create a message header intended to send with the specified address. +/// +/// This creates a `c::msghdr` and calls a function `f` on it. The `msghdr`'s +/// raw pointers may point to temporaries, so this function should avoid +/// storing the pointers anywhere that would outlive the function call. +/// +/// # Safety +/// +/// If `f` dereferences the pointers in the `msghdr`, it must do so only within +/// the bounds indicated by the associated lengths in the `msghdr`. +pub(crate) unsafe fn with_msghdr<R>( + addr: &impl SocketAddrArg, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + f: impl FnOnce(&c::msghdr) -> R, +) -> R { + addr.with_sockaddr(|addr_ptr, addr_len| { + let mut h = zero_msghdr(); + h.msg_name = addr_ptr as *mut _; + h.msg_namelen = bitcast!(addr_len); + h.msg_iov = iov.as_ptr() as _; + h.msg_iovlen = msg_iov_len(iov.len()); + h.msg_control = control.as_control_ptr().cast(); + h.msg_controllen = msg_control_len(control.control_len()); + // Pass a reference to the `c::msghdr` instead of passing it by value + // because it may contain pointers to temporary objects that won't + // live beyond the call to `with_sockaddr`. + f(&h) + }) +} + +/// Create a zero-initialized message header struct value. +#[cfg(all(unix, not(target_os = "redox")))] +pub(crate) fn zero_msghdr() -> c::msghdr { + // SAFETY: We can't initialize all the fields by value because on some + // platforms the `msghdr` struct in the libc crate contains private padding + // fields. But it is still a C type that's meant to be zero-initializable. + unsafe { zeroed() } +} diff --git a/vendor/rustix/src/backend/libc/net/netdevice.rs b/vendor/rustix/src/backend/libc/net/netdevice.rs new file mode 100644 index 00000000..887f768d --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/netdevice.rs @@ -0,0 +1,55 @@ +//! Wrappers for netdevice ioctls. + +#![allow(unsafe_code)] + +#[cfg(feature = "alloc")] +use crate::alloc::string::String; +use crate::backend::c; +use crate::backend::io::syscalls::ioctl; +use crate::fd::BorrowedFd; +use crate::io; +#[cfg(feature = "alloc")] +use c::SIOCGIFNAME; +use c::{__c_anonymous_ifr_ifru, c_char, ifreq, IFNAMSIZ, SIOCGIFINDEX}; + +pub(crate) fn name_to_index(fd: BorrowedFd<'_>, if_name: &str) -> io::Result<u32> { + let if_name_bytes = if_name.as_bytes(); + if if_name_bytes.len() >= IFNAMSIZ as usize { + return Err(io::Errno::NODEV); + } + + let mut ifreq = ifreq { + ifr_name: [0; 16], + ifr_ifru: __c_anonymous_ifr_ifru { ifru_ifindex: 0 }, + }; + + let mut if_name_c_char_iter = if_name_bytes.iter().map(|byte| *byte as c_char); + ifreq.ifr_name[..if_name_bytes.len()].fill_with(|| if_name_c_char_iter.next().unwrap()); + + unsafe { ioctl(fd, SIOCGIFINDEX as _, &mut ifreq as *mut ifreq as _) }?; + let index = unsafe { ifreq.ifr_ifru.ifru_ifindex }; + Ok(index as u32) +} + +#[cfg(feature = "alloc")] +pub(crate) fn index_to_name(fd: BorrowedFd<'_>, index: u32) -> io::Result<String> { + let mut ifreq = ifreq { + ifr_name: [0; 16], + ifr_ifru: __c_anonymous_ifr_ifru { + ifru_ifindex: index as _, + }, + }; + + unsafe { ioctl(fd, SIOCGIFNAME as _, &mut ifreq as *mut ifreq as _) }?; + + if let Some(nul_byte) = ifreq.ifr_name.iter().position(|char| *char == 0) { + let name: String = ifreq.ifr_name[..nul_byte] + .iter() + .map(|v| *v as u8 as char) + .collect(); + + Ok(name) + } else { + Err(io::Errno::INVAL) + } +} diff --git a/vendor/rustix/src/backend/libc/net/read_sockaddr.rs b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs new file mode 100644 index 00000000..5f8c48ec --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs @@ -0,0 +1,264 @@ +//! The BSD sockets API requires us to read the `sa_family` field before we can +//! interpret the rest of a `sockaddr` produced by the kernel. + +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use super::ext::{in6_addr_s6_addr, in_addr_s_addr, sockaddr_in6_sin6_scope_id}; +use crate::backend::c; +#[cfg(not(windows))] +use crate::ffi::CStr; +use crate::io::Errno; +use crate::net::addr::SocketAddrLen; +#[cfg(linux_kernel)] +use crate::net::netlink::SocketAddrNetlink; +#[cfg(target_os = "linux")] +use crate::net::xdp::{SocketAddrXdp, SocketAddrXdpFlags}; +use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use core::mem::size_of; + +// This must match the header of `sockaddr`. +#[repr(C)] +pub(crate) struct sockaddr_header { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sa_len: u8, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + ))] + sa_family: u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "nto", + target_os = "vita" + )))] + sa_family: u16, +} + +/// Read the `sa_family` field from a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +#[inline] +pub(crate) unsafe fn read_sa_family(storage: *const c::sockaddr) -> u16 { + // Assert that we know the layout of `sockaddr`. + let _ = c::sockaddr { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + sa_len: 0_u8, + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + sa_family: 0_u8, + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + )))] + sa_family: 0_u16, + #[cfg(not(any(target_os = "haiku", target_os = "horizon")))] + sa_data: [0; 14], + #[cfg(target_os = "horizon")] + sa_data: [0; 26], + #[cfg(target_os = "haiku")] + sa_data: [0; 30], + }; + + (*storage.cast::<sockaddr_header>()).sa_family.into() +} + +/// Read the first byte of the `sun_path` field, assuming we have an `AF_UNIX` +/// socket address. +#[cfg(apple)] +#[inline] +unsafe fn read_sun_path0(storage: *const c::sockaddr) -> u8 { + // In `read_sa_family` we assert that we know the layout of `sockaddr`. + storage + .cast::<u8>() + .add(super::addr::offsetof_sun_path()) + .read() +} + +/// Check if a socket address returned from the OS is considered non-empty. +/// +/// # Safety +/// +/// `storage` must point to a least an initialized `sockaddr_header`. +#[inline] +pub(crate) unsafe fn sockaddr_nonempty(storage: *const c::sockaddr, len: SocketAddrLen) -> bool { + if len == 0 { + return false; + } + + assert!(len as usize >= size_of::<c::sa_family_t>()); + let family: c::c_int = read_sa_family(storage.cast::<c::sockaddr>()).into(); + if family == c::AF_UNSPEC { + return false; + } + + // On macOS, if we get an `AF_UNIX` with an empty path, treat it as an + // absent address. + #[cfg(apple)] + if family == c::AF_UNIX && read_sun_path0(storage) == 0 { + return false; + } + + true +} + +/// Set the `sa_family` field of a socket address to `AF_UNSPEC`, so that we +/// can test for `AF_UNSPEC` to test whether it was stored to. +/// +/// # Safety +/// +/// `storage` must point to a least an initialized `sockaddr_header`. +pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr) { + (*storage.cast::<sockaddr_header>()).sa_family = c::AF_UNSPEC as _; +} + +#[inline] +pub(crate) fn read_sockaddr_v4(addr: &SocketAddrAny) -> Result<SocketAddrV4, Errno> { + if addr.address_family() != AddressFamily::INET { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in>()); + let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in>() }; + Ok(SocketAddrV4::new( + Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), + u16::from_be(decode.sin_port), + )) +} + +#[inline] +pub(crate) fn read_sockaddr_v6(addr: &SocketAddrAny) -> Result<SocketAddrV6, Errno> { + if addr.address_family() != AddressFamily::INET6 { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_in6>()); + let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_in6>() }; + Ok(SocketAddrV6::new( + Ipv6Addr::from(in6_addr_s6_addr(decode.sin6_addr)), + u16::from_be(decode.sin6_port), + u32::from_be(decode.sin6_flowinfo), + sockaddr_in6_sin6_scope_id(decode), + )) +} + +#[cfg(unix)] +#[inline] +pub(crate) fn read_sockaddr_unix(addr: &SocketAddrAny) -> Result<SocketAddrUnix, Errno> { + if addr.address_family() != AddressFamily::UNIX { + return Err(Errno::AFNOSUPPORT); + } + + let offsetof_sun_path = super::addr::offsetof_sun_path(); + let len = addr.addr_len() as usize; + + assert!(len >= offsetof_sun_path); + + if len == offsetof_sun_path { + SocketAddrUnix::new(&[][..]) + } else { + let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_un>() }; + + // On Linux check for Linux's [abstract namespace]. + // + // [abstract namespace]: https://man7.org/linux/man-pages/man7/unix.7.html + #[cfg(linux_kernel)] + if decode.sun_path[0] == 0 { + let name = &decode.sun_path[1..len - offsetof_sun_path]; + let name = unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(name) }; + return SocketAddrUnix::new_abstract_name(name); + } + + // Otherwise we expect a NUL-terminated filesystem path. + + // Trim off unused bytes from the end of `path_bytes`. + let path_bytes = if cfg!(any(solarish, target_os = "freebsd")) { + // FreeBSD and illumos sometimes set the length to longer + // than the length of the NUL-terminated string. Find the + // NUL and truncate the string accordingly. + &decode.sun_path[..decode + .sun_path + .iter() + .position(|b| *b == 0) + .ok_or(Errno::INVAL)?] + } else { + // Otherwise, use the provided length. + let provided_len = len - 1 - offsetof_sun_path; + if decode.sun_path[provided_len] != 0 { + return Err(Errno::INVAL); + } + debug_assert_eq!( + unsafe { CStr::from_ptr(decode.sun_path.as_ptr().cast()) } + .to_bytes() + .len(), + provided_len + ); + &decode.sun_path[..provided_len] + }; + + SocketAddrUnix::new(unsafe { core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes) }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn read_sockaddr_xdp(addr: &SocketAddrAny) -> Result<SocketAddrXdp, Errno> { + if addr.address_family() != AddressFamily::XDP { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_xdp>()); + let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_xdp>() }; + + // This ignores the `sxdp_shared_umem_fd` field, which is only expected to + // be significant in `bind` calls, and not returned from `acceptfrom` or + // `recvmsg` or similar. + Ok(SocketAddrXdp::new( + SocketAddrXdpFlags::from_bits_retain(decode.sxdp_flags), + u32::from_be(decode.sxdp_ifindex), + u32::from_be(decode.sxdp_queue_id), + )) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn read_sockaddr_netlink(addr: &SocketAddrAny) -> Result<SocketAddrNetlink, Errno> { + if addr.address_family() != AddressFamily::NETLINK { + return Err(Errno::AFNOSUPPORT); + } + assert!(addr.addr_len() as usize >= size_of::<c::sockaddr_nl>()); + let decode = unsafe { &*addr.as_ptr().cast::<c::sockaddr_nl>() }; + Ok(SocketAddrNetlink::new(decode.nl_pid, decode.nl_groups)) +} diff --git a/vendor/rustix/src/backend/libc/net/send_recv.rs b/vendor/rustix/src/backend/libc/net/send_recv.rs new file mode 100644 index 00000000..4b67f2c5 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/send_recv.rs @@ -0,0 +1,153 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { + /// `MSG_*` flags for use with [`send`], [`sendto`], and related + /// functions. + /// + /// [`send`]: crate::net::send + /// [`sendto`]: crate::net::sendto + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct SendFlags: u32 { + /// `MSG_CONFIRM` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "espidf", + target_os = "nto", + target_os = "haiku", + target_os = "horizon", + target_os = "hurd", + target_os = "redox", + target_os = "vita", + )))] + const CONFIRM = bitcast!(c::MSG_CONFIRM); + /// `MSG_DONTROUTE` + const DONTROUTE = bitcast!(c::MSG_DONTROUTE); + /// `MSG_DONTWAIT` + #[cfg(not(windows))] + const DONTWAIT = bitcast!(c::MSG_DONTWAIT); + /// `MSG_EOR` + #[cfg(not(any(windows, target_os = "horizon")))] + const EOR = bitcast!(c::MSG_EOR); + /// `MSG_MORE` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + )))] + const MORE = bitcast!(c::MSG_MORE); + #[cfg(not(any(apple, windows, target_os = "redox", target_os = "vita")))] + /// `MSG_NOSIGNAL` + const NOSIGNAL = bitcast!(c::MSG_NOSIGNAL); + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `MSG_*` flags for use with [`recv`], [`recvfrom`], and related + /// functions. + /// + /// [`recv`]: crate::net::recv + /// [`recvfrom`]: crate::net::recvfrom + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct RecvFlags: u32 { + /// `MSG_CMSG_CLOEXEC` + #[cfg(not(any( + apple, + solarish, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", + )))] + const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC); + /// `MSG_DONTWAIT` + #[cfg(not(windows))] + const DONTWAIT = bitcast!(c::MSG_DONTWAIT); + /// `MSG_ERRQUEUE` + #[cfg(not(any( + bsd, + solarish, + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "hurd", + target_os = "nto", + target_os = "redox", + target_os = "vita", + )))] + const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE); + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + /// `MSG_PEEK` + const PEEK = bitcast!(c::MSG_PEEK); + /// `MSG_TRUNC` + // Apple, illumos, and NetBSD have `MSG_TRUNC` but it's not documented + // for use with `recv` and friends, and in practice appears to be + // ignored. + #[cfg(not(any(apple, solarish, target_os = "horizon", target_os = "netbsd")))] + const TRUNC = bitcast!(c::MSG_TRUNC); + /// `MSG_WAITALL` + const WAITALL = bitcast!(c::MSG_WAITALL); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} + +bitflags! { + /// `MSG_*` flags returned from [`recvmsg`], in the `flags` field of + /// [`RecvMsg`] + /// + /// [`recvmsg`]: crate::net::recvmsg + /// [`RecvMsg`]: crate::net::RecvMsg + #[repr(transparent)] + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] + pub struct ReturnFlags: u32 { + /// `MSG_OOB` + const OOB = bitcast!(c::MSG_OOB); + /// `MSG_EOR` + #[cfg(not(any(windows, target_os = "horizon")))] + const EOR = bitcast!(c::MSG_EOR); + /// `MSG_TRUNC` + #[cfg(not(target_os = "horizon"))] + const TRUNC = bitcast!(c::MSG_TRUNC); + /// `MSG_CTRUNC` + #[cfg(not(target_os = "horizon"))] + const CTRUNC = bitcast!(c::MSG_CTRUNC); + + /// `MSG_CMSG_CLOEXEC` + #[cfg(linux_kernel)] + const CMSG_CLOEXEC = bitcast!(c::MSG_CMSG_CLOEXEC); + /// `MSG_ERRQUEUE` + #[cfg(linux_kernel)] + const ERRQUEUE = bitcast!(c::MSG_ERRQUEUE); + + /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags> + const _ = !0; + } +} diff --git a/vendor/rustix/src/backend/libc/net/sockopt.rs b/vendor/rustix/src/backend/libc/net/sockopt.rs new file mode 100644 index 00000000..132ebe75 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/sockopt.rs @@ -0,0 +1,1339 @@ +//! libc syscalls supporting `rustix::net::sockopt`. + +use super::ext::{in6_addr_new, in_addr_new}; +use crate::backend::c; +use crate::backend::conv::{borrowed_fd, ret}; +use crate::fd::BorrowedFd; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use crate::ffi::CStr; +use crate::io; +use crate::net::sockopt::Timeout; +#[cfg(target_os = "linux")] +use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg}; +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "haiku", + target_os = "netbsd", + target_os = "nto", + target_os = "vita", +)))] +use crate::net::AddressFamily; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::Protocol; +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +use crate::net::RawProtocol; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::net::SocketAddrV4; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; +#[cfg(linux_kernel)] +use crate::net::{SocketAddrV6, UCred}; +use crate::utils::as_mut_ptr; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use alloc::borrow::ToOwned as _; +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +use alloc::string::String; +#[cfg(apple)] +use c::TCP_KEEPALIVE as TCP_KEEPIDLE; +#[cfg(not(any(apple, target_os = "haiku", target_os = "nto", target_os = "openbsd")))] +use c::TCP_KEEPIDLE; +use core::mem::{size_of, MaybeUninit}; +use core::time::Duration; +#[cfg(target_os = "linux")] +use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1}; +#[cfg(windows)] +use windows_sys::Win32::Foundation::BOOL; + +#[inline] +fn getsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32) -> io::Result<T> { + let mut optlen = size_of::<T>().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + + let mut value = MaybeUninit::<T>::zeroed(); + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + + // On Windows at least, `getsockopt` has been observed writing 1 + // byte on at least (`IPPROTO_TCP`, `TCP_NODELAY`), even though + // Windows' documentation says that should write a 4-byte `BOOL`. + // So, we initialize the memory to zeros above, and just assert + // that `getsockopt` doesn't write too many bytes here. + assert!( + optlen as usize <= size_of::<T>(), + "unexpected getsockopt size" + ); + + unsafe { Ok(value.assume_init()) } +} + +#[inline] +fn getsockopt_raw<T>( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + value: &mut MaybeUninit<T>, + optlen: &mut c::socklen_t, +) -> io::Result<()> { + unsafe { + ret(c::getsockopt( + borrowed_fd(fd), + level, + optname, + as_mut_ptr(value).cast(), + optlen, + )) + } +} + +#[inline] +fn setsockopt<T: Copy>(fd: BorrowedFd<'_>, level: i32, optname: i32, value: T) -> io::Result<()> { + let optlen = size_of::<T>().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + setsockopt_raw(fd, level, optname, &value, optlen) +} + +#[inline] +fn setsockopt_raw<T>( + fd: BorrowedFd<'_>, + level: i32, + optname: i32, + ptr: *const T, + optlen: c::socklen_t, +) -> io::Result<()> { + unsafe { + ret(c::setsockopt( + borrowed_fd(fd), + level, + optname, + ptr.cast(), + optlen, + )) + } +} + +#[inline] +pub(crate) fn socket_type(fd: BorrowedFd<'_>) -> io::Result<SocketType> { + getsockopt(fd, c::SOL_SOCKET, c::SO_TYPE) +} + +#[inline] +pub(crate) fn set_socket_reuseaddr(fd: BorrowedFd<'_>, reuseaddr: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR, from_bool(reuseaddr)) +} + +#[inline] +pub(crate) fn socket_reuseaddr(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEADDR).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_broadcast(fd: BorrowedFd<'_>, broadcast: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST, from_bool(broadcast)) +} + +#[inline] +pub(crate) fn socket_broadcast(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_BROADCAST).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_linger(fd: BorrowedFd<'_>, linger: Option<Duration>) -> io::Result<()> { + // Convert `linger` to seconds, rounding up. + let l_linger = if let Some(linger) = linger { + duration_to_secs(linger)? + } else { + 0 + }; + let linger = c::linger { + l_onoff: linger.is_some().into(), + l_linger, + }; + setsockopt(fd, c::SOL_SOCKET, c::SO_LINGER, linger) +} + +#[inline] +pub(crate) fn socket_linger(fd: BorrowedFd<'_>) -> io::Result<Option<Duration>> { + let linger: c::linger = getsockopt(fd, c::SOL_SOCKET, c::SO_LINGER)?; + Ok((linger.l_onoff != 0).then(|| Duration::from_secs(linger.l_linger as u64))) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_socket_passcred(fd: BorrowedFd<'_>, passcred: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED, from_bool(passcred)) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn socket_passcred(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PASSCRED).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_timeout( + fd: BorrowedFd<'_>, + id: Timeout, + timeout: Option<Duration>, +) -> io::Result<()> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO, + Timeout::Send => c::SO_SNDTIMEO, + }; + + #[cfg(not(windows))] + let timeout = match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + + // Rust's musl libc bindings deprecated `time_t` while they + // transition to 64-bit `time_t`. What we want here is just + // “whatever type `timeval`'s `tv_sec` is”, so we're ok using + // the deprecated type. + #[allow(deprecated)] + let tv_sec = timeout.as_secs().try_into().unwrap_or(c::time_t::MAX); + + // `subsec_micros` rounds down, so we use `subsec_nanos` and + // manually round up. + let mut timeout = c::timeval { + tv_sec, + tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => c::timeval { + tv_sec: 0, + tv_usec: 0, + }, + }; + + #[cfg(windows)] + let timeout: u32 = match timeout { + Some(timeout) => { + if timeout == Duration::ZERO { + return Err(io::Errno::INVAL); + } + + // `as_millis` rounds down, so we use `as_nanos` and + // manually round up. + let mut timeout: u32 = ((timeout.as_nanos() + 999_999) / 1_000_000) + .try_into() + .map_err(|_convert_err| io::Errno::INVAL)?; + if timeout == 0 { + timeout = 1; + } + timeout + } + None => 0, + }; + + setsockopt(fd, c::SOL_SOCKET, optname, timeout) +} + +#[inline] +pub(crate) fn socket_timeout(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> { + let optname = match id { + Timeout::Recv => c::SO_RCVTIMEO, + Timeout::Send => c::SO_SNDTIMEO, + }; + + #[cfg(not(windows))] + { + let timeout: c::timeval = getsockopt(fd, c::SOL_SOCKET, optname)?; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + Ok(None) + } else { + Ok(Some( + Duration::from_secs(timeout.tv_sec as u64) + + Duration::from_micros(timeout.tv_usec as u64), + )) + } + } + + #[cfg(windows)] + { + let timeout: u32 = getsockopt(fd, c::SOL_SOCKET, optname)?; + if timeout == 0 { + Ok(None) + } else { + Ok(Some(Duration::from_millis(timeout as u64))) + } + } +} + +#[cfg(any(apple, freebsdlike, target_os = "netbsd"))] +#[inline] +pub(crate) fn socket_nosigpipe(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE).map(to_bool) +} + +#[cfg(any(apple, freebsdlike, target_os = "netbsd"))] +#[inline] +pub(crate) fn set_socket_nosigpipe(fd: BorrowedFd<'_>, val: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_NOSIGPIPE, from_bool(val)) +} + +#[inline] +pub(crate) fn socket_error(fd: BorrowedFd<'_>) -> io::Result<Result<(), io::Errno>> { + let err: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_ERROR)?; + Ok(if err == 0 { + Ok(()) + } else { + Err(io::Errno::from_raw_os_error(err)) + }) +} + +#[inline] +pub(crate) fn set_socket_keepalive(fd: BorrowedFd<'_>, keepalive: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE, from_bool(keepalive)) +} + +#[inline] +pub(crate) fn socket_keepalive(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_KEEPALIVE).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_recv_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF, size) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "redox"))] +#[inline] +pub(crate) fn set_socket_recv_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUFFORCE, size) +} + +#[inline] +pub(crate) fn socket_recv_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { + getsockopt(fd, c::SOL_SOCKET, c::SO_RCVBUF).map(|size: u32| size as usize) +} + +#[inline] +pub(crate) fn set_socket_send_buffer_size(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF, size) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia", target_os = "redox"))] +#[inline] +pub(crate) fn set_socket_send_buffer_size_force(fd: BorrowedFd<'_>, size: usize) -> io::Result<()> { + let size: c::c_int = size.try_into().map_err(|_| io::Errno::INVAL)?; + setsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUFFORCE, size) +} + +#[inline] +pub(crate) fn socket_send_buffer_size(fd: BorrowedFd<'_>) -> io::Result<usize> { + getsockopt(fd, c::SOL_SOCKET, c::SO_SNDBUF).map(|size: u32| size as usize) +} + +#[inline] +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "hurd", + target_os = "netbsd", + target_os = "nto", + target_os = "vita", +)))] +pub(crate) fn socket_domain(fd: BorrowedFd<'_>) -> io::Result<AddressFamily> { + let domain: c::c_int = getsockopt(fd, c::SOL_SOCKET, c::SO_DOMAIN)?; + Ok(AddressFamily( + domain.try_into().map_err(|_| io::Errno::OPNOTSUPP)?, + )) +} + +#[inline] +#[cfg(not(apple))] // Apple platforms declare the constant, but do not actually implement it. +pub(crate) fn socket_acceptconn(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_ACCEPTCONN).map(to_bool) +} + +#[inline] +pub(crate) fn set_socket_oobinline(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE, from_bool(value)) +} + +#[inline] +pub(crate) fn socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool) +} + +#[cfg(not(any(solarish, windows, target_os = "cygwin")))] +#[inline] +pub(crate) fn set_socket_reuseport(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT, from_bool(value)) +} + +#[cfg(not(any(solarish, windows, target_os = "cygwin")))] +#[inline] +pub(crate) fn socket_reuseport(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT).map(to_bool) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn set_socket_reuseport_lb(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB, from_bool(value)) +} + +#[cfg(target_os = "freebsd")] +#[inline] +pub(crate) fn socket_reuseport_lb(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::SOL_SOCKET, c::SO_REUSEPORT_LB).map(to_bool) +} + +#[cfg(any( + linux_kernel, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "openbsd", + target_os = "redox", + target_env = "newlib" +))] +#[inline] +pub(crate) fn socket_protocol(fd: BorrowedFd<'_>) -> io::Result<Option<Protocol>> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PROTOCOL) + .map(|raw| RawProtocol::new(raw).map(Protocol::from_raw)) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> { + getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn socket_incoming_cpu(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_socket_incoming_cpu(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_SOCKET, c::SO_INCOMING_CPU, value) +} + +#[inline] +pub(crate) fn set_ip_ttl(fd: BorrowedFd<'_>, ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TTL, ttl) +} + +#[inline] +pub(crate) fn ip_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IP_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_v6only(fd: BorrowedFd<'_>, only_v6: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY, from_bool(only_v6)) +} + +#[inline] +pub(crate) fn ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "cygwin"))] +#[inline] +pub(crate) fn ip_mtu(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MTU) +} + +#[cfg(any(linux_kernel, target_os = "cygwin"))] +#[inline] +pub(crate) fn ipv6_mtu(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MTU) +} + +#[inline] +pub(crate) fn set_ip_multicast_if(fd: BorrowedFd<'_>, value: &Ipv4Addr) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF, to_imr_addr(value)) +} + +#[inline] +pub(crate) fn ip_multicast_if(fd: BorrowedFd<'_>) -> io::Result<Ipv4Addr> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF).map(from_in_addr) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +pub(crate) fn set_ip_multicast_if_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_IF, mreqn) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_if(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_IF, value as c::c_int) +} + +#[inline] +pub(crate) fn ipv6_multicast_if(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_IF) +} + +#[inline] +pub(crate) fn set_ip_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn ip_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ip_multicast_ttl(fd: BorrowedFd<'_>, multicast_ttl: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL, multicast_ttl) +} + +#[inline] +pub(crate) fn ip_multicast_ttl(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IP_MULTICAST_TTL) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_loop(fd: BorrowedFd<'_>, multicast_loop: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_IPV6, + c::IPV6_MULTICAST_LOOP, + from_bool(multicast_loop), + ) +} + +#[inline] +pub(crate) fn ipv6_multicast_loop(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP).map(to_bool) +} + +#[inline] +pub(crate) fn set_ipv6_multicast_hops(fd: BorrowedFd<'_>, multicast_hops: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS, multicast_hops) +} + +#[inline] +pub(crate) fn ipv6_multicast_hops(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IP, c::IPV6_MULTICAST_HOPS) +} + +#[inline] +pub(crate) fn set_ip_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +pub(crate) fn set_ip_add_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreqn) +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +pub(crate) fn set_ip_add_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_ADD_SOURCE_MEMBERSHIP, mreq_source) +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +pub(crate) fn set_ip_drop_source_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> io::Result<()> { + let mreq_source = to_imr_source(multiaddr, interface, sourceaddr); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_SOURCE_MEMBERSHIP, mreq_source) +} + +#[inline] +pub(crate) fn set_ipv6_add_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + #[cfg(not(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + )))] + use c::IPV6_ADD_MEMBERSHIP; + #[cfg(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + ))] + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn set_ip_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, +) -> io::Result<()> { + let mreq = to_ip_mreq(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +pub(crate) fn set_ip_drop_membership_with_ifindex( + fd: BorrowedFd<'_>, + multiaddr: &Ipv4Addr, + address: &Ipv4Addr, + ifindex: u32, +) -> io::Result<()> { + let mreqn = to_ip_mreqn(multiaddr, address, ifindex as i32); + setsockopt(fd, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreqn) +} + +#[inline] +pub(crate) fn set_ipv6_drop_membership( + fd: BorrowedFd<'_>, + multiaddr: &Ipv6Addr, + interface: u32, +) -> io::Result<()> { + #[cfg(not(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + )))] + use c::IPV6_DROP_MEMBERSHIP; + #[cfg(any( + bsd, + solarish, + target_os = "haiku", + target_os = "l4re", + target_os = "nto" + ))] + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + + let mreq = to_ipv6mr(multiaddr, interface); + setsockopt(fd, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) +} + +#[inline] +pub(crate) fn ipv6_unicast_hops(fd: BorrowedFd<'_>) -> io::Result<u8> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS).map(|hops: c::c_int| hops as u8) +} + +#[inline] +pub(crate) fn set_ipv6_unicast_hops(fd: BorrowedFd<'_>, hops: Option<u8>) -> io::Result<()> { + let hops = match hops { + Some(hops) => hops as c::c_int, + None => -1, + }; + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_UNICAST_HOPS, hops) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_env = "newlib" +))] +#[inline] +pub(crate) fn set_ip_tos(fd: BorrowedFd<'_>, value: u8) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_TOS, i32::from(value)) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "haiku", + target_os = "nto", + target_env = "newlib" +))] +#[inline] +pub(crate) fn ip_tos(fd: BorrowedFd<'_>) -> io::Result<u8> { + let value: i32 = getsockopt(fd, c::IPPROTO_IP, c::IP_TOS)?; + Ok(value as u8) +} + +#[cfg(any( + apple, + linux_like, + target_os = "cygwin", + target_os = "freebsd", + target_os = "fuchsia", +))] +#[inline] +pub(crate) fn set_ip_recvtos(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS, from_bool(value)) +} + +#[cfg(any( + apple, + linux_like, + target_os = "cygwin", + target_os = "freebsd", + target_os = "fuchsia", +))] +#[inline] +pub(crate) fn ip_recvtos(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_RECVTOS).map(to_bool) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "nto" +))] +#[inline] +pub(crate) fn set_ipv6_recvtclass(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS, from_bool(value)) +} + +#[cfg(any( + bsd, + linux_like, + target_os = "aix", + target_os = "fuchsia", + target_os = "nto" +))] +#[inline] +pub(crate) fn ipv6_recvtclass(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_RECVTCLASS).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_ip_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND, from_bool(value)) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn ip_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IP, c::IP_FREEBIND).map(to_bool) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn set_ipv6_freebind(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND, from_bool(value)) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn ipv6_freebind(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_FREEBIND).map(to_bool) +} + +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +#[inline] +pub(crate) fn ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> { + let level = c::IPPROTO_IP; + let optname = c::SO_ORIGINAL_DST; + + let mut addr = crate::net::SocketAddrBuf::new(); + getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?; + Ok(unsafe { addr.into_any() }.try_into().unwrap()) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> { + let level = c::IPPROTO_IPV6; + let optname = c::IP6T_SO_ORIGINAL_DST; + + let mut addr = crate::net::SocketAddrBuf::new(); + getsockopt_raw(fd, level, optname, &mut addr.storage, &mut addr.len)?; + Ok(unsafe { addr.into_any() }.try_into().unwrap()) +} + +#[cfg(not(any( + solarish, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +#[inline] +pub(crate) fn set_ipv6_tclass(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS, value) +} + +#[cfg(not(any( + solarish, + windows, + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +#[inline] +pub(crate) fn ipv6_tclass(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_TCLASS) +} + +#[inline] +pub(crate) fn set_tcp_nodelay(fd: BorrowedFd<'_>, nodelay: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY, from_bool(nodelay)) +} + +#[inline] +pub(crate) fn tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool) +} + +#[inline] +#[cfg(not(any( + target_os = "haiku", + target_os = "nto", + target_os = "openbsd", + target_os = "redox" +)))] +pub(crate) fn set_tcp_keepcnt(fd: BorrowedFd<'_>, count: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT, count) +} + +#[inline] +#[cfg(not(any( + target_os = "haiku", + target_os = "nto", + target_os = "openbsd", + target_os = "redox" +)))] +pub(crate) fn tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT) +} + +#[inline] +#[cfg(not(any(target_os = "haiku", target_os = "nto", target_os = "openbsd")))] +pub(crate) fn set_tcp_keepidle(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE, secs) +} + +#[inline] +#[cfg(not(any(target_os = "haiku", target_os = "nto", target_os = "openbsd")))] +pub(crate) fn tcp_keepidle(fd: BorrowedFd<'_>) -> io::Result<Duration> { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, TCP_KEEPIDLE)?; + Ok(Duration::from_secs(secs as u64)) +} + +#[inline] +#[cfg(not(any( + target_os = "haiku", + target_os = "nto", + target_os = "openbsd", + target_os = "redox" +)))] +pub(crate) fn set_tcp_keepintvl(fd: BorrowedFd<'_>, duration: Duration) -> io::Result<()> { + let secs: c::c_uint = duration_to_secs(duration)?; + setsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL, secs) +} + +#[inline] +#[cfg(not(any( + target_os = "haiku", + target_os = "nto", + target_os = "openbsd", + target_os = "redox" +)))] +pub(crate) fn tcp_keepintvl(fd: BorrowedFd<'_>) -> io::Result<Duration> { + let secs: c::c_uint = getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPINTVL)?; + Ok(Duration::from_secs(secs as u64)) +} + +#[inline] +#[cfg(any(linux_like, target_os = "fuchsia"))] +pub(crate) fn set_tcp_user_timeout(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT, value) +} + +#[inline] +#[cfg(any(linux_like, target_os = "fuchsia"))] +pub(crate) fn tcp_user_timeout(fd: BorrowedFd<'_>) -> io::Result<u32> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_USER_TIMEOUT) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_quickack(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK, from_bool(value)) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn tcp_quickack(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_QUICKACK).map(to_bool) +} + +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +#[inline] +pub(crate) fn set_tcp_congestion(fd: BorrowedFd<'_>, value: &str) -> io::Result<()> { + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + let optlen = value.len().try_into().unwrap(); + setsockopt_raw(fd, level, optname, value.as_ptr(), optlen) +} + +#[cfg(feature = "alloc")] +#[cfg(any( + linux_like, + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos" +))] +#[inline] +pub(crate) fn tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> { + const OPTLEN: c::socklen_t = 16; + + let level = c::IPPROTO_TCP; + let optname = c::TCP_CONGESTION; + let mut value = MaybeUninit::<[MaybeUninit<u8>; OPTLEN as usize]>::uninit(); + let mut optlen = OPTLEN; + getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + unsafe { + let value = value.assume_init(); + let slice: &[u8] = core::mem::transmute(&value[..optlen as usize]); + assert!(slice.contains(&b'\0')); + Ok( + core::str::from_utf8(CStr::from_ptr(slice.as_ptr().cast()).to_bytes()) + .unwrap() + .to_owned(), + ) + } +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_thin_linear_timeouts(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt( + fd, + c::IPPROTO_TCP, + c::TCP_THIN_LINEAR_TIMEOUTS, + from_bool(value), + ) +} + +#[cfg(any(linux_like, target_os = "fuchsia"))] +#[inline] +pub(crate) fn tcp_thin_linear_timeouts(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_THIN_LINEAR_TIMEOUTS).map(to_bool) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn set_tcp_cork(fd: BorrowedFd<'_>, value: bool) -> io::Result<()> { + setsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK, from_bool(value)) +} + +#[cfg(any(linux_like, solarish, target_os = "fuchsia"))] +#[inline] +pub(crate) fn tcp_cork(fd: BorrowedFd<'_>) -> io::Result<bool> { + getsockopt(fd, c::IPPROTO_TCP, c::TCP_CORK).map(to_bool) +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> { + getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> { + setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> { + // The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the + // supplied pointer, depending on the kernel version. Both structs only + // contain u64 values. By using the larger of both as the parameter, we can + // shuffle the values to the non-v1 version returned by + // `get_xdp_mmap_offsets` while keeping the return type unaffected by the + // kernel version. This works because C will layout all struct members one + // after the other. + + let mut optlen = size_of::<xdp_mmap_offsets>().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed(); + getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?; + + if optlen as usize == size_of::<c::xdp_mmap_offsets_v1>() { + // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are + // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`. + let xpd_mmap_offsets = unsafe { value.assume_init() }; + Ok(XdpMmapOffsets { + rx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.producer, + consumer: xpd_mmap_offsets.rx.consumer, + desc: xpd_mmap_offsets.rx.desc, + flags: None, + }, + tx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.flags, + consumer: xpd_mmap_offsets.tx.producer, + desc: xpd_mmap_offsets.tx.consumer, + flags: None, + }, + fr: XdpRingOffset { + producer: xpd_mmap_offsets.tx.desc, + consumer: xpd_mmap_offsets.tx.flags, + desc: xpd_mmap_offsets.fr.producer, + flags: None, + }, + cr: XdpRingOffset { + producer: xpd_mmap_offsets.fr.consumer, + consumer: xpd_mmap_offsets.fr.desc, + desc: xpd_mmap_offsets.fr.flags, + flags: None, + }, + }) + } else { + assert_eq!( + optlen as usize, + size_of::<xdp_mmap_offsets>(), + "unexpected getsockopt size" + ); + // SAFETY: All members of xdp_mmap_offsets are `u64` and thus are + // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()` + let xpd_mmap_offsets = unsafe { value.assume_init() }; + Ok(XdpMmapOffsets { + rx: XdpRingOffset { + producer: xpd_mmap_offsets.rx.producer, + consumer: xpd_mmap_offsets.rx.consumer, + desc: xpd_mmap_offsets.rx.desc, + flags: Some(xpd_mmap_offsets.rx.flags), + }, + tx: XdpRingOffset { + producer: xpd_mmap_offsets.tx.producer, + consumer: xpd_mmap_offsets.tx.consumer, + desc: xpd_mmap_offsets.tx.desc, + flags: Some(xpd_mmap_offsets.tx.flags), + }, + fr: XdpRingOffset { + producer: xpd_mmap_offsets.fr.producer, + consumer: xpd_mmap_offsets.fr.consumer, + desc: xpd_mmap_offsets.fr.desc, + flags: Some(xpd_mmap_offsets.fr.flags), + }, + cr: XdpRingOffset { + producer: xpd_mmap_offsets.cr.producer, + consumer: xpd_mmap_offsets.cr.consumer, + desc: xpd_mmap_offsets.cr.desc, + flags: Some(xpd_mmap_offsets.cr.flags), + }, + }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> { + let mut optlen = size_of::<xdp_statistics>().try_into().unwrap(); + debug_assert!( + optlen as usize >= size_of::<c::c_int>(), + "Socket APIs don't ever use `bool` directly" + ); + let mut value = MaybeUninit::<xdp_statistics>::zeroed(); + getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?; + + if optlen as usize == size_of::<xdp_statistics_v1>() { + // SAFETY: All members of xdp_statistics are `u64` and thus are + // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`. + let xdp_statistics = unsafe { value.assume_init() }; + Ok(XdpStatistics { + rx_dropped: xdp_statistics.rx_dropped, + rx_invalid_descs: xdp_statistics.rx_dropped, + tx_invalid_descs: xdp_statistics.rx_dropped, + rx_ring_full: None, + rx_fill_ring_empty_descs: None, + tx_ring_empty_descs: None, + }) + } else { + assert_eq!( + optlen as usize, + size_of::<xdp_statistics>(), + "unexpected getsockopt size" + ); + // SAFETY: All members of xdp_statistics are `u64` and thus are + // correctly initialized by `MaybeUninit::<xdp_statistics>::zeroed()`. + let xdp_statistics = unsafe { value.assume_init() }; + Ok(XdpStatistics { + rx_dropped: xdp_statistics.rx_dropped, + rx_invalid_descs: xdp_statistics.rx_invalid_descs, + tx_invalid_descs: xdp_statistics.tx_invalid_descs, + rx_ring_full: Some(xdp_statistics.rx_ring_full), + rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs), + tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs), + }) + } +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> { + getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS) +} + +#[inline] +fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq { + c::ip_mreq { + imr_multiaddr: to_imr_addr(multiaddr), + imr_interface: to_imr_addr(interface), + } +} + +#[cfg(not(windows))] +#[inline] +fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr { + Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()) +} + +#[cfg(windows)] +fn from_in_addr(in_addr: c::in_addr) -> Ipv4Addr { + Ipv4Addr::from(unsafe { in_addr.S_un.S_addr.to_ne_bytes() }) +} + +#[cfg(any( + apple, + freebsdlike, + linux_like, + target_os = "fuchsia", + target_os = "openbsd" +))] +#[inline] +fn to_ip_mreqn(multiaddr: &Ipv4Addr, address: &Ipv4Addr, ifindex: i32) -> c::ip_mreqn { + c::ip_mreqn { + imr_multiaddr: to_imr_addr(multiaddr), + imr_address: to_imr_addr(address), + imr_ifindex: ifindex, + } +} + +#[cfg(any(apple, freebsdlike, linux_like, solarish, target_os = "aix"))] +#[inline] +fn to_imr_source( + multiaddr: &Ipv4Addr, + interface: &Ipv4Addr, + sourceaddr: &Ipv4Addr, +) -> c::ip_mreq_source { + c::ip_mreq_source { + imr_multiaddr: to_imr_addr(multiaddr), + imr_interface: to_imr_addr(interface), + imr_sourceaddr: to_imr_addr(sourceaddr), + } +} + +#[inline] +fn to_imr_addr(addr: &Ipv4Addr) -> c::in_addr { + in_addr_new(u32::from_ne_bytes(addr.octets())) +} + +#[inline] +fn to_ipv6mr(multiaddr: &Ipv6Addr, interface: u32) -> c::ipv6_mreq { + c::ipv6_mreq { + ipv6mr_multiaddr: to_ipv6mr_multiaddr(multiaddr), + ipv6mr_interface: to_ipv6mr_interface(interface), + } +} + +#[inline] +fn to_ipv6mr_multiaddr(multiaddr: &Ipv6Addr) -> c::in6_addr { + in6_addr_new(multiaddr.octets()) +} + +#[cfg(target_os = "android")] +#[inline] +fn to_ipv6mr_interface(interface: u32) -> c::c_int { + interface as c::c_int +} + +#[cfg(not(target_os = "android"))] +#[inline] +fn to_ipv6mr_interface(interface: u32) -> c::c_uint { + interface as c::c_uint +} + +// `getsockopt` and `setsockopt` represent boolean values as integers. +#[cfg(not(windows))] +type RawSocketBool = c::c_int; +#[cfg(windows)] +type RawSocketBool = BOOL; + +// Wrap `RawSocketBool` in a newtype to discourage misuse. +#[repr(transparent)] +#[derive(Copy, Clone)] +struct SocketBool(RawSocketBool); + +// Convert from a `bool` to a `SocketBool`. +#[inline] +fn from_bool(value: bool) -> SocketBool { + SocketBool(value.into()) +} + +// Convert from a `SocketBool` to a `bool`. +#[inline] +fn to_bool(value: SocketBool) -> bool { + value.0 != 0 +} + +/// Convert to seconds, rounding up if necessary. +#[inline] +fn duration_to_secs<T: TryFrom<u64>>(duration: Duration) -> io::Result<T> { + let mut secs = duration.as_secs(); + if duration.subsec_nanos() != 0 { + secs = secs.checked_add(1).ok_or(io::Errno::INVAL)?; + } + T::try_from(secs).map_err(|_e| io::Errno::INVAL) +} diff --git a/vendor/rustix/src/backend/libc/net/syscalls.rs b/vendor/rustix/src/backend/libc/net/syscalls.rs new file mode 100644 index 00000000..aafea002 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/syscalls.rs @@ -0,0 +1,438 @@ +//! libc syscalls supporting `rustix::net`. + +use super::read_sockaddr::initialize_family_to_unspec; +use super::send_recv::{RecvFlags, SendFlags}; +use crate::backend::c; +#[cfg(target_os = "linux")] +use crate::backend::conv::ret_u32; +use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len}; +use crate::fd::{BorrowedFd, OwnedFd}; +use crate::io; +use crate::net::addr::SocketAddrArg; +#[cfg(target_os = "linux")] +use crate::net::MMsgHdr; +use crate::net::{ + AddressFamily, Protocol, Shutdown, SocketAddrAny, SocketAddrBuf, SocketFlags, SocketType, +}; +use crate::utils::as_ptr; +use core::mem::{size_of, MaybeUninit}; +use core::ptr::null_mut; +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +use { + super::msghdr::{noaddr_msghdr, with_msghdr, with_recv_msghdr}, + super::send_recv::ReturnFlags, + crate::io::{IoSlice, IoSliceMut}, + crate::net::{RecvAncillaryBuffer, RecvMsg, SendAncillaryBuffer}, +}; + +pub(crate) unsafe fn recv( + fd: BorrowedFd<'_>, + buf: (*mut u8, usize), + flags: RecvFlags, +) -> io::Result<usize> { + ret_send_recv(c::recv( + borrowed_fd(fd), + buf.0.cast(), + send_recv_len(buf.1), + bitflags_bits!(flags), + )) +} + +pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Result<usize> { + unsafe { + ret_send_recv(c::send( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + )) + } +} + +pub(crate) unsafe fn recvfrom( + fd: BorrowedFd<'_>, + buf: (*mut u8, usize), + flags: RecvFlags, +) -> io::Result<(usize, Option<SocketAddrAny>)> { + let mut addr = SocketAddrBuf::new(); + + // `recvfrom` does not write to the storage if the socket is + // connection-oriented sockets, so we initialize the family field to + // `AF_UNSPEC` so that we can detect this case. + initialize_family_to_unspec(addr.storage.as_mut_ptr().cast::<c::sockaddr>()); + + let nread = ret_send_recv(c::recvfrom( + borrowed_fd(fd), + buf.0.cast(), + send_recv_len(buf.1), + bitflags_bits!(flags), + addr.storage.as_mut_ptr().cast::<c::sockaddr>(), + &mut addr.len, + ))?; + + Ok((nread, addr.into_any_option())) +} + +pub(crate) fn sendto( + fd: BorrowedFd<'_>, + buf: &[u8], + flags: SendFlags, + addr: &impl SocketAddrArg, +) -> io::Result<usize> { + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + ret_send_recv(c::sendto( + borrowed_fd(fd), + buf.as_ptr().cast(), + send_recv_len(buf.len()), + bitflags_bits!(flags), + addr_ptr.cast(), + bitcast!(addr_len), + )) + }) + } +} + +pub(crate) fn socket( + domain: AddressFamily, + type_: SocketType, + protocol: Option<Protocol>, +) -> io::Result<OwnedFd> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + ret_owned_fd(c::socket( + domain.0 as c::c_int, + type_.0 as c::c_int, + raw_protocol as c::c_int, + )) + } +} + +pub(crate) fn socket_with( + domain: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option<Protocol>, +) -> io::Result<OwnedFd> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + ret_owned_fd(c::socket( + domain.0 as c::c_int, + (type_.0 | flags.bits()) as c::c_int, + raw_protocol as c::c_int, + )) + } +} + +pub(crate) fn bind(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> { + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + ret(c::bind( + borrowed_fd(sockfd), + addr_ptr.cast(), + bitcast!(addr_len), + )) + }) + } +} + +pub(crate) fn connect(sockfd: BorrowedFd<'_>, addr: &impl SocketAddrArg) -> io::Result<()> { + unsafe { + addr.with_sockaddr(|addr_ptr, addr_len| { + ret(c::connect( + borrowed_fd(sockfd), + addr_ptr.cast(), + bitcast!(addr_len), + )) + }) + } +} + +pub(crate) fn connect_unspec(sockfd: BorrowedFd<'_>) -> io::Result<()> { + debug_assert_eq!(c::AF_UNSPEC, 0); + let addr = MaybeUninit::<c::sockaddr_storage>::zeroed(); + unsafe { + ret(c::connect( + borrowed_fd(sockfd), + as_ptr(&addr).cast(), + size_of::<c::sockaddr_storage>() as c::socklen_t, + )) + } +} + +pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> { + unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) } +} + +pub(crate) fn accept(sockfd: BorrowedFd<'_>) -> io::Result<OwnedFd> { + unsafe { + let owned_fd = ret_owned_fd(c::accept(borrowed_fd(sockfd), null_mut(), null_mut()))?; + Ok(owned_fd) + } +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub(crate) fn recvmsg( + sockfd: BorrowedFd<'_>, + iov: &mut [IoSliceMut<'_>], + control: &mut RecvAncillaryBuffer<'_>, + msg_flags: RecvFlags, +) -> io::Result<RecvMsg> { + let mut addr = SocketAddrBuf::new(); + + // SAFETY: This passes the `msghdr` reference to the OS which reads the + // buffers only within the designated bounds. + let (bytes, flags) = unsafe { + with_recv_msghdr(&mut addr, iov, control, |msghdr| { + let bytes = ret_send_recv(c::recvmsg( + borrowed_fd(sockfd), + msghdr, + bitflags_bits!(msg_flags), + ))?; + Ok((bytes, msghdr.msg_flags)) + })? + }; + + Ok(RecvMsg { + bytes, + address: unsafe { addr.into_any_option() }, + flags: ReturnFlags::from_bits_retain(bitcast!(flags)), + }) +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub(crate) fn sendmsg( + sockfd: BorrowedFd<'_>, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + let msghdr = noaddr_msghdr(iov, control); + unsafe { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + &msghdr, + bitflags_bits!(msg_flags), + )) + } +} + +#[cfg(not(any( + windows, + target_os = "espidf", + target_os = "horizon", + target_os = "redox", + target_os = "vita" +)))] +pub(crate) fn sendmsg_addr( + sockfd: BorrowedFd<'_>, + addr: &impl SocketAddrArg, + iov: &[IoSlice<'_>], + control: &mut SendAncillaryBuffer<'_, '_, '_>, + msg_flags: SendFlags, +) -> io::Result<usize> { + // SAFETY: This passes the `msghdr` reference to the OS which reads the + // buffers only within the designated bounds. + unsafe { + with_msghdr(addr, iov, control, |msghdr| { + ret_send_recv(c::sendmsg( + borrowed_fd(sockfd), + msghdr, + bitflags_bits!(msg_flags), + )) + }) + } +} + +#[cfg(target_os = "linux")] +pub(crate) fn sendmmsg( + sockfd: BorrowedFd<'_>, + msgs: &mut [MMsgHdr<'_>], + flags: SendFlags, +) -> io::Result<usize> { + unsafe { + ret_u32(c::sendmmsg( + borrowed_fd(sockfd), + msgs.as_mut_ptr() as _, + msgs.len().try_into().unwrap_or(c::c_uint::MAX), + bitflags_bits!(flags), + )) + .map(|ret| ret as usize) + } +} + +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] +pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result<OwnedFd> { + unsafe { + let owned_fd = ret_owned_fd(c::accept4( + borrowed_fd(sockfd), + null_mut(), + null_mut(), + flags.bits() as c::c_int, + ))?; + Ok(owned_fd) + } +} + +pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + unsafe { + let mut addr = SocketAddrBuf::new(); + let owned_fd = ret_owned_fd(c::accept( + borrowed_fd(sockfd), + addr.storage.as_mut_ptr().cast::<c::sockaddr>(), + &mut addr.len, + ))?; + Ok((owned_fd, addr.into_any_option())) + } +} + +#[cfg(not(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +)))] +pub(crate) fn acceptfrom_with( + sockfd: BorrowedFd<'_>, + flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + unsafe { + let mut addr = SocketAddrBuf::new(); + let owned_fd = ret_owned_fd(c::accept4( + borrowed_fd(sockfd), + addr.storage.as_mut_ptr().cast::<c::sockaddr>(), + &mut addr.len, + flags.bits() as c::c_int, + ))?; + Ok((owned_fd, addr.into_any_option())) + } +} + +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. +#[cfg(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +))] +pub(crate) fn accept_with(sockfd: BorrowedFd<'_>, _flags: SocketFlags) -> io::Result<OwnedFd> { + accept(sockfd) +} + +/// Darwin lacks `accept4`, but does have `accept`. We define `SocketFlags` to +/// have no flags, so we can discard it here. +#[cfg(any( + apple, + windows, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "horizon", + target_os = "nto", + target_os = "redox", + target_os = "vita", +))] +pub(crate) fn acceptfrom_with( + sockfd: BorrowedFd<'_>, + _flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { + acceptfrom(sockfd) +} + +pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { + unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) } +} + +pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> { + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(c::getsockname( + borrowed_fd(sockfd), + addr.storage.as_mut_ptr().cast::<c::sockaddr>(), + &mut addr.len, + ))?; + Ok(addr.into_any()) + } +} + +pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> { + unsafe { + let mut addr = SocketAddrBuf::new(); + ret(c::getpeername( + borrowed_fd(sockfd), + addr.storage.as_mut_ptr().cast::<c::sockaddr>(), + &mut addr.len, + ))?; + Ok(addr.into_any_option()) + } +} + +#[cfg(not(windows))] +pub(crate) fn socketpair( + domain: AddressFamily, + type_: SocketType, + flags: SocketFlags, + protocol: Option<Protocol>, +) -> io::Result<(OwnedFd, OwnedFd)> { + let raw_protocol = match protocol { + Some(p) => p.0.get(), + None => 0, + }; + unsafe { + let mut fds = MaybeUninit::<[OwnedFd; 2]>::uninit(); + ret(c::socketpair( + c::c_int::from(domain.0), + (type_.0 | flags.bits()) as c::c_int, + raw_protocol as c::c_int, + fds.as_mut_ptr().cast::<c::c_int>(), + ))?; + + let [fd0, fd1] = fds.assume_init(); + Ok((fd0, fd1)) + } +} diff --git a/vendor/rustix/src/backend/libc/net/write_sockaddr.rs b/vendor/rustix/src/backend/libc/net/write_sockaddr.rs new file mode 100644 index 00000000..08f04646 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/write_sockaddr.rs @@ -0,0 +1,72 @@ +//! The BSD sockets API requires us to read the `sa_family` field before we can +//! interpret the rest of a `sockaddr` produced by the kernel. + +use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new}; +use crate::backend::c; +use crate::net::{SocketAddrV4, SocketAddrV6}; + +pub(crate) fn encode_sockaddr_v4(v4: &SocketAddrV4) -> c::sockaddr_in { + c::sockaddr_in { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita", + ))] + sin_len: core::mem::size_of::<c::sockaddr_in>() as _, + sin_family: c::AF_INET as _, + sin_port: u16::to_be(v4.port()), + sin_addr: in_addr_new(u32::from_ne_bytes(v4.ip().octets())), + #[cfg(not(any(target_os = "haiku", target_os = "vita")))] + sin_zero: [0; 8_usize], + #[cfg(target_os = "haiku")] + sin_zero: [0; 24_usize], + #[cfg(target_os = "vita")] + sin_zero: [0; 6_usize], + #[cfg(target_os = "vita")] + sin_vport: 0, + } +} + +pub(crate) fn encode_sockaddr_v6(v6: &SocketAddrV6) -> c::sockaddr_in6 { + #[cfg(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + ))] + { + sockaddr_in6_new( + core::mem::size_of::<c::sockaddr_in6>() as _, + c::AF_INET6 as _, + u16::to_be(v6.port()), + u32::to_be(v6.flowinfo()), + in6_addr_new(v6.ip().octets()), + v6.scope_id(), + ) + } + #[cfg(not(any( + bsd, + target_os = "aix", + target_os = "espidf", + target_os = "haiku", + target_os = "hurd", + target_os = "nto", + target_os = "vita" + )))] + { + sockaddr_in6_new( + c::AF_INET6 as _, + u16::to_be(v6.port()), + u32::to_be(v6.flowinfo()), + in6_addr_new(v6.ip().octets()), + v6.scope_id(), + ) + } +} |
