diff options
Diffstat (limited to 'vendor/rustix/src/backend/libc/net')
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/addr.rs | 245 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/ext.rs | 135 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/mod.rs | 16 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/msghdr.rs | 134 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/read_sockaddr.rs | 306 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/send_recv.rs | 103 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/sockopt.rs | 1065 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/syscalls.rs | 568 | ||||
| -rw-r--r-- | vendor/rustix/src/backend/libc/net/write_sockaddr.rs | 103 | 
9 files changed, 2675 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 0000000..719a549 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/addr.rs @@ -0,0 +1,245 @@ +//! Socket address utilities. + +use crate::backend::c; +#[cfg(unix)] +use { +    crate::ffi::CStr, +    crate::io, +    crate::path, +    core::cmp::Ordering, +    core::fmt, +    core::hash::{Hash, Hasher}, +    core::slice, +}; + +/// `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 bytes = path.to_bytes_with_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, +        }) +    } + +    fn init() -> c::sockaddr_un { +        c::sockaddr_un { +            #[cfg(any(bsd, target_os = "aix", target_os = "haiku", target_os = "nto"))] +            sun_len: 0, +            #[cfg(target_os = "vita")] +            ss_len: 0, +            sun_family: c::AF_UNIX as _, +            #[cfg(any(bsd, target_os = "nto"))] +            sun_path: [0; 104], +            #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", 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] +    pub fn path(&self) -> Option<&CStr> { +        let len = self.len(); +        if len != 0 && self.unix.sun_path[0] != 0 { +            let end = len as usize - offsetof_sun_path(); +            let bytes = &self.unix.sun_path[..end]; +            // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. +            // And `from_bytes_with_nul_unchecked` since the string is +            // NUL-terminated. +            unsafe { +                Some(CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts( +                    bytes.as_ptr().cast(), +                    bytes.len(), +                ))) +            } +        } else { +            None +        } +    } + +    /// For an abstract address, return the identifier. +    #[cfg(linux_kernel)] +    #[inline] +    pub fn abstract_name(&self) -> Option<&[u8]> { +        let len = self.len(); +        if len != 0 && self.unix.sun_path[0] == 0 { +            let end = len as usize - offsetof_sun_path(); +            let bytes = &self.unix.sun_path[1..end]; +            // SAFETY: `from_raw_parts` to convert from `&[c_char]` to `&[u8]`. +            unsafe { Some(slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len())) } +        } else { +            None +        } +    } + +    #[inline] +    pub(crate) fn addr_len(&self) -> c::socklen_t { +        #[cfg(not(any(bsd, target_os = "haiku")))] +        { +            self.len +        } +        #[cfg(any(bsd, target_os = "haiku"))] +        { +            c::socklen_t::from(self.unix.sun_len) +        } +    } + +    #[inline] +    pub(crate) fn len(&self) -> usize { +        self.addr_len() as usize +    } +} + +#[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, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { +        if let Some(path) = self.path() { +            path.fmt(fmt) +        } else { +            #[cfg(linux_kernel)] +            if let Some(name) = self.abstract_name() { +                return name.fmt(fmt); +            } + +            "(unnamed)".fmt(fmt) +        } +    } +} + +/// `struct sockaddr_storage` as a raw struct. +pub type SocketAddrStorage = c::sockaddr_storage; + +/// 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 = "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 = "nto", +            target_os = "vita" +        ))] +        sun_family: 0_u8, +        #[cfg(not(any( +            bsd, +            target_os = "aix", +            target_os = "espidf", +            target_os = "haiku", +            target_os = "nto", +            target_os = "vita" +        )))] +        sun_family: 0_u16, +        #[cfg(any(bsd, target_os = "nto"))] +        sun_path: [0; 104], +        #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", 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 0000000..2e11c05 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/ext.rs @@ -0,0 +1,135 @@ +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 = "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 = "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 0000000..d7ab68d --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/mod.rs @@ -0,0 +1,16 @@ +pub(crate) mod addr; +pub(crate) mod ext; +#[cfg(not(any( +    windows, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) mod msghdr; +pub(crate) mod read_sockaddr; +pub(crate) mod send_recv; +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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 0000000..dd9b156 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/msghdr.rs @@ -0,0 +1,134 @@ +//! 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::backend::conv::{msg_control_len, msg_iov_len}; +use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}; + +use crate::io::{self, IoSlice, IoSliceMut}; +use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6}; +use crate::utils::as_ptr; + +use core::mem::{size_of, zeroed, MaybeUninit}; + +/// Create a message header intended to receive a datagram. +pub(crate) fn with_recv_msghdr<R>( +    name: &mut MaybeUninit<c::sockaddr_storage>, +    iov: &mut [IoSliceMut<'_>], +    control: &mut RecvAncillaryBuffer<'_>, +    f: impl FnOnce(&mut c::msghdr) -> io::Result<R>, +) -> io::Result<R> { +    control.clear(); + +    let namelen = size_of::<c::sockaddr_storage>() as c::socklen_t; +    let mut msghdr = { +        let mut h = zero_msghdr(); +        h.msg_name = name.as_mut_ptr().cast(); +        h.msg_namelen = namelen; +        h.msg_iov = iov.as_mut_ptr().cast(); +        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 +    }; + +    let res = f(&mut msghdr); + +    // Reset the control length. +    if res.is_ok() { +        unsafe { +            control.set_control_len(msghdr.msg_controllen.try_into().unwrap_or(usize::MAX)); +        } +    } + +    res +} + +/// Create a message header intended to send without an address. +pub(crate) fn with_noaddr_msghdr<R>( +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    f: impl FnOnce(c::msghdr) -> R, +) -> R { +    f({ +        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 an IPv4 address. +pub(crate) fn with_v4_msghdr<R>( +    addr: &SocketAddrV4, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    f: impl FnOnce(c::msghdr) -> R, +) -> R { +    let encoded = encode_sockaddr_v4(addr); + +    f({ +        let mut h = zero_msghdr(); +        h.msg_name = as_ptr(&encoded) as _; +        h.msg_namelen = size_of::<SocketAddrV4>() as _; +        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 an IPv6 address. +pub(crate) fn with_v6_msghdr<R>( +    addr: &SocketAddrV6, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    f: impl FnOnce(c::msghdr) -> R, +) -> R { +    let encoded = encode_sockaddr_v6(addr); + +    f({ +        let mut h = zero_msghdr(); +        h.msg_name = as_ptr(&encoded) as _; +        h.msg_namelen = size_of::<SocketAddrV6>() as _; +        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 a Unix address. +#[cfg(all(unix, not(target_os = "redox")))] +pub(crate) fn with_unix_msghdr<R>( +    addr: &crate::net::SocketAddrUnix, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    f: impl FnOnce(c::msghdr) -> R, +) -> R { +    f({ +        let mut h = zero_msghdr(); +        h.msg_name = as_ptr(&addr.unix) as _; +        h.msg_namelen = addr.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()); +        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/read_sockaddr.rs b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs new file mode 100644 index 0000000..6da7a50 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/read_sockaddr.rs @@ -0,0 +1,306 @@ +//! The BSD sockets API requires us to read the `ss_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; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use core::mem::size_of; + +// This must match the header of `sockaddr`. +#[repr(C)] +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" +    ))] +    ss_family: u8, +    #[cfg(not(any( +        bsd, +        target_os = "aix", +        target_os = "espidf", +        target_os = "haiku", +        target_os = "nto", +        target_os = "vita" +    )))] +    ss_family: u16, +} + +/// Read the `ss_family` field from a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +#[inline] +unsafe fn read_ss_family(storage: *const c::sockaddr_storage) -> 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 = "nto", +            target_os = "vita" +        ))] +        sa_len: 0_u8, +        #[cfg(any( +            bsd, +            target_os = "aix", +            target_os = "espidf", +            target_os = "haiku", +            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 = "nto", +            target_os = "vita" +        )))] +        sa_family: 0_u16, +        #[cfg(not(target_os = "haiku"))] +        sa_data: [0; 14], +        #[cfg(target_os = "haiku")] +        sa_data: [0; 30], +    }; + +    (*storage.cast::<sockaddr_header>()).ss_family.into() +} + +/// Set the `ss_family` field of a socket address to `AF_UNSPEC`, so that we +/// can test for `AF_UNSPEC` to test whether it was stored to. +pub(crate) unsafe fn initialize_family_to_unspec(storage: *mut c::sockaddr_storage) { +    (*storage.cast::<sockaddr_header>()).ss_family = c::AF_UNSPEC as _; +} + +/// Read a socket address encoded in a platform-specific format. +/// +/// # Safety +/// +/// `storage` must point to valid socket address storage. +pub(crate) unsafe fn read_sockaddr( +    storage: *const c::sockaddr_storage, +    len: usize, +) -> io::Result<SocketAddrAny> { +    #[cfg(unix)] +    let offsetof_sun_path = super::addr::offsetof_sun_path(); + +    if len < size_of::<c::sa_family_t>() { +        return Err(io::Errno::INVAL); +    } +    match read_ss_family(storage).into() { +        c::AF_INET => { +            if len < size_of::<c::sockaddr_in>() { +                return Err(io::Errno::INVAL); +            } +            let decode = &*storage.cast::<c::sockaddr_in>(); +            Ok(SocketAddrAny::V4(SocketAddrV4::new( +                Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), +                u16::from_be(decode.sin_port), +            ))) +        } +        c::AF_INET6 => { +            if len < size_of::<c::sockaddr_in6>() { +                return Err(io::Errno::INVAL); +            } +            let decode = &*storage.cast::<c::sockaddr_in6>(); +            #[cfg(not(windows))] +            let s6_addr = decode.sin6_addr.s6_addr; +            #[cfg(windows)] +            let s6_addr = decode.sin6_addr.u.Byte; +            #[cfg(not(windows))] +            let sin6_scope_id = decode.sin6_scope_id; +            #[cfg(windows)] +            let sin6_scope_id = decode.Anonymous.sin6_scope_id; +            Ok(SocketAddrAny::V6(SocketAddrV6::new( +                Ipv6Addr::from(s6_addr), +                u16::from_be(decode.sin6_port), +                u32::from_be(decode.sin6_flowinfo), +                sin6_scope_id, +            ))) +        } +        #[cfg(unix)] +        c::AF_UNIX => { +            if len < offsetof_sun_path { +                return Err(io::Errno::INVAL); +            } +            if len == offsetof_sun_path { +                SocketAddrUnix::new(&[][..]).map(SocketAddrAny::Unix) +            } else { +                let decode = &*storage.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 { +                    return SocketAddrUnix::new_abstract_name(core::mem::transmute::< +                        &[c::c_char], +                        &[u8], +                    >( +                        &decode.sun_path[1..len - offsetof_sun_path], +                    )) +                    .map(SocketAddrAny::Unix); +                } + +                // 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(io::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(io::Errno::INVAL); +                    } +                    debug_assert_eq!( +                        CStr::from_ptr(decode.sun_path.as_ptr()).to_bytes().len(), +                        provided_len +                    ); +                    &decode.sun_path[..provided_len] +                }; + +                SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes)) +                    .map(SocketAddrAny::Unix) +            } +        } +        _ => Err(io::Errno::INVAL), +    } +} + +/// Read an optional socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +pub(crate) unsafe fn maybe_read_sockaddr_os( +    storage: *const c::sockaddr_storage, +    len: usize, +) -> Option<SocketAddrAny> { +    if len == 0 { +        return None; +    } + +    assert!(len >= size_of::<c::sa_family_t>()); +    let family = read_ss_family(storage).into(); +    if family == c::AF_UNSPEC { +        None +    } else { +        Some(inner_read_sockaddr_os(family, storage, len)) +    } +} + +/// Read a socket address returned from the OS. +/// +/// # Safety +/// +/// `storage` must point to a valid socket address returned from the OS. +pub(crate) unsafe fn read_sockaddr_os( +    storage: *const c::sockaddr_storage, +    len: usize, +) -> SocketAddrAny { +    assert!(len >= size_of::<c::sa_family_t>()); +    let family = read_ss_family(storage).into(); +    inner_read_sockaddr_os(family, storage, len) +} + +unsafe fn inner_read_sockaddr_os( +    family: c::c_int, +    storage: *const c::sockaddr_storage, +    len: usize, +) -> SocketAddrAny { +    #[cfg(unix)] +    let offsetof_sun_path = super::addr::offsetof_sun_path(); + +    assert!(len >= size_of::<c::sa_family_t>()); +    match family { +        c::AF_INET => { +            assert!(len >= size_of::<c::sockaddr_in>()); +            let decode = &*storage.cast::<c::sockaddr_in>(); +            SocketAddrAny::V4(SocketAddrV4::new( +                Ipv4Addr::from(u32::from_be(in_addr_s_addr(decode.sin_addr))), +                u16::from_be(decode.sin_port), +            )) +        } +        c::AF_INET6 => { +            assert!(len >= size_of::<c::sockaddr_in6>()); +            let decode = &*storage.cast::<c::sockaddr_in6>(); +            SocketAddrAny::V6(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)] +        c::AF_UNIX => { +            assert!(len >= offsetof_sun_path); +            if len == offsetof_sun_path { +                SocketAddrAny::Unix(SocketAddrUnix::new(&[][..]).unwrap()) +            } else { +                let decode = &*storage.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 { +                    return SocketAddrAny::Unix( +                        SocketAddrUnix::new_abstract_name(core::mem::transmute::< +                            &[c::c_char], +                            &[u8], +                        >( +                            &decode.sun_path[1..len - offsetof_sun_path], +                        )) +                        .unwrap(), +                    ); +                } + +                // Otherwise we expect a NUL-terminated filesystem path. +                assert_eq!(decode.sun_path[len - 1 - offsetof_sun_path], 0); +                let path_bytes = &decode.sun_path[..len - 1 - offsetof_sun_path]; + +                // 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. +                #[cfg(any(solarish, target_os = "freebsd"))] +                let path_bytes = &path_bytes[..path_bytes.iter().position(|b| *b == 0).unwrap()]; + +                SocketAddrAny::Unix( +                    SocketAddrUnix::new(core::mem::transmute::<&[c::c_char], &[u8]>(path_bytes)) +                        .unwrap(), +                ) +            } +        } +        other => unimplemented!("{:?}", other), +    } +} 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 0000000..5dc60dd --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/send_recv.rs @@ -0,0 +1,103 @@ +use crate::backend::c; +use bitflags::bitflags; + +bitflags! { +    /// `MSG_*` flags for use with [`send`], [`send_to`], 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 = "espidf", +            target_os = "nto", +            target_os = "haiku", +            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(windows))] +        const EOT = bitcast!(c::MSG_EOR); +        /// `MSG_MORE` +        #[cfg(not(any( +            bsd, +            solarish, +            windows, +            target_os = "aix", +            target_os = "haiku", +            target_os = "nto", +            target_os = "vita", +        )))] +        const MORE = bitcast!(c::MSG_MORE); +        #[cfg(not(any(apple, windows, 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 { +        #[cfg(not(any( +            apple, +            solarish, +            windows, +            target_os = "aix", +            target_os = "espidf", +            target_os = "haiku", +            target_os = "nto", +            target_os = "vita", +        )))] +        /// `MSG_CMSG_CLOEXEC` +        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 = "espidf", +            target_os = "haiku", +            target_os = "nto", +            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` +        const TRUNC = bitcast!(c::MSG_TRUNC); +        /// `MSG_WAITALL` +        const WAITALL = bitcast!(c::MSG_WAITALL); + +        /// <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 0000000..cff2ca2 --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/sockopt.rs @@ -0,0 +1,1065 @@ +//! 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(not(any( +    apple, +    windows, +    target_os = "aix", +    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; +use crate::net::{Ipv4Addr, Ipv6Addr, SocketType}; +#[cfg(any(linux_kernel, target_os = "fuchsia"))] +use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrV4}; +#[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; +#[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 = "openbsd", target_os = "haiku", target_os = "nto")))] +use c::TCP_KEEPIDLE; +use core::mem::{size_of, MaybeUninit}; +use core::time::Duration; +#[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 = core::mem::size_of::<T>().try_into().unwrap(); +    debug_assert!( +        optlen as usize >= core::mem::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 = core::mem::size_of::<T>().try_into().unwrap(); +    debug_assert!( +        optlen as usize >= core::mem::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 get_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 get_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 get_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 get_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 get_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 get_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 get_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 get_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 get_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) +} + +#[inline] +pub(crate) fn get_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) +} + +#[inline] +pub(crate) fn get_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 = "dragonfly", +    target_os = "emscripten", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "netbsd", +    target_os = "nto", +    target_os = "vita", +)))] +pub(crate) fn get_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 get_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 get_socket_oobinline(fd: BorrowedFd<'_>) -> io::Result<bool> { +    getsockopt(fd, c::SOL_SOCKET, c::SO_OOBINLINE).map(to_bool) +} + +#[cfg(not(any(solarish, windows)))] +#[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)))] +#[inline] +pub(crate) fn get_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 get_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 get_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 get_socket_cookie(fd: BorrowedFd<'_>) -> io::Result<u64> { +    getsockopt(fd, c::SOL_SOCKET, c::SO_COOKIE) +} + +#[cfg(target_os = "linux")] +#[inline] +pub(crate) fn get_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 get_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 get_ipv6_v6only(fd: BorrowedFd<'_>) -> io::Result<bool> { +    getsockopt(fd, c::IPPROTO_IPV6, c::IPV6_V6ONLY).map(to_bool) +} + +#[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 get_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 get_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 get_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 get_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: i32, +) -> io::Result<()> { +    let mreqn = to_ip_mreqn(multiaddr, address, ifindex); +    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: i32, +) -> io::Result<()> { +    let mreqn = to_ip_mreqn(multiaddr, address, ifindex); +    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 get_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 get_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 = "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 = "freebsd", target_os = "fuchsia"))] +#[inline] +pub(crate) fn get_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 get_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 get_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 get_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 get_ip_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV4> { +    let level = c::IPPROTO_IP; +    let optname = c::SO_ORIGINAL_DST; +    let mut value = MaybeUninit::<SocketAddrStorage>::uninit(); +    let mut optlen = core::mem::size_of_val(&value).try_into().unwrap(); + +    getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + +    let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? }; +    match any { +        SocketAddrAny::V4(v4) => Ok(v4), +        _ => unreachable!(), +    } +} + +#[cfg(linux_kernel)] +#[inline] +pub(crate) fn get_ipv6_original_dst(fd: BorrowedFd<'_>) -> io::Result<SocketAddrV6> { +    let level = c::IPPROTO_IPV6; +    let optname = c::IP6T_SO_ORIGINAL_DST; +    let mut value = MaybeUninit::<SocketAddrStorage>::uninit(); +    let mut optlen = core::mem::size_of_val(&value).try_into().unwrap(); + +    getsockopt_raw(fd, level, optname, &mut value, &mut optlen)?; + +    let any = unsafe { SocketAddrAny::read(value.as_ptr(), optlen as usize)? }; +    match any { +        SocketAddrAny::V6(v6) => Ok(v6), +        _ => unreachable!(), +    } +} + +#[cfg(not(any( +    solarish, +    windows, +    target_os = "espidf", +    target_os = "haiku", +    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 = "vita" +)))] +#[inline] +pub(crate) fn get_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 get_tcp_nodelay(fd: BorrowedFd<'_>) -> io::Result<bool> { +    getsockopt(fd, c::IPPROTO_TCP, c::TCP_NODELAY).map(to_bool) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +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 = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_tcp_keepcnt(fd: BorrowedFd<'_>) -> io::Result<u32> { +    getsockopt(fd, c::IPPROTO_TCP, c::TCP_KEEPCNT) +} + +#[inline] +#[cfg(not(any(target_os = "openbsd", target_os = "haiku", target_os = "nto")))] +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 = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_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 = "openbsd", target_os = "haiku", target_os = "nto")))] +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 = "openbsd", target_os = "haiku", target_os = "nto")))] +pub(crate) fn get_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 get_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 get_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 get_tcp_congestion(fd: BorrowedFd<'_>) -> io::Result<String> { +    let level = c::IPPROTO_TCP; +    let optname = c::TCP_CONGESTION; +    const OPTLEN: c::socklen_t = 16; +    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.iter().any(|b| *b == 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 get_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 get_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 get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> { +    getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED) +} + +#[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(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 0000000..48dbf1f --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/syscalls.rs @@ -0,0 +1,568 @@ +//! libc syscalls supporting `rustix::net`. + +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use crate::backend::c; +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::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use crate::utils::as_ptr; +use core::mem::{size_of, MaybeUninit}; +#[cfg(not(any( +    windows, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +use { +    super::msghdr::{with_noaddr_msghdr, with_recv_msghdr, with_v4_msghdr, with_v6_msghdr}, +    crate::io::{IoSlice, IoSliceMut}, +    crate::net::{RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer}, +}; +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +use { +    super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os}, +    super::send_recv::{RecvFlags, SendFlags}, +    super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6}, +    crate::net::{AddressFamily, Protocol, Shutdown, SocketFlags, SocketType}, +    core::ptr::null_mut, +}; + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) unsafe fn recv( +    fd: BorrowedFd<'_>, +    buf: *mut u8, +    len: usize, +    flags: RecvFlags, +) -> io::Result<usize> { +    ret_send_recv(c::recv( +        borrowed_fd(fd), +        buf.cast(), +        send_recv_len(len), +        bitflags_bits!(flags), +    )) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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), +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) unsafe fn recvfrom( +    fd: BorrowedFd<'_>, +    buf: *mut u8, +    buf_len: usize, +    flags: RecvFlags, +) -> io::Result<(usize, Option<SocketAddrAny>)> { +    let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); +    let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; + +    // `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(storage.as_mut_ptr()); + +    ret_send_recv(c::recvfrom( +        borrowed_fd(fd), +        buf.cast(), +        send_recv_len(buf_len), +        bitflags_bits!(flags), +        storage.as_mut_ptr().cast(), +        &mut len, +    )) +    .map(|nread| { +        ( +            nread, +            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), +        ) +    }) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_v4( +    fd: BorrowedFd<'_>, +    buf: &[u8], +    flags: SendFlags, +    addr: &SocketAddrV4, +) -> io::Result<usize> { +    unsafe { +        ret_send_recv(c::sendto( +            borrowed_fd(fd), +            buf.as_ptr().cast(), +            send_recv_len(buf.len()), +            bitflags_bits!(flags), +            as_ptr(&encode_sockaddr_v4(addr)).cast::<c::sockaddr>(), +            size_of::<c::sockaddr_in>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_v6( +    fd: BorrowedFd<'_>, +    buf: &[u8], +    flags: SendFlags, +    addr: &SocketAddrV6, +) -> io::Result<usize> { +    unsafe { +        ret_send_recv(c::sendto( +            borrowed_fd(fd), +            buf.as_ptr().cast(), +            send_recv_len(buf.len()), +            bitflags_bits!(flags), +            as_ptr(&encode_sockaddr_v6(addr)).cast::<c::sockaddr>(), +            size_of::<c::sockaddr_in6>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn sendto_unix( +    fd: BorrowedFd<'_>, +    buf: &[u8], +    flags: SendFlags, +    addr: &SocketAddrUnix, +) -> io::Result<usize> { +    unsafe { +        ret_send_recv(c::sendto( +            borrowed_fd(fd), +            buf.as_ptr().cast(), +            send_recv_len(buf.len()), +            bitflags_bits!(flags), +            as_ptr(&addr.unix).cast(), +            addr.addr_len(), +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { +    unsafe { +        ret(c::bind( +            borrowed_fd(sockfd), +            as_ptr(&encode_sockaddr_v4(addr)).cast(), +            size_of::<c::sockaddr_in>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { +    unsafe { +        ret(c::bind( +            borrowed_fd(sockfd), +            as_ptr(&encode_sockaddr_v6(addr)).cast(), +            size_of::<c::sockaddr_in6>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { +    unsafe { +        ret(c::bind( +            borrowed_fd(sockfd), +            as_ptr(&addr.unix).cast(), +            addr.addr_len(), +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> { +    unsafe { +        ret(c::connect( +            borrowed_fd(sockfd), +            as_ptr(&encode_sockaddr_v4(addr)).cast(), +            size_of::<c::sockaddr_in>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_v6(sockfd: BorrowedFd<'_>, addr: &SocketAddrV6) -> io::Result<()> { +    unsafe { +        ret(c::connect( +            borrowed_fd(sockfd), +            as_ptr(&encode_sockaddr_v6(addr)).cast(), +            size_of::<c::sockaddr_in6>() as c::socklen_t, +        )) +    } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +pub(crate) fn connect_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Result<()> { +    unsafe { +        ret(c::connect( +            borrowed_fd(sockfd), +            as_ptr(&addr.unix).cast(), +            addr.addr_len(), +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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, +        )) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn listen(sockfd: BorrowedFd<'_>, backlog: c::c_int) -> io::Result<()> { +    unsafe { ret(c::listen(borrowed_fd(sockfd), backlog)) } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +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 = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn recvmsg( +    sockfd: BorrowedFd<'_>, +    iov: &mut [IoSliceMut<'_>], +    control: &mut RecvAncillaryBuffer<'_>, +    msg_flags: RecvFlags, +) -> io::Result<RecvMsgReturn> { +    let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); + +    with_recv_msghdr(&mut storage, iov, control, |msghdr| { +        let result = unsafe { +            ret_send_recv(c::recvmsg( +                borrowed_fd(sockfd), +                msghdr, +                bitflags_bits!(msg_flags), +            )) +        }; + +        result.map(|bytes| { +            // Get the address of the sender, if any. +            let addr = +                unsafe { maybe_read_sockaddr_os(msghdr.msg_name as _, msghdr.msg_namelen as _) }; + +            RecvMsgReturn { +                bytes, +                address: addr, +                flags: RecvFlags::from_bits_retain(bitcast!(msghdr.msg_flags)), +            } +        }) +    }) +} + +#[cfg(not(any( +    windows, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn sendmsg( +    sockfd: BorrowedFd<'_>, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    msg_flags: SendFlags, +) -> io::Result<usize> { +    with_noaddr_msghdr(iov, control, |msghdr| unsafe { +        ret_send_recv(c::sendmsg( +            borrowed_fd(sockfd), +            &msghdr, +            bitflags_bits!(msg_flags), +        )) +    }) +} + +#[cfg(not(any( +    windows, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn sendmsg_v4( +    sockfd: BorrowedFd<'_>, +    addr: &SocketAddrV4, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    msg_flags: SendFlags, +) -> io::Result<usize> { +    with_v4_msghdr(addr, iov, control, |msghdr| unsafe { +        ret_send_recv(c::sendmsg( +            borrowed_fd(sockfd), +            &msghdr, +            bitflags_bits!(msg_flags), +        )) +    }) +} + +#[cfg(not(any( +    windows, +    target_os = "espidf", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi" +)))] +pub(crate) fn sendmsg_v6( +    sockfd: BorrowedFd<'_>, +    addr: &SocketAddrV6, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    msg_flags: SendFlags, +) -> io::Result<usize> { +    with_v6_msghdr(addr, iov, control, |msghdr| unsafe { +        ret_send_recv(c::sendmsg( +            borrowed_fd(sockfd), +            &msghdr, +            bitflags_bits!(msg_flags), +        )) +    }) +} + +#[cfg(all( +    unix, +    not(any(target_os = "espidf", target_os = "redox", target_os = "vita")) +))] +pub(crate) fn sendmsg_unix( +    sockfd: BorrowedFd<'_>, +    addr: &SocketAddrUnix, +    iov: &[IoSlice<'_>], +    control: &mut SendAncillaryBuffer<'_, '_, '_>, +    msg_flags: SendFlags, +) -> io::Result<usize> { +    super::msghdr::with_unix_msghdr(addr, iov, control, |msghdr| unsafe { +        ret_send_recv(c::sendmsg( +            borrowed_fd(sockfd), +            &msghdr, +            bitflags_bits!(msg_flags), +        )) +    }) +} + +#[cfg(not(any( +    apple, +    windows, +    target_os = "aix", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "redox", +    target_os = "nto", +    target_os = "vita", +    target_os = "wasi", +)))] +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) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn acceptfrom(sockfd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { +    unsafe { +        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); +        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; +        let owned_fd = ret_owned_fd(c::accept( +            borrowed_fd(sockfd), +            storage.as_mut_ptr().cast(), +            &mut len, +        ))?; +        Ok(( +            owned_fd, +            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), +        )) +    } +} + +#[cfg(not(any( +    apple, +    windows, +    target_os = "aix", +    target_os = "espidf", +    target_os = "haiku", +    target_os = "nto", +    target_os = "redox", +    target_os = "vita", +    target_os = "wasi", +)))] +pub(crate) fn acceptfrom_with( +    sockfd: BorrowedFd<'_>, +    flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { +    unsafe { +        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); +        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; +        let owned_fd = ret_owned_fd(c::accept4( +            borrowed_fd(sockfd), +            storage.as_mut_ptr().cast(), +            &mut len, +            flags.bits() as c::c_int, +        ))?; +        Ok(( +            owned_fd, +            maybe_read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap()), +        )) +    } +} + +/// 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 = "nto", +    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 = "nto", +    target_os = "vita", +))] +pub(crate) fn acceptfrom_with( +    sockfd: BorrowedFd<'_>, +    _flags: SocketFlags, +) -> io::Result<(OwnedFd, Option<SocketAddrAny>)> { +    acceptfrom(sockfd) +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn shutdown(sockfd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> { +    unsafe { ret(c::shutdown(borrowed_fd(sockfd), how as c::c_int)) } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn getsockname(sockfd: BorrowedFd<'_>) -> io::Result<SocketAddrAny> { +    unsafe { +        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); +        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; +        ret(c::getsockname( +            borrowed_fd(sockfd), +            storage.as_mut_ptr().cast(), +            &mut len, +        ))?; +        Ok(read_sockaddr_os(storage.as_ptr(), len.try_into().unwrap())) +    } +} + +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +pub(crate) fn getpeername(sockfd: BorrowedFd<'_>) -> io::Result<Option<SocketAddrAny>> { +    unsafe { +        let mut storage = MaybeUninit::<c::sockaddr_storage>::uninit(); +        let mut len = size_of::<c::sockaddr_storage>() as c::socklen_t; +        ret(c::getpeername( +            borrowed_fd(sockfd), +            storage.as_mut_ptr().cast(), +            &mut len, +        ))?; +        Ok(maybe_read_sockaddr_os( +            storage.as_ptr(), +            len.try_into().unwrap(), +        )) +    } +} + +#[cfg(not(any(windows, target_os = "redox", target_os = "wasi")))] +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 0000000..2eee98c --- /dev/null +++ b/vendor/rustix/src/backend/libc/net/write_sockaddr.rs @@ -0,0 +1,103 @@ +//! The BSD sockets API requires us to read the `ss_family` field before we can +//! interpret the rest of a `sockaddr` produced by the kernel. + +use super::addr::SocketAddrStorage; +#[cfg(unix)] +use super::addr::SocketAddrUnix; +use super::ext::{in6_addr_new, in_addr_new, sockaddr_in6_new}; +use crate::backend::c; +use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6}; +use core::mem::size_of; + +pub(crate) unsafe fn write_sockaddr( +    addr: &SocketAddrAny, +    storage: *mut SocketAddrStorage, +) -> usize { +    match addr { +        SocketAddrAny::V4(v4) => write_sockaddr_v4(v4, storage), +        SocketAddrAny::V6(v6) => write_sockaddr_v6(v6, storage), +        #[cfg(unix)] +        SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage), +    } +} + +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 = "nto", +            target_os = "vita", +        ))] +        sin_len: 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, +    } +} + +unsafe fn write_sockaddr_v4(v4: &SocketAddrV4, storage: *mut SocketAddrStorage) -> usize { +    let encoded = encode_sockaddr_v4(v4); +    core::ptr::write(storage.cast(), encoded); +    size_of::<c::sockaddr_in>() +} + +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 = "nto", +        target_os = "vita" +    ))] +    { +        sockaddr_in6_new( +            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 = "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(), +        ) +    } +} + +unsafe fn write_sockaddr_v6(v6: &SocketAddrV6, storage: *mut SocketAddrStorage) -> usize { +    let encoded = encode_sockaddr_v6(v6); +    core::ptr::write(storage.cast(), encoded); +    size_of::<c::sockaddr_in6>() +} + +#[cfg(unix)] +unsafe fn write_sockaddr_unix(unix: &SocketAddrUnix, storage: *mut SocketAddrStorage) -> usize { +    core::ptr::write(storage.cast(), unix.unix); +    unix.len() +}  | 
