diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3a1b10f4..a3b98b6c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -77,6 +77,7 @@ jobs: - i686-unknown-hurd-gnu - i686-unknown-linux-gnu - sparcv9-sun-solaris + - wasm32-wasip2 - x86_64-apple-darwin - x86_64-apple-ios - x86_64-pc-solaris diff --git a/Cargo.toml b/Cargo.toml index 8ef85ebc..a9228f76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,8 @@ targets = ["aarch64-apple-ios", "aarch64-linux-android", "x86_64-apple-darwin", [package.metadata.playground] features = ["all"] -[target."cfg(unix)".dependencies] -libc = "0.2.150" +[target.'cfg(any(unix, target_os = "wasi"))'.dependencies] +libc = "0.2.162" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52" diff --git a/README.md b/README.md index 8bb09495..0024fcf0 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ feature flag. # Minimum Supported Rust Version (MSRV) -Socket2 uses 1.63.0 as MSRV. +Socket2 uses 1.63.0 (1.66.0 for wasm32-wasip2) as MSRV. # License diff --git a/src/lib.rs b/src/lib.rs index 8f593163..e42ff20b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,15 +58,18 @@ // Disallow warnings in examples. #![doc(test(attr(deny(warnings))))] +#[cfg(not(target_os = "wasi"))] use std::fmt; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::io::IoSlice; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::marker::PhantomData; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::mem; +#[cfg(not(target_os = "wasi"))] use std::mem::MaybeUninit; use std::net::SocketAddr; +#[cfg(not(target_os = "wasi"))] use std::ops::{Deref, DerefMut}; use std::time::Duration; @@ -107,7 +110,7 @@ macro_rules! from { ($from: ty, $for: ty) => { impl From<$from> for $for { fn from(socket: $from) -> $for { - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] unsafe { <$for>::from_raw_fd(socket.into_raw_fd()) } @@ -176,9 +179,10 @@ mod sockref; #[cfg_attr(unix, path = "sys/unix.rs")] #[cfg_attr(windows, path = "sys/windows.rs")] +#[cfg_attr(target_os = "wasi", path = "sys/wasi.rs")] mod sys; -#[cfg(not(any(windows, unix)))] +#[cfg(not(any(windows, unix, all(target_os = "wasi", target_env = "p2"))))] compile_error!("Socket2 doesn't support the compile target"); use sys::c_int; @@ -216,6 +220,7 @@ impl Domain { pub const IPV6: Domain = Domain(sys::AF_INET6); /// Domain for Unix socket communication, corresponding to `AF_UNIX`. + #[cfg(not(target_os = "wasi"))] pub const UNIX: Domain = Domain(sys::AF_UNIX); /// Returns the correct domain for `address`. @@ -270,15 +275,24 @@ impl Type { pub const DCCP: Type = Type(sys::SOCK_DCCP); /// Type corresponding to `SOCK_SEQPACKET`. - #[cfg(all(feature = "all", not(target_os = "espidf")))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "all", not(target_os = "espidf")))))] + #[cfg(all(feature = "all", not(any(target_os = "espidf", target_os = "wasi"))))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "all", not(any(target_os = "espidf", target_os = "wasi"))))) + )] pub const SEQPACKET: Type = Type(sys::SOCK_SEQPACKET); /// Type corresponding to `SOCK_RAW`. - #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] + #[cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] pub const RAW: Type = Type(sys::SOCK_RAW); } @@ -306,18 +320,20 @@ impl From for c_int { pub struct Protocol(c_int); impl Protocol { - /// Protocol corresponding to `ICMPv4`. - pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP); - - /// Protocol corresponding to `ICMPv6`. - pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6); - /// Protocol corresponding to `TCP`. pub const TCP: Protocol = Protocol(sys::IPPROTO_TCP); /// Protocol corresponding to `UDP`. pub const UDP: Protocol = Protocol(sys::IPPROTO_UDP); + #[cfg(not(target_os = "wasi"))] + /// Protocol corresponding to `ICMPv4`. + pub const ICMPV4: Protocol = Protocol(sys::IPPROTO_ICMP); + + #[cfg(not(target_os = "wasi"))] + /// Protocol corresponding to `ICMPv6`. + pub const ICMPV6: Protocol = Protocol(sys::IPPROTO_ICMPV6); + #[cfg(target_os = "linux")] /// Protocol corresponding to `MPTCP`. pub const MPTCP: Protocol = Protocol(sys::IPPROTO_MPTCP); @@ -363,12 +379,12 @@ impl From for c_int { /// Flags for incoming messages. /// /// Flags provide additional information about incoming messages. -#[cfg(not(target_os = "redox"))] -#[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] +#[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] #[derive(Copy, Clone, Eq, PartialEq)] pub struct RecvFlags(c_int); -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] impl RecvFlags { /// Check if the message contains a truncated datagram. /// @@ -386,15 +402,18 @@ impl RecvFlags { /// A version of [`IoSliceMut`] that allows the buffer to be uninitialised. /// /// [`IoSliceMut`]: std::io::IoSliceMut +#[cfg(not(target_os = "wasi"))] #[repr(transparent)] pub struct MaybeUninitSlice<'a>(sys::MaybeUninitSlice<'a>); +#[cfg(not(target_os = "wasi"))] impl<'a> fmt::Debug for MaybeUninitSlice<'a> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(self.0.as_slice(), fmt) } } +#[cfg(not(target_os = "wasi"))] impl<'a> MaybeUninitSlice<'a> { /// Creates a new `MaybeUninitSlice` wrapping a byte slice. /// @@ -406,6 +425,7 @@ impl<'a> MaybeUninitSlice<'a> { } } +#[cfg(not(target_os = "wasi"))] impl<'a> Deref for MaybeUninitSlice<'a> { type Target = [MaybeUninit]; @@ -414,6 +434,7 @@ impl<'a> Deref for MaybeUninitSlice<'a> { } } +#[cfg(not(target_os = "wasi"))] impl<'a> DerefMut for MaybeUninitSlice<'a> { fn deref_mut(&mut self) -> &mut [MaybeUninit] { self.0.as_mut_slice() @@ -520,6 +541,7 @@ impl TcpKeepalive { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", target_os = "windows", ))] @@ -537,6 +559,7 @@ impl TcpKeepalive { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", target_os = "windows", ))) @@ -566,6 +589,7 @@ impl TcpKeepalive { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -585,6 +609,7 @@ impl TcpKeepalive { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))) @@ -601,14 +626,14 @@ impl TcpKeepalive { /// /// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdrMut`] /// for the variant used by `recvmsg(2)`. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] pub struct MsgHdr<'addr, 'bufs, 'control> { inner: sys::msghdr, #[allow(clippy::type_complexity)] _lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>, } -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> { /// Create a new `MsgHdr` with all empty/zero fields. #[allow(clippy::new_without_default)] @@ -658,7 +683,7 @@ impl<'addr, 'bufs, 'control> MsgHdr<'addr, 'bufs, 'control> { } } -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { "MsgHdr".fmt(fmt) @@ -669,7 +694,7 @@ impl<'name, 'bufs, 'control> fmt::Debug for MsgHdr<'name, 'bufs, 'control> { /// /// This wraps `msghdr` on Unix and `WSAMSG` on Windows. Also see [`MsgHdr`] for /// the variant used by `sendmsg(2)`. -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] pub struct MsgHdrMut<'addr, 'bufs, 'control> { inner: sys::msghdr, #[allow(clippy::type_complexity)] @@ -680,7 +705,7 @@ pub struct MsgHdrMut<'addr, 'bufs, 'control> { )>, } -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> { /// Create a new `MsgHdrMut` with all empty/zero fields. #[allow(clippy::new_without_default)] @@ -735,7 +760,7 @@ impl<'addr, 'bufs, 'control> MsgHdrMut<'addr, 'bufs, 'control> { } } -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] impl<'name, 'bufs, 'control> fmt::Debug for MsgHdrMut<'name, 'bufs, 'control> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { "MsgHdrMut".fmt(fmt) diff --git a/src/sockaddr.rs b/src/sockaddr.rs index c80dccf3..5d6888c4 100644 --- a/src/sockaddr.rs +++ b/src/sockaddr.rs @@ -1,15 +1,18 @@ use std::hash::Hash; use std::mem::{self, size_of, MaybeUninit}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +#[cfg(not(target_os = "wasi"))] use std::path::Path; use std::{fmt, io, ptr}; #[cfg(windows)] use windows_sys::Win32::Networking::WinSock::SOCKADDR_IN6_0; +#[cfg(not(target_os = "wasi"))] +use crate::sys::AF_UNIX; use crate::sys::{ c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, AF_INET, - AF_INET6, AF_UNIX, + AF_INET6, }; use crate::Domain; @@ -146,6 +149,7 @@ impl SockAddr { /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. /// /// Returns an error if the path is longer than `SUN_LEN`. + #[cfg(not(target_os = "wasi"))] pub fn unix

(path: P) -> io::Result where P: AsRef, @@ -201,6 +205,7 @@ impl SockAddr { /// Returns true if this address is of a unix socket (for local interprocess communication), /// i.e. it is from the `AF_UNIX` family, false otherwise. + #[cfg(not(target_os = "wasi"))] pub fn is_unix(&self) -> bool { self.storage.ss_family == AF_UNIX as sa_family_t } @@ -225,7 +230,7 @@ impl SockAddr { ip, port, addr.sin6_flowinfo, - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] addr.sin6_scope_id, #[cfg(windows)] unsafe { @@ -282,7 +287,10 @@ impl From for SockAddr { storage.sin_family = AF_INET as sa_family_t; storage.sin_port = addr.port().to_be(); storage.sin_addr = crate::sys::to_in_addr(addr.ip()); - storage.sin_zero = Default::default(); + #[cfg(not(target_os = "wasi"))] + { + storage.sin_zero = Default::default(); + } mem::size_of::() as socklen_t }; #[cfg(any( @@ -317,7 +325,7 @@ impl From for SockAddr { storage.sin6_port = addr.port().to_be(); storage.sin6_addr = crate::sys::to_in6_addr(addr.ip()); storage.sin6_flowinfo = addr.flowinfo(); - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] { storage.sin6_scope_id = addr.scope_id(); } @@ -401,6 +409,7 @@ mod tests { let addr = SockAddr::from(std); assert!(addr.is_ipv4()); assert!(!addr.is_ipv6()); + #[cfg(not(target_os = "wasi"))] assert!(!addr.is_unix()); assert_eq!(addr.family(), AF_INET as sa_family_t); assert_eq!(addr.domain(), Domain::IPV4); @@ -429,6 +438,7 @@ mod tests { let addr = SockAddr::from(std); assert!(addr.is_ipv6()); assert!(!addr.is_ipv4()); + #[cfg(not(target_os = "wasi"))] assert!(!addr.is_unix()); assert_eq!(addr.family(), AF_INET6 as sa_family_t); assert_eq!(addr.domain(), Domain::IPV6); diff --git a/src/socket.rs b/src/socket.rs index 3d9d44af..7f161bdc 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -8,12 +8,14 @@ use std::fmt; use std::io::{self, Read, Write}; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use std::io::{IoSlice, IoSliceMut}; use std::mem::MaybeUninit; -#[cfg(not(target_os = "nto"))] +#[cfg(not(any(target_os = "nto", target_os = "wasi")))] use std::net::Ipv6Addr; use std::net::{self, Ipv4Addr, Shutdown}; +#[cfg(target_os = "wasi")] +use std::os::fd::{FromRawFd, IntoRawFd}; #[cfg(unix)] use std::os::unix::io::{FromRawFd, IntoRawFd}; #[cfg(windows)] @@ -24,7 +26,7 @@ use crate::sys::{self, c_int, getsockopt, setsockopt, Bool}; #[cfg(all(unix, not(target_os = "redox")))] use crate::MsgHdrMut; use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type}; -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] use crate::{MaybeUninitSlice, MsgHdr, RecvFlags}; /// Owned wrapper around a system socket. @@ -102,7 +104,7 @@ impl Socket { // Violating this assumption (fd never negative) causes UB, // something we don't want. So check for that we have this // `assert!`. - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] assert!(raw >= 0, "tried to create a `Socket` with an invalid fd"); sys::socket_from_raw(raw) }, @@ -233,7 +235,7 @@ impl Socket { match res { Ok(()) => return Ok(()), Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} - #[cfg(unix)] + #[cfg(any(unix, target_os = "wasi"))] Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} Err(e) => return Err(e), } @@ -356,6 +358,7 @@ impl Socket { /// On Windows this can **not** be used function cannot be used on a /// QOS-enabled socket, see /// . + #[cfg(not(target_os = "wasi"))] pub fn try_clone(&self) -> io::Result { sys::try_clone(self.as_raw()).map(Socket::from_raw) } @@ -427,6 +430,7 @@ impl Socket { /// /// [`recv`]: Socket::recv /// [`out_of_band_inline`]: Socket::out_of_band_inline + #[cfg(not(target_os = "wasi"))] #[cfg_attr(target_os = "redox", allow(rustdoc::broken_intra_doc_links))] pub fn recv_out_of_band(&self, buf: &mut [MaybeUninit]) -> io::Result { self.recv_with_flags(buf, sys::MSG_OOB) @@ -470,8 +474,8 @@ impl Socket { /// Note that the [`io::Read::read_vectored`] implementation calls this /// function with `buf`s of type `&mut [IoSliceMut]`, allowing initialised /// buffers to be used without using `unsafe`. - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn recv_vectored( &self, bufs: &mut [MaybeUninitSlice<'_>], @@ -490,8 +494,8 @@ impl Socket { /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn recv_vectored_with_flags( &self, bufs: &mut [MaybeUninitSlice<'_>], @@ -513,6 +517,7 @@ impl Socket { /// [`recv`]. /// /// [`recv`]: Socket::recv + #[cfg(not(target_os = "wasi"))] pub fn peek(&self, buf: &mut [MaybeUninit]) -> io::Result { self.recv_with_flags(buf, sys::MSG_PEEK) } @@ -556,8 +561,8 @@ impl Socket { /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn recv_from_vectored( &self, bufs: &mut [MaybeUninitSlice<'_>], @@ -576,8 +581,8 @@ impl Socket { /// as [`recv_vectored`]. /// /// [`recv_vectored`]: Socket::recv_vectored - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn recv_from_vectored_with_flags( &self, bufs: &mut [MaybeUninitSlice<'_>], @@ -615,6 +620,7 @@ impl Socket { /// /// [`recv`]: Socket::recv /// [`peek_sender`]: Socket::peek_sender + #[cfg(not(target_os = "wasi"))] pub fn peek_from(&self, buf: &mut [MaybeUninit]) -> io::Result<(usize, SockAddr)> { self.recv_from_with_flags(buf, sys::MSG_PEEK) } @@ -625,6 +631,7 @@ impl Socket { /// but suppresses the `WSAEMSGSIZE` error on Windows. /// /// [`peek_from`]: Socket::peek_from + #[cfg(not(target_os = "wasi"))] pub fn peek_sender(&self) -> io::Result { sys::peek_sender(self.as_raw()) } @@ -662,8 +669,8 @@ impl Socket { } /// Send data to the connected peer. Returns the amount of bytes written. - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn send_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored_with_flags(bufs, 0) } @@ -673,8 +680,8 @@ impl Socket { #[doc = man_links!(sendmsg(2))] /// /// [`send_vectored`]: Socket::send_vectored - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn send_vectored_with_flags( &self, bufs: &[IoSlice<'_>], @@ -690,6 +697,7 @@ impl Socket { /// /// [`send`]: Socket::send /// [`out_of_band_inline`]: Socket::out_of_band_inline + #[cfg(not(target_os = "wasi"))] #[cfg_attr(target_os = "redox", allow(rustdoc::broken_intra_doc_links))] pub fn send_out_of_band(&self, buf: &[u8]) -> io::Result { self.send_with_flags(buf, sys::MSG_OOB) @@ -720,8 +728,8 @@ impl Socket { /// Send data to a peer listening on `addr`. Returns the amount of bytes /// written. #[doc = man_links!(sendmsg(2))] - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn send_to_vectored(&self, bufs: &[IoSlice<'_>], addr: &SockAddr) -> io::Result { self.send_to_vectored_with_flags(bufs, addr, 0) } @@ -730,8 +738,8 @@ impl Socket { /// arbitrary flags to the underlying `sendmsg`/`WSASendTo` call. /// /// [`send_to_vectored`]: Socket::send_to_vectored - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn send_to_vectored_with_flags( &self, bufs: &[IoSlice<'_>], @@ -743,8 +751,8 @@ impl Socket { /// Send a message on a socket using a message structure. #[doc = man_links!(sendmsg(2))] - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn sendmsg(&self, msg: &MsgHdr<'_, '_, '_>, flags: sys::c_int) -> io::Result { sys::sendmsg(self.as_raw(), msg, flags) } @@ -841,6 +849,7 @@ impl Socket { /// For more information about this option, see [`set_broadcast`]. /// /// [`set_broadcast`]: Socket::set_broadcast + #[cfg(not(target_os = "wasi"))] pub fn broadcast(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_BROADCAST) @@ -852,6 +861,7 @@ impl Socket { /// /// When enabled, this socket is allowed to send packets to a broadcast /// address. + #[cfg(not(target_os = "wasi"))] pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { unsafe { setsockopt( @@ -907,6 +917,7 @@ impl Socket { /// For more information about this option, see [`set_linger`]. /// /// [`set_linger`]: Socket::set_linger + #[cfg(not(target_os = "wasi"))] pub fn linger(&self) -> io::Result> { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER) @@ -928,6 +939,7 @@ impl Socket { /// silently truncated. /// /// On Apple platforms (e.g. macOS, iOS, etc) this uses `SO_LINGER_SEC`. + #[cfg(not(target_os = "wasi"))] pub fn set_linger(&self, linger: Option) -> io::Result<()> { let linger = into_linger(linger); unsafe { setsockopt(self.as_raw(), sys::SOL_SOCKET, sys::SO_LINGER, linger) } @@ -938,8 +950,8 @@ impl Socket { /// For more information about this option, see [`set_out_of_band_inline`]. /// /// [`set_out_of_band_inline`]: Socket::set_out_of_band_inline - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn out_of_band_inline(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::SOL_SOCKET, sys::SO_OOBINLINE) @@ -953,8 +965,8 @@ impl Socket { /// receive data stream. Otherwise, out-of-band data is passed only when the /// `MSG_OOB` flag is set during receiving. As per RFC6093, TCP sockets /// using the Urgent mechanism are encouraged to set this flag. - #[cfg(not(target_os = "redox"))] - #[cfg_attr(docsrs, doc(cfg(not(target_os = "redox"))))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] + #[cfg_attr(docsrs, doc(cfg(not(any(target_os = "redox", target_os = "wasi")))))] pub fn set_out_of_band_inline(&self, oob_inline: bool) -> io::Result<()> { unsafe { setsockopt( @@ -1028,6 +1040,7 @@ impl Socket { /// /// If the returned timeout is `None`, then `read` and `recv` calls will /// block indefinitely. + #[cfg(not(target_os = "wasi"))] pub fn read_timeout(&self) -> io::Result> { sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO) } @@ -1036,6 +1049,7 @@ impl Socket { /// /// If `timeout` is `None`, then `read` and `recv` calls will block /// indefinitely. + #[cfg(not(target_os = "wasi"))] pub fn set_read_timeout(&self, duration: Option) -> io::Result<()> { sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_RCVTIMEO, duration) } @@ -1099,6 +1113,7 @@ impl Socket { /// /// If the returned timeout is `None`, then `write` and `send` calls will /// block indefinitely. + #[cfg(not(target_os = "wasi"))] pub fn write_timeout(&self) -> io::Result> { sys::timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO) } @@ -1107,11 +1122,13 @@ impl Socket { /// /// If `timeout` is `None`, then `write` and `send` calls will block /// indefinitely. + #[cfg(not(target_os = "wasi"))] pub fn set_write_timeout(&self, duration: Option) -> io::Result<()> { sys::set_timeout_opt(self.as_raw(), sys::SOL_SOCKET, sys::SO_SNDTIMEO, duration) } } +#[cfg(not(target_os = "wasi"))] const fn from_linger(linger: sys::linger) -> Option { if linger.l_onoff == 0 { None @@ -1120,6 +1137,7 @@ const fn from_linger(linger: sys::linger) -> Option { } } +#[cfg(not(target_os = "wasi"))] const fn into_linger(duration: Option) -> sys::linger { match duration { Some(duration) => sys::linger { @@ -1140,10 +1158,16 @@ const fn into_linger(duration: Option) -> sys::linger { /// * Windows: impl Socket { /// This method is deprecated, use [`crate::Socket::header_included_v4`]. - #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] + #[cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] #[deprecated = "Use `Socket::header_included_v4` instead"] pub fn header_included(&self) -> io::Result { @@ -1154,10 +1178,16 @@ impl Socket { /// For more information about this option, see [`set_header_included`]. /// /// [`set_header_included`]: Socket::set_header_included - #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] + #[cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] pub fn header_included_v4(&self) -> io::Result { unsafe { @@ -1171,10 +1201,16 @@ impl Socket { any(target_os = "fuchsia", target_os = "illumos", target_os = "solaris"), allow(rustdoc::broken_intra_doc_links) )] - #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] + #[cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] #[deprecated = "Use `Socket::set_header_included_v4` instead"] pub fn set_header_included(&self, included: bool) -> io::Result<()> { @@ -1196,10 +1232,16 @@ impl Socket { any(target_os = "fuchsia", target_os = "illumos", target_os = "solaris"), allow(rustdoc::broken_intra_doc_links) )] - #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))] + #[cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] pub fn set_header_included_v4(&self, included: bool) -> io::Result<()> { unsafe { @@ -1261,6 +1303,7 @@ impl Socket { /// address of the local interface with which the system should join the /// multicast group. If it's [`Ipv4Addr::UNSPECIFIED`] (`INADDR_ANY`) then /// an appropriate interface is chosen by the system. + #[cfg(not(target_os = "wasi"))] pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { let mreq = sys::IpMreq { imr_multiaddr: sys::to_in_addr(multiaddr), @@ -1274,6 +1317,7 @@ impl Socket { /// For more information about this option, see [`join_multicast_v4`]. /// /// [`join_multicast_v4`]: Socket::join_multicast_v4 + #[cfg(not(target_os = "wasi"))] pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { let mreq = sys::IpMreq { imr_multiaddr: sys::to_in_addr(multiaddr), @@ -1306,6 +1350,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn join_multicast_v4_n( &self, @@ -1339,6 +1384,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn leave_multicast_v4_n( &self, @@ -1374,6 +1420,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn join_ssm_v4( &self, @@ -1412,6 +1459,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn leave_ssm_v4( &self, @@ -1476,6 +1524,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_if_v4`]. /// /// [`set_multicast_if_v4`]: Socket::set_multicast_if_v4 + #[cfg(not(target_os = "wasi"))] pub fn multicast_if_v4(&self) -> io::Result { unsafe { getsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_IF).map(sys::from_in_addr) @@ -1485,6 +1534,7 @@ impl Socket { /// Set the value of the `IP_MULTICAST_IF` option for this socket. /// /// Specifies the interface to use for routing multicast packets. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_if_v4(&self, interface: &Ipv4Addr) -> io::Result<()> { let interface = sys::to_in_addr(interface); unsafe { @@ -1502,6 +1552,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_loop_v4`]. /// /// [`set_multicast_loop_v4`]: Socket::set_multicast_loop_v4 + #[cfg(not(target_os = "wasi"))] pub fn multicast_loop_v4(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_LOOP) @@ -1513,6 +1564,7 @@ impl Socket { /// /// If enabled, multicast packets will be looped back to the local socket. /// Note that this may not have any affect on IPv6 sockets. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_loop_v4(&self, loop_v4: bool) -> io::Result<()> { unsafe { setsockopt( @@ -1529,6 +1581,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_ttl_v4`]. /// /// [`set_multicast_ttl_v4`]: Socket::set_multicast_ttl_v4 + #[cfg(not(target_os = "wasi"))] pub fn multicast_ttl_v4(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IP, sys::IP_MULTICAST_TTL) @@ -1543,6 +1596,7 @@ impl Socket { /// don't leave the local network unless explicitly requested. /// /// Note that this may not have any affect on IPv6 sockets. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { unsafe { setsockopt( @@ -1586,6 +1640,7 @@ impl Socket { target_os = "solaris", target_os = "illumos", target_os = "haiku", + target_os = "wasi", )))] pub fn set_tos(&self, tos: u32) -> io::Result<()> { unsafe { setsockopt(self.as_raw(), sys::IPPROTO_IP, sys::IP_TOS, tos as c_int) } @@ -1605,6 +1660,7 @@ impl Socket { target_os = "solaris", target_os = "illumos", target_os = "haiku", + target_os = "wasi", )))] pub fn tos(&self) -> io::Result { unsafe { @@ -1631,6 +1687,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn set_recv_tos(&self, recv_tos: bool) -> io::Result<()> { unsafe { @@ -1662,6 +1719,7 @@ impl Socket { target_os = "nto", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn recv_tos(&self) -> io::Result { unsafe { @@ -1690,12 +1748,16 @@ impl Socket { target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", - target_os = "netbsd" + target_os = "netbsd", + target_os = "wasi" )) ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] pub fn header_included_v6(&self) -> io::Result { unsafe { @@ -1724,12 +1786,16 @@ impl Socket { target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", - target_os = "netbsd" + target_os = "netbsd", + target_os = "wasi" )) ))] #[cfg_attr( docsrs, - doc(cfg(all(feature = "all", not(any(target_os = "redox", target_os = "espidf"))))) + doc(cfg(all( + feature = "all", + not(any(target_os = "redox", target_os = "espidf", target_os = "wasi")) + ))) )] pub fn set_header_included_v6(&self, included: bool) -> io::Result<()> { unsafe { @@ -1749,7 +1815,7 @@ impl Socket { /// This function specifies a new multicast group for this socket to join. /// The address must be a valid multicast address, and `interface` is the /// index of the interface to join/leave (or 0 to indicate any interface). - #[cfg(not(target_os = "nto"))] + #[cfg(not(any(target_os = "nto", target_os = "wasi")))] pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { let mreq = sys::Ipv6Mreq { ipv6mr_multiaddr: sys::to_in6_addr(multiaddr), @@ -1773,7 +1839,7 @@ impl Socket { /// For more information about this option, see [`join_multicast_v6`]. /// /// [`join_multicast_v6`]: Socket::join_multicast_v6 - #[cfg(not(target_os = "nto"))] + #[cfg(not(any(target_os = "nto", target_os = "wasi")))] pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { let mreq = sys::Ipv6Mreq { ipv6mr_multiaddr: sys::to_in6_addr(multiaddr), @@ -1795,6 +1861,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_hops_v6`]. /// /// [`set_multicast_hops_v6`]: Socket::set_multicast_hops_v6 + #[cfg(not(target_os = "wasi"))] pub fn multicast_hops_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_HOPS) @@ -1807,6 +1874,7 @@ impl Socket { /// Indicates the number of "routers" multicast packets will transit for /// this socket. The default value is 1 which means that multicast packets /// don't leave the local network unless explicitly requested. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_hops_v6(&self, hops: u32) -> io::Result<()> { unsafe { setsockopt( @@ -1860,6 +1928,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_if_v6`]. /// /// [`set_multicast_if_v6`]: Socket::set_multicast_if_v6 + #[cfg(not(target_os = "wasi"))] pub fn multicast_if_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_IF) @@ -1872,6 +1941,7 @@ impl Socket { /// Specifies the interface to use for routing multicast packets. Unlike /// ipv4, this is generally required in ipv6 contexts where network routing /// prefixes may overlap. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_if_v6(&self, interface: u32) -> io::Result<()> { unsafe { setsockopt( @@ -1888,6 +1958,7 @@ impl Socket { /// For more information about this option, see [`set_multicast_loop_v6`]. /// /// [`set_multicast_loop_v6`]: Socket::set_multicast_loop_v6 + #[cfg(not(target_os = "wasi"))] pub fn multicast_loop_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_MULTICAST_LOOP) @@ -1899,6 +1970,7 @@ impl Socket { /// /// Controls whether this socket sees the multicast packets it sends itself. /// Note that this may not have any affect on IPv4 sockets. + #[cfg(not(target_os = "wasi"))] pub fn set_multicast_loop_v6(&self, loop_v6: bool) -> io::Result<()> { unsafe { setsockopt( @@ -1939,6 +2011,7 @@ impl Socket { /// For more information about this option, see [`set_only_v6`]. /// /// [`set_only_v6`]: Socket::set_only_v6 + #[cfg(not(target_os = "wasi"))] pub fn only_v6(&self) -> io::Result { unsafe { getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, sys::IPV6_V6ONLY) @@ -1954,6 +2027,7 @@ impl Socket { /// /// If this is set to `false` then the socket can be used to send and /// receive packets from an IPv4-mapped IPv6 address. + #[cfg(not(target_os = "wasi"))] pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { unsafe { setsockopt( @@ -1982,6 +2056,7 @@ impl Socket { target_os = "hurd", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn recv_tclass_v6(&self) -> io::Result { unsafe { @@ -2007,6 +2082,7 @@ impl Socket { target_os = "hurd", target_os = "espidf", target_os = "vita", + target_os = "wasi", )))] pub fn set_recv_tclass_v6(&self, recv_tclass: bool) -> io::Result<()> { unsafe { @@ -2074,6 +2150,7 @@ impl Socket { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -2093,6 +2170,7 @@ impl Socket { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))) @@ -2123,6 +2201,7 @@ impl Socket { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -2142,6 +2221,7 @@ impl Socket { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))) @@ -2235,7 +2315,7 @@ impl Read for Socket { self.recv(buf) } - #[cfg(not(target_os = "redox"))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // Safety: both `IoSliceMut` and `MaybeUninitSlice` promise to have the // same layout, that of `iovec`/`WSABUF`. Furthermore, `recv_vectored` @@ -2253,7 +2333,7 @@ impl<'a> Read for &'a Socket { self.recv(buf) } - #[cfg(not(target_os = "redox"))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { // Safety: see other `Read::read` impl. let bufs = unsafe { &mut *(bufs as *mut [IoSliceMut<'_>] as *mut [MaybeUninitSlice<'_>]) }; @@ -2266,7 +2346,7 @@ impl Write for Socket { self.send(buf) } - #[cfg(not(target_os = "redox"))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored(bufs) } @@ -2281,7 +2361,7 @@ impl<'a> Write for &'a Socket { self.send(buf) } - #[cfg(not(target_os = "redox"))] + #[cfg(not(any(target_os = "redox", target_os = "wasi")))] fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { self.send_vectored(bufs) } diff --git a/src/sockref.rs b/src/sockref.rs index d23b7c0f..869d86ae 100644 --- a/src/sockref.rs +++ b/src/sockref.rs @@ -2,6 +2,8 @@ use std::fmt; use std::marker::PhantomData; use std::mem::ManuallyDrop; use std::ops::Deref; +#[cfg(target_os = "wasi")] +use std::os::fd::{AsFd, AsRawFd, FromRawFd}; #[cfg(unix)] use std::os::unix::io::{AsFd, AsRawFd, FromRawFd}; #[cfg(windows)] @@ -77,8 +79,8 @@ impl<'s> Deref for SockRef<'s> { } /// On Windows, a corresponding `From<&impl AsSocket>` implementation exists. -#[cfg(unix)] -#[cfg_attr(docsrs, doc(cfg(unix)))] +#[cfg(any(unix, target_os = "wasi"))] +#[cfg_attr(docsrs, doc(cfg(any(unix, target_os = "wasi"))))] impl<'s, S> From<&'s S> for SockRef<'s> where S: AsFd, diff --git a/src/sys/wasi.rs b/src/sys/wasi.rs new file mode 100644 index 00000000..7cdae464 --- /dev/null +++ b/src/sys/wasi.rs @@ -0,0 +1,423 @@ +// Copyright 2015 The Rust Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp::min; +use std::mem::MaybeUninit; +use std::net::{Ipv4Addr, Ipv6Addr, Shutdown}; +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use std::time::{Duration, Instant}; +use std::{io, mem, ptr}; + +pub(crate) use libc::{ + c_int, sa_family_t, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_storage, socklen_t, ssize_t, + AF_INET, AF_INET6, IPPROTO_IP, IPPROTO_IPV6, IPPROTO_TCP, IPPROTO_UDP, IPV6_UNICAST_HOPS, + IP_TTL, SOCK_DGRAM, SOCK_STREAM, SOL_SOCKET, SO_ERROR, SO_KEEPALIVE, SO_RCVBUF, SO_REUSEADDR, + SO_SNDBUF, SO_TYPE, TCP_NODELAY, +}; +use libc::{in6_addr, in_addr}; +#[cfg(feature = "all")] +pub(crate) use libc::{TCP_KEEPCNT, TCP_KEEPINTVL}; + +use crate::{Domain, Protocol, SockAddr, TcpKeepalive, Type}; + +pub(crate) type Bool = c_int; + +/// Helper macro to execute a system call that returns an `io::Result`. +macro_rules! syscall { + ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{ + #[allow(unused_unsafe)] + let res = unsafe { libc::$fn($($arg, )*) }; + if res == -1 { + Err(std::io::Error::last_os_error()) + } else { + Ok(res) + } + }}; +} + +const MAX_BUF_LEN: usize = ssize_t::MAX as usize; + +impl_debug!( + Domain, + libc::AF_INET, + libc::AF_INET6, + libc::AF_UNSPEC, // = 0. +); + +/// WASI only API. +impl Type { + /// Set `SOCK_NONBLOCK` on the `Type`. + pub const fn nonblocking(self) -> Type { + Type(self.0 | libc::SOCK_NONBLOCK) + } +} + +impl_debug!( + Type, + libc::SOCK_STREAM, + libc::SOCK_DGRAM, + libc::SOCK_NONBLOCK, +); + +impl_debug!(Protocol, libc::IPPROTO_TCP, libc::IPPROTO_UDP,); + +pub(crate) type Socket = c_int; + +pub(crate) unsafe fn socket_from_raw(socket: Socket) -> crate::socket::Inner { + crate::socket::Inner::from_raw_fd(socket) +} + +pub(crate) fn socket_as_raw(socket: &crate::socket::Inner) -> Socket { + socket.as_raw_fd() +} + +pub(crate) fn socket_into_raw(socket: crate::socket::Inner) -> Socket { + socket.into_raw_fd() +} + +pub(crate) fn socket(family: c_int, ty: c_int, protocol: c_int) -> io::Result { + syscall!(socket(family, ty, protocol)) +} + +pub(crate) fn bind(fd: Socket, addr: &SockAddr) -> io::Result<()> { + syscall!(bind(fd, addr.as_ptr(), addr.len() as _)).map(|_| ()) +} + +pub(crate) fn connect(fd: Socket, addr: &SockAddr) -> io::Result<()> { + syscall!(connect(fd, addr.as_ptr(), addr.len())).map(|_| ()) +} + +pub(crate) fn poll_connect(socket: &crate::Socket, timeout: Duration) -> io::Result<()> { + let start = Instant::now(); + + let mut pollfd = libc::pollfd { + fd: socket.as_raw(), + events: libc::POLLIN | libc::POLLOUT, + revents: 0, + }; + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::ErrorKind::TimedOut.into()); + } + + let timeout = (timeout - elapsed).as_millis(); + let timeout = timeout.clamp(1, c_int::MAX as u128) as c_int; + + match syscall!(poll(&mut pollfd, 1, timeout)) { + Ok(0) => return Err(io::ErrorKind::TimedOut.into()), + Ok(_) => { + // WASI poll does not return POLLHUP or POLLERR in revents. Check if the + // connnection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = socket.take_error()? { + return Err(e); + } + return Ok(()); + } + // Got interrupted, try again. + Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue, + Err(err) => return Err(err), + } + } +} + +pub(crate) fn listen(fd: Socket, backlog: c_int) -> io::Result<()> { + syscall!(listen(fd, backlog)).map(|_| ()) +} + +pub(crate) fn accept(fd: Socket) -> io::Result<(Socket, SockAddr)> { + // Safety: `accept` initialises the `SockAddr` for us. + unsafe { SockAddr::try_init(|storage, len| syscall!(accept(fd, storage.cast(), len))) } +} + +pub(crate) fn getsockname(fd: Socket) -> io::Result { + // Safety: `accept` initialises the `SockAddr` for us. + unsafe { SockAddr::try_init(|storage, len| syscall!(getsockname(fd, storage.cast(), len))) } + .map(|(_, addr)| addr) +} + +pub(crate) fn getpeername(fd: Socket) -> io::Result { + // Safety: `accept` initialises the `SockAddr` for us. + unsafe { SockAddr::try_init(|storage, len| syscall!(getpeername(fd, storage.cast(), len))) } + .map(|(_, addr)| addr) +} + +pub(crate) fn set_nonblocking(fd: Socket, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + syscall!(ioctl(fd, libc::FIONBIO, &mut nonblocking)).map(|_| ()) +} + +pub(crate) fn shutdown(fd: Socket, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + syscall!(shutdown(fd, how)).map(|_| ()) +} + +pub(crate) fn recv(fd: Socket, buf: &mut [MaybeUninit], flags: c_int) -> io::Result { + syscall!(recv( + fd, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + )) + .map(|n| n as usize) +} + +pub(crate) fn recv_from( + fd: Socket, + buf: &mut [MaybeUninit], + flags: c_int, +) -> io::Result<(usize, SockAddr)> { + // Safety: `recvfrom` initialises the `SockAddr` for us. + unsafe { + SockAddr::try_init(|addr, addrlen| { + syscall!(recvfrom( + fd, + buf.as_mut_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + addr.cast(), + addrlen + )) + .map(|n| n as usize) + }) + } +} + +pub(crate) fn send(fd: Socket, buf: &[u8], flags: c_int) -> io::Result { + syscall!(send( + fd, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + )) + .map(|n| n as usize) +} + +pub(crate) fn send_to(fd: Socket, buf: &[u8], addr: &SockAddr, flags: c_int) -> io::Result { + syscall!(sendto( + fd, + buf.as_ptr().cast(), + min(buf.len(), MAX_BUF_LEN), + flags, + addr.as_ptr(), + addr.len(), + )) + .map(|n| n as usize) +} + +#[cfg(feature = "all")] +pub(crate) fn keepalive_time(fd: Socket) -> io::Result { + unsafe { + getsockopt::(fd, IPPROTO_TCP, libc::TCP_KEEPIDLE) + .map(|secs| Duration::from_secs(secs as u64)) + } +} + +pub(crate) fn set_tcp_keepalive(fd: Socket, keepalive: &TcpKeepalive) -> io::Result<()> { + if let Some(time) = keepalive.time { + let secs = into_secs(time); + unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, secs)? } + } + + if let Some(interval) = keepalive.interval { + let secs = into_secs(interval); + unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPINTVL, secs)? } + } + + if let Some(retries) = keepalive.retries { + unsafe { setsockopt(fd, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, retries as c_int)? } + } + + Ok(()) +} + +fn into_secs(duration: Duration) -> c_int { + min(duration.as_secs(), c_int::MAX as u64) as c_int +} + +/// Caller must ensure `T` is the correct type for `opt` and `val`. +pub(crate) unsafe fn getsockopt(fd: Socket, opt: c_int, val: c_int) -> io::Result { + let mut payload: MaybeUninit = MaybeUninit::uninit(); + let mut len = size_of::() as libc::socklen_t; + syscall!(getsockopt( + fd, + opt, + val, + payload.as_mut_ptr().cast(), + &mut len, + )) + .map(|_| { + debug_assert_eq!(len as usize, size_of::()); + // Safety: `getsockopt` initialised `payload` for us. + payload.assume_init() + }) +} + +/// Caller must ensure `T` is the correct type for `opt` and `val`. +pub(crate) unsafe fn setsockopt( + fd: Socket, + opt: c_int, + val: c_int, + payload: T, +) -> io::Result<()> { + let payload = ptr::addr_of!(payload).cast(); + syscall!(setsockopt( + fd, + opt, + val, + payload, + mem::size_of::() as libc::socklen_t, + )) + .map(|_| ()) +} + +pub(crate) const fn to_in_addr(addr: &Ipv4Addr) -> in_addr { + // `s_addr` is stored as BE on all machines, and the array is in BE order. + // So the native endian conversion method is used so that it's never + // swapped. + in_addr { + s_addr: u32::from_ne_bytes(addr.octets()), + } +} + +pub(crate) fn from_in_addr(in_addr: in_addr) -> Ipv4Addr { + Ipv4Addr::from(in_addr.s_addr.to_ne_bytes()) +} + +pub(crate) const fn to_in6_addr(addr: &Ipv6Addr) -> in6_addr { + in6_addr { + s6_addr: addr.octets(), + } +} + +pub(crate) fn from_in6_addr(addr: in6_addr) -> Ipv6Addr { + Ipv6Addr::from(addr.s6_addr) +} + +/// WASI only API. +impl crate::Socket { + /// Accept a new incoming connection from this listener. + /// + /// This function directly corresponds to the `accept4(2)` function. + /// + /// This function will block the calling thread until a new connection is + /// established. When established, the corresponding `Socket` and the remote + /// peer's address will be returned. + pub fn accept4(&self, flags: c_int) -> io::Result<(crate::Socket, SockAddr)> { + // Safety: `accept4` initialises the `SockAddr` for us. + unsafe { + SockAddr::try_init(|storage, len| { + syscall!(accept4(self.as_raw(), storage.cast(), len, flags)) + .map(crate::Socket::from_raw) + }) + } + } + + /// Returns `true` if `listen(2)` was called on this socket by checking the + /// `SO_ACCEPTCONN` option on this socket. + pub fn is_listener(&self) -> io::Result { + unsafe { + getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_ACCEPTCONN) + .map(|v| v != 0) + } + } + + /// Returns the [`Domain`] of this socket by checking the `SO_DOMAIN` option + /// on this socket. + pub fn domain(&self) -> io::Result { + unsafe { getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_DOMAIN).map(Domain) } + } + + /// Returns the [`Protocol`] of this socket by checking the `SO_PROTOCOL` + /// option on this socket. + pub fn protocol(&self) -> io::Result> { + unsafe { + getsockopt::(self.as_raw(), libc::SOL_SOCKET, libc::SO_PROTOCOL).map(|v| match v + { + 0 => None, + p => Some(Protocol(p)), + }) + } + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl AsFd for crate::Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + // SAFETY: lifetime is bound by self. + unsafe { BorrowedFd::borrow_raw(self.as_raw()) } + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl AsRawFd for crate::Socket { + fn as_raw_fd(&self) -> c_int { + self.as_raw() + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl From for OwnedFd { + fn from(sock: crate::Socket) -> OwnedFd { + // SAFETY: sock.into_raw() always returns a valid fd. + unsafe { OwnedFd::from_raw_fd(sock.into_raw()) } + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl IntoRawFd for crate::Socket { + fn into_raw_fd(self) -> c_int { + self.into_raw() + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl From for crate::Socket { + fn from(fd: OwnedFd) -> crate::Socket { + // SAFETY: `OwnedFd` ensures the fd is valid. + unsafe { crate::Socket::from_raw_fd(fd.into_raw_fd()) } + } +} + +#[cfg_attr(docsrs, doc(cfg(target_os = "wasi")))] +impl FromRawFd for crate::Socket { + unsafe fn from_raw_fd(fd: c_int) -> crate::Socket { + crate::Socket::from_raw(fd) + } +} + +#[test] +fn in_addr_convertion() { + let ip = Ipv4Addr::new(127, 0, 0, 1); + let raw = to_in_addr(&ip); + // NOTE: `in_addr` is packed on NetBSD and it's unsafe to borrow. + let a = raw.s_addr; + assert_eq!(a, u32::from_ne_bytes([127, 0, 0, 1])); + assert_eq!(from_in_addr(raw), ip); + + let ip = Ipv4Addr::new(127, 34, 4, 12); + let raw = to_in_addr(&ip); + let a = raw.s_addr; + assert_eq!(a, u32::from_ne_bytes([127, 34, 4, 12])); + assert_eq!(from_in_addr(raw), ip); +} + +#[test] +fn in6_addr_convertion() { + let ip = Ipv6Addr::new(0x2000, 1, 2, 3, 4, 5, 6, 7); + let raw = to_in6_addr(&ip); + let want = [32, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7]; + assert_eq!(raw.s6_addr, want); + assert_eq!(from_in6_addr(raw), ip); +} diff --git a/tests/socket.rs b/tests/socket.rs index c87ca0e0..5bd080a4 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -14,15 +14,16 @@ ))] use std::fs::File; use std::io; -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] use std::io::IoSlice; +#[cfg(not(target_os = "wasi"))] use std::io::Read; use std::io::Write; -#[cfg(not(target_os = "vita"))] +#[cfg(not(any(target_os = "vita", target_os = "wasi")))] use std::mem::MaybeUninit; use std::mem::{self}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] use std::net::{Ipv6Addr, SocketAddrV6}; #[cfg(all( feature = "all", @@ -45,15 +46,16 @@ use std::os::windows::io::AsRawSocket; #[cfg(unix)] use std::path::Path; use std::str; -#[cfg(not(target_os = "vita"))] +#[cfg(not(any(target_os = "vita", target_os = "wasi")))] use std::thread; use std::time::Duration; +#[cfg(not(target_os = "wasi"))] use std::{env, fs}; #[cfg(windows)] use windows_sys::Win32::Foundation::{GetHandleInformation, HANDLE_FLAG_INHERIT}; -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] use socket2::MaybeUninitSlice; #[cfg(not(target_os = "vita"))] use socket2::TcpKeepalive; @@ -75,6 +77,7 @@ fn domain_fmt_debug() { let tests = &[ (Domain::IPV4, "AF_INET"), (Domain::IPV6, "AF_INET6"), + #[cfg(not(target_os = "wasi"))] (Domain::UNIX, "AF_UNIX"), #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] (Domain::PACKET, "AF_PACKET"), @@ -98,9 +101,9 @@ fn type_fmt_debug() { let tests = &[ (Type::STREAM, "SOCK_STREAM"), (Type::DGRAM, "SOCK_DGRAM"), - #[cfg(feature = "all")] + #[cfg(all(feature = "all", not(target_os = "wasi")))] (Type::SEQPACKET, "SOCK_SEQPACKET"), - #[cfg(all(feature = "all", not(target_os = "redox")))] + #[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "wasi"))))] (Type::RAW, "SOCK_RAW"), (500.into(), "500"), ]; @@ -117,10 +120,12 @@ fn type_fmt_debug() { #[test] fn protocol_fmt_debug() { let tests = &[ - (Protocol::ICMPV4, "IPPROTO_ICMP"), - (Protocol::ICMPV6, "IPPROTO_ICMPV6"), (Protocol::TCP, "IPPROTO_TCP"), (Protocol::UDP, "IPPROTO_UDP"), + #[cfg(not(target_os = "wasi"))] + (Protocol::ICMPV4, "IPPROTO_ICMP"), + #[cfg(not(target_os = "wasi"))] + (Protocol::ICMPV6, "IPPROTO_ICMPV6"), #[cfg(target_os = "linux")] (Protocol::MPTCP, "IPPROTO_MPTCP"), #[cfg(all(feature = "all", target_os = "linux"))] @@ -141,13 +146,17 @@ fn protocol_fmt_debug() { #[test] #[should_panic = "tried to create a `Socket` with an invalid fd"] -#[cfg(unix)] +#[cfg(any(unix, target_os = "wasi"))] fn from_invalid_raw_fd_should_panic() { + #[cfg(target_os = "wasi")] + use std::os::fd::FromRawFd; + #[cfg(unix)] use std::os::unix::io::FromRawFd; let _socket = unsafe { Socket::from_raw_fd(-1) }; } #[test] +#[cfg(not(target_os = "wasi"))] fn socket_address_unix() { let string = "/tmp/socket"; let addr = SockAddr::unix(string).unwrap(); @@ -168,6 +177,7 @@ fn socket_address_unix() { } #[test] +#[cfg(not(target_os = "wasi"))] fn socket_address_unix_unnamed() { let addr = SockAddr::unix("").unwrap(); assert!(addr.as_socket_ipv4().is_none()); @@ -236,8 +246,8 @@ fn assert_common_flags(socket: &Socket, expected: bool) { #[cfg(windows)] assert_flag_no_inherit(socket, expected); - // Vita does not have process API, so neither SO_NOSIGPIPE nor FD_CLOEXEC are supported on this platform - #[cfg(target_os = "vita")] + // Vita and WASI do not have process API, so neither SO_NOSIGPIPE nor FD_CLOEXEC are supported on this platform + #[cfg(any(target_os = "vita", target_os = "wasi"))] { let _ = socket; let _ = expected; @@ -283,7 +293,8 @@ fn no_common_flags() { target_os = "fuchsia", target_os = "linux", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "wasi" ) ))] #[test] @@ -325,7 +336,7 @@ pub fn assert_nonblocking(socket: &Socket, want: bool) { } } -#[cfg(windows)] +#[cfg(any(windows, target_os = "wasi"))] #[track_caller] pub fn assert_nonblocking(_: &Socket, _: bool) { // No way to get this information... @@ -469,6 +480,7 @@ where assert_eq!(flags, want as _, "non-blocking option"); } +#[cfg(not(target_os = "wasi"))] const DATA: &[u8] = b"hello world"; #[test] @@ -531,6 +543,7 @@ fn pair() { assert_eq!(&buf[..n], DATA); } +#[cfg(not(target_os = "wasi"))] fn unix_sockets_supported() -> bool { #[cfg(windows)] { @@ -555,6 +568,7 @@ fn unix_sockets_supported() -> bool { } #[test] +#[cfg(not(target_os = "wasi"))] fn unix() { if !unix_sockets_supported() { return; @@ -606,7 +620,7 @@ fn vsock() { } #[test] -#[cfg(not(target_os = "vita"))] // Vita does not support OOB +#[cfg(not(any(target_os = "vita", target_os = "wasi")))] // Vita and WASI do not support OOB fn out_of_band() { let listener = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap(); listener.bind(&any_ipv4()).unwrap(); @@ -637,7 +651,7 @@ fn out_of_band() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn udp_peek_sender() { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -652,7 +666,7 @@ fn udp_peek_sender() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn send_recv_vectored() { let (socket_a, socket_b) = udp_pair_connected(); @@ -699,7 +713,7 @@ fn send_recv_vectored() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn send_from_recv_to_vectored() { let (socket_a, socket_b) = udp_pair_unconnected(); let addr_a = socket_a.local_addr().unwrap(); @@ -752,7 +766,7 @@ fn send_from_recv_to_vectored() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn sendmsg() { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -770,7 +784,7 @@ fn sendmsg() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn recv_vectored_truncated() { let (socket_a, socket_b) = udp_pair_connected(); @@ -790,7 +804,7 @@ fn recv_vectored_truncated() { } #[test] -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn recv_from_vectored_truncated() { let (socket_a, socket_b) = udp_pair_unconnected(); let addr_a = socket_a.local_addr().unwrap(); @@ -816,7 +830,7 @@ fn recv_from_vectored_truncated() { } /// Create a pair of non-connected UDP sockets suitable for unit tests. -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn udp_pair_unconnected() -> (Socket, Socket) { // Use ephemeral ports assigned by the OS. let unspecified_addr = SocketAddrV6::new(Ipv6Addr::LOCALHOST, 0, 0, 0); @@ -844,7 +858,7 @@ fn udp_pair_unconnected() -> (Socket, Socket) { } /// Create a pair of connected UDP sockets suitable for unit tests. -#[cfg(not(any(target_os = "redox", target_os = "vita")))] +#[cfg(not(any(target_os = "redox", target_os = "vita", target_os = "wasi")))] fn udp_pair_connected() -> (Socket, Socket) { let (socket_a, socket_b) = udp_pair_unconnected(); @@ -874,6 +888,7 @@ fn tcp_keepalive() { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", target_os = "windows", ) @@ -892,6 +907,7 @@ fn tcp_keepalive() { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -920,6 +936,7 @@ fn tcp_keepalive() { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -942,6 +959,7 @@ fn tcp_keepalive() { target_os = "macos", target_os = "netbsd", target_os = "tvos", + target_os = "wasi", target_os = "watchos", ) ))] @@ -1159,6 +1177,7 @@ fn sendfile() { target_os = "freebsd", target_os = "fuchsia", target_os = "linux", + target_os = "wasi", ) ))] #[test] @@ -1178,6 +1197,7 @@ fn is_listener() { // target_os = "freebsd", target_os = "fuchsia", target_os = "linux", + target_os = "wasi", ) ))] #[test] @@ -1188,8 +1208,11 @@ fn domain() { let socket = Socket::new(Domain::IPV6, Type::STREAM, None).unwrap(); assert_eq!(socket.domain().unwrap(), Domain::IPV6); - let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); - assert_eq!(socket.domain().unwrap(), Domain::UNIX); + #[cfg(not(target_os = "wasi"))] + { + let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); + assert_eq!(socket.domain().unwrap(), Domain::UNIX); + } } #[cfg(all( @@ -1199,13 +1222,17 @@ fn domain() { target_os = "freebsd", target_os = "fuchsia", target_os = "linux", + target_os = "wasi", target_os = "windows", ) ))] #[test] fn protocol() { - let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); - assert_eq!(socket.protocol().unwrap(), None); + #[cfg(not(target_os = "wasi"))] + { + let socket = Socket::new(Domain::UNIX, Type::STREAM, None).unwrap(); + assert_eq!(socket.protocol().unwrap(), None); + } /* Don't have permission for this on CI. let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::ICMPV4)).unwrap(); @@ -1278,7 +1305,7 @@ fn any_ipv4() -> SockAddr { /// Assume the `buf`fer to be initialised. // TODO: replace with `MaybeUninit::slice_assume_init_ref` once stable. -#[cfg(not(target_os = "vita"))] // Loopback has special behavior on vita +#[cfg(not(any(target_os = "vita", target_os = "wasi")))] // Loopback has special behavior on vita unsafe fn assume_init(buf: &[MaybeUninit]) -> &[u8] { &*(buf as *const [MaybeUninit] as *const [u8]) } @@ -1347,12 +1374,17 @@ test!( set_send_buffer_size(SET_BUF_SIZE), GET_BUF_SIZE ); -#[cfg(not(target_os = "redox"))] +#[cfg(not(any(target_os = "redox", target_os = "wasi")))] test!(out_of_band_inline, set_out_of_band_inline(true)); test!(reuse_address, set_reuse_address(true)); #[cfg(all( feature = "all", - not(any(windows, target_os = "solaris", target_os = "illumos")) + not(any( + windows, + target_os = "solaris", + target_os = "illumos", + target_os = "wasi" + )) ))] test!(reuse_port, set_reuse_port(true)); #[cfg(all(feature = "all", target_os = "freebsd"))] @@ -1390,7 +1422,9 @@ test!(quickack, set_quickack(false)); any(target_os = "android", target_os = "fuchsia", target_os = "linux") ))] test!(thin_linear_timeouts, set_thin_linear_timeouts(true)); +#[cfg(not(target_os = "wasi"))] test!(linger, set_linger(Some(Duration::from_secs(10)))); +#[cfg(not(target_os = "wasi"))] test!( read_timeout, set_read_timeout(Some(Duration::from_secs(10))) @@ -1409,6 +1443,7 @@ test!(IPv4 ttl, set_ttl(40)); target_os = "solaris", target_os = "illumos", target_os = "haiku", + target_os = "wasi", )))] test!(IPv4 tos, set_tos(96)); @@ -1424,10 +1459,11 @@ test!(IPv4 tos, set_tos(96)); target_os = "windows", target_os = "vita", target_os = "haiku", + target_os = "wasi", )))] test!(IPv4 recv_tos, set_recv_tos(true)); -#[cfg(not(windows))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. +#[cfg(not(any(windows, target_os = "wasi")))] // TODO: returns `WSAENOPROTOOPT` (10042) on Windows. test!(IPv4 broadcast, set_broadcast(true)); #[cfg(not(target_os = "vita"))] @@ -1438,7 +1474,8 @@ test!(IPv6 unicast_hops_v6, set_unicast_hops_v6(20)); target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", - target_os = "vita" + target_os = "vita", + target_os = "wasi" )))] test!(IPv6 only_v6, set_only_v6(true)); // IPv6 socket are already IPv6 only on FreeBSD and Windows. @@ -1472,6 +1509,7 @@ test!(IPv6 tclass_v6, set_tclass_v6(96)); target_os = "windows", target_os = "vita", target_os = "haiku", + target_os = "wasi", )))] test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); @@ -1498,6 +1536,7 @@ test!(IPv6 multicast_all_v6, set_multicast_all_v6(false)); target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] fn join_leave_multicast_v4_n() { let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); @@ -1529,6 +1568,7 @@ fn join_leave_multicast_v4_n() { target_os = "redox", target_os = "fuchsia", target_os = "vita", + target_os = "wasi", )))] fn join_leave_ssm_v4() { let socket = Socket::new(Domain::IPV4, Type::DGRAM, None).unwrap(); @@ -1540,7 +1580,7 @@ fn join_leave_ssm_v4() { } #[test] -#[cfg(all(feature = "all", not(target_os = "redox")))] +#[cfg(all(feature = "all", not(any(target_os = "redox", target_os = "wasi"))))] fn header_included() { let socket = match Socket::new(Domain::IPV4, Type::RAW, None) { Ok(socket) => socket, @@ -1572,7 +1612,8 @@ fn header_included() { target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", - target_os = "netbsd" + target_os = "netbsd", + target_os = "wasi" )) ))] fn header_included_ipv6() {