From 4f834264ab8d0cb95ac7972532661c84a154667e Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 2 Feb 2025 18:53:52 +0100 Subject: std: move network code into `sys` As per #117276, this PR moves `sys_common::net` and the `sys::pal::net` into the newly created `sys::net` module. In order to support #135141, I've moved all the current network code into a separate `connection` module, future functions like `hostname` can live in separate modules. I'll probably do a follow-up PR and clean up some of the actual code, this is mostly just a reorganization. --- library/std/src/sys/mod.rs | 1 + library/std/src/sys/net/connection/sgx.rs | 536 ++++++++++++++ library/std/src/sys/net/connection/socket.rs | 780 +++++++++++++++++++++ .../std/src/sys/net/connection/socket/hermit.rs | 346 +++++++++ library/std/src/sys/net/connection/socket/solid.rs | 413 +++++++++++ library/std/src/sys/net/connection/socket/tests.rs | 19 + library/std/src/sys/net/connection/socket/unix.rs | 651 +++++++++++++++++ .../std/src/sys/net/connection/socket/wasip2.rs | 417 +++++++++++ .../std/src/sys/net/connection/socket/windows.rs | 575 +++++++++++++++ library/std/src/sys/net/connection/unsupported.rs | 369 ++++++++++ library/std/src/sys/net/connection/wasip1.rs | 542 ++++++++++++++ library/std/src/sys/net/connection/xous/dns.rs | 128 ++++ library/std/src/sys/net/connection/xous/mod.rs | 83 +++ .../std/src/sys/net/connection/xous/tcplistener.rs | 243 +++++++ .../std/src/sys/net/connection/xous/tcpstream.rs | 424 +++++++++++ library/std/src/sys/net/connection/xous/udp.rs | 459 ++++++++++++ library/std/src/sys/net/mod.rs | 36 + library/std/src/sys/pal/hermit/mod.rs | 1 - library/std/src/sys/pal/hermit/net.rs | 347 --------- library/std/src/sys/pal/sgx/mod.rs | 1 - library/std/src/sys/pal/sgx/net.rs | 536 -------------- library/std/src/sys/pal/solid/error.rs | 3 +- library/std/src/sys/pal/solid/mod.rs | 3 +- library/std/src/sys/pal/solid/net.rs | 425 ----------- library/std/src/sys/pal/teeos/mod.rs | 1 - library/std/src/sys/pal/teeos/net.rs | 369 ---------- library/std/src/sys/pal/uefi/mod.rs | 2 - library/std/src/sys/pal/unix/l4re.rs | 564 --------------- library/std/src/sys/pal/unix/mod.rs | 6 - library/std/src/sys/pal/unix/net.rs | 651 ----------------- library/std/src/sys/pal/unsupported/mod.rs | 1 - library/std/src/sys/pal/unsupported/net.rs | 369 ---------- library/std/src/sys/pal/wasi/mod.rs | 4 +- library/std/src/sys/pal/wasi/net.rs | 543 -------------- library/std/src/sys/pal/wasip2/mod.rs | 1 - library/std/src/sys/pal/wasip2/net.rs | 417 ----------- library/std/src/sys/pal/wasm/mod.rs | 2 - library/std/src/sys/pal/windows/mod.rs | 3 +- library/std/src/sys/pal/windows/net.rs | 574 --------------- library/std/src/sys/pal/xous/mod.rs | 1 - library/std/src/sys/pal/xous/net/dns.rs | 128 ---- library/std/src/sys/pal/xous/net/mod.rs | 83 --- library/std/src/sys/pal/xous/net/tcplistener.rs | 243 ------- library/std/src/sys/pal/xous/net/tcpstream.rs | 424 ----------- library/std/src/sys/pal/xous/net/udp.rs | 459 ------------ library/std/src/sys/pal/zkvm/mod.rs | 2 - 46 files changed, 6027 insertions(+), 6158 deletions(-) create mode 100644 library/std/src/sys/net/connection/sgx.rs create mode 100644 library/std/src/sys/net/connection/socket.rs create mode 100644 library/std/src/sys/net/connection/socket/hermit.rs create mode 100644 library/std/src/sys/net/connection/socket/solid.rs create mode 100644 library/std/src/sys/net/connection/socket/tests.rs create mode 100644 library/std/src/sys/net/connection/socket/unix.rs create mode 100644 library/std/src/sys/net/connection/socket/wasip2.rs create mode 100644 library/std/src/sys/net/connection/socket/windows.rs create mode 100644 library/std/src/sys/net/connection/unsupported.rs create mode 100644 library/std/src/sys/net/connection/wasip1.rs create mode 100644 library/std/src/sys/net/connection/xous/dns.rs create mode 100644 library/std/src/sys/net/connection/xous/mod.rs create mode 100644 library/std/src/sys/net/connection/xous/tcplistener.rs create mode 100644 library/std/src/sys/net/connection/xous/tcpstream.rs create mode 100644 library/std/src/sys/net/connection/xous/udp.rs create mode 100644 library/std/src/sys/net/mod.rs delete mode 100644 library/std/src/sys/pal/hermit/net.rs delete mode 100644 library/std/src/sys/pal/sgx/net.rs delete mode 100644 library/std/src/sys/pal/solid/net.rs delete mode 100644 library/std/src/sys/pal/teeos/net.rs delete mode 100644 library/std/src/sys/pal/unix/l4re.rs delete mode 100644 library/std/src/sys/pal/unix/net.rs delete mode 100644 library/std/src/sys/pal/unsupported/net.rs delete mode 100644 library/std/src/sys/pal/wasi/net.rs delete mode 100644 library/std/src/sys/pal/wasip2/net.rs delete mode 100644 library/std/src/sys/pal/windows/net.rs delete mode 100644 library/std/src/sys/pal/xous/net/dns.rs delete mode 100644 library/std/src/sys/pal/xous/net/mod.rs delete mode 100644 library/std/src/sys/pal/xous/net/tcplistener.rs delete mode 100644 library/std/src/sys/pal/xous/net/tcpstream.rs delete mode 100644 library/std/src/sys/pal/xous/net/udp.rs (limited to 'library/std/src/sys') diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47dece..39a0bc6e337 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -12,6 +12,7 @@ pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; +pub mod net; pub mod os_str; pub mod path; pub mod random; diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs new file mode 100644 index 00000000000..b390a5eac5f --- /dev/null +++ b/library/std/src/sys/net/connection/sgx.rs @@ -0,0 +1,536 @@ +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; +use crate::sync::Arc; +use crate::sys::abi::usercalls; +use crate::sys::fd::FileDesc; +use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; +use crate::time::Duration; +use crate::{error, fmt}; + +const DEFAULT_FAKE_TTL: u32 = 64; + +#[derive(Debug, Clone)] +pub struct Socket { + inner: Arc, + local_addr: Option, +} + +impl Socket { + fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket { + Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) } + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.inner + } +} + +impl TryIntoInner for Socket { + fn try_into_inner(self) -> Result { + let Socket { inner, local_addr } = self; + Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) + } +} + +impl FromInner<(FileDesc, Option)> for Socket { + fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { + Socket { inner: Arc::new(inner), local_addr } + } +} + +#[derive(Clone)] +pub struct TcpStream { + inner: Socket, + peer_addr: Option, +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + if let Some(ref peer) = self.peer_addr { + res.field("peer", peer); + } + + res.field("fd", &self.inner.inner.as_inner()).finish() + } +} + +fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { + match result { + Ok(saddr) => Ok(saddr.to_string()), + // need to downcast twice because io::Error::into_inner doesn't return the original + // value if the conversion fails + Err(e) => { + if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { + Ok(e.into_inner().unwrap().downcast::().unwrap().host) + } else { + Err(e) + } + } + } +} + +fn addr_to_sockaddr(addr: Option<&str>) -> io::Result { + addr.ok_or(io::ErrorKind::AddrNotAvailable)? + .to_socket_addrs() + // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry + .map(|mut it| it.next().unwrap()) +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + } + + pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { + if dur == Duration::default() { + return Err(io::Error::ZERO_TIMEOUT); + } + Self::connect(Ok(addr)) // FIXME: ignoring timeout + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::ZERO_TIMEOUT); + } + _ => sgx_ineffective(()), + } + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + match dur { + Some(dur) if dur == Duration::default() => { + return Err(io::Error::ZERO_TIMEOUT); + } + _ => sgx_ineffective(()), + } + } + + pub fn read_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn write_timeout(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + Ok(0) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.inner.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.inner.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.inner.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.inner.inner.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.inner.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.inner.is_write_vectored() + } + + pub fn peer_addr(&self) -> io::Result { + addr_to_sockaddr(self.peer_addr.as_deref()) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(self.inner.local_addr.as_deref()) + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn linger(&self) -> io::Result> { + sgx_ineffective(None) + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn nodelay(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly +// reconstructed if `Socket::try_into_inner` fails. +impl IntoInner<(Socket, Option)> for TcpStream { + fn into_inner(self) -> (Socket, Option) { + (self.inner, self.peer_addr) + } +} + +impl FromInner<(Socket, Option)> for TcpStream { + fn from_inner((inner, peer_addr): (Socket, Option)) -> TcpStream { + TcpStream { inner, peer_addr } + } +} + +#[derive(Clone)] +pub struct TcpListener { + inner: Socket, +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Some(ref addr) = self.inner.local_addr { + res.field("addr", addr); + } + + res.field("fd", &self.inner.inner.as_inner()).finish() + } +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = io_err_to_addr(addr)?; + let (fd, local_addr) = usercalls::bind_stream(&addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + } + + pub fn socket_addr(&self) -> io::Result { + addr_to_sockaddr(self.inner.local_addr.as_deref()) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; + let peer_addr = Some(peer_addr); + let ret_peer = + addr_to_sockaddr(peer_addr.as_deref()).unwrap_or_else(|_| ([0; 4], 0).into()); + Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) + } + + pub fn duplicate(&self) -> io::Result { + Ok(self.clone()) + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn ttl(&self) -> io::Result { + sgx_ineffective(DEFAULT_FAKE_TTL) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } + + pub fn only_v6(&self) -> io::Result { + sgx_ineffective(false) + } + + pub fn take_error(&self) -> io::Result> { + Ok(None) + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + sgx_ineffective(()) + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +#[derive(Debug)] +pub struct NonIpSockAddr { + host: String, +} + +impl error::Error for NonIpSockAddr { + #[allow(deprecated)] + fn description(&self) -> &str { + "Failed to convert address to SocketAddr" + } +} + +impl fmt::Display for NonIpSockAddr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Failed to convert address to SocketAddr: {}", self.host) + } +} + +pub struct LookupHost(!); + +impl LookupHost { + fn new(host: String) -> io::Result { + Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) + } + + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(v: &str) -> io::Result { + LookupHost::new(v.to_owned()) + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + LookupHost::new(format!("{host}:{port}")) + } +} + +#[allow(bad_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + #[allow(dead_code)] + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + #[allow(dead_code)] + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } +} diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket.rs new file mode 100644 index 00000000000..6fe3430b53f --- /dev/null +++ b/library/std/src/sys/net/connection/socket.rs @@ -0,0 +1,780 @@ +#[cfg(test)] +mod tests; + +use crate::ffi::{c_int, c_void}; +use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, fmt, mem, ptr}; + +cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris", + target_os = "haiku", + target_os = "l4re", + target_os = "nto", + target_os = "nuttx", + target_vendor = "apple", + ))] { + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + } else { + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "linux", target_os = "android", + target_os = "hurd", + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos", + target_os = "haiku", target_os = "nto"))] { + use libc::MSG_NOSIGNAL; + } else { + const MSG_NOSIGNAL: c_int = 0x0; + } +} + +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "dragonfly", target_os = "freebsd", + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris", target_os = "illumos", + target_os = "nto"))] { + use crate::ffi::c_uchar; + type IpV4MultiCastType = c_uchar; + } else { + type IpV4MultiCastType = c_int; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// sockaddr and misc bindings +//////////////////////////////////////////////////////////////////////////////// + +pub fn setsockopt( + sock: &Socket, + level: c_int, + option_name: c_int, + option_value: T, +) -> io::Result<()> { + unsafe { + cvt(c::setsockopt( + sock.as_raw(), + level, + option_name, + (&raw const option_value) as *const _, + mem::size_of::() as c::socklen_t, + ))?; + Ok(()) + } +} + +pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { + unsafe { + let mut option_value: T = mem::zeroed(); + let mut option_len = mem::size_of::() as c::socklen_t; + cvt(c::getsockopt( + sock.as_raw(), + level, + option_name, + (&raw mut option_value) as *mut _, + &mut option_len, + ))?; + Ok(option_value) + } +} + +fn sockname(f: F) -> io::Result +where + F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> c_int, +{ + unsafe { + let mut storage: c::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as c::socklen_t; + cvt(f((&raw mut storage) as *mut _, &mut len))?; + sockaddr_to_addr(&storage, len as usize) + } +} + +pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { + match storage.ss_family as c_int { + c::AF_INET => { + assert!(len >= mem::size_of::()); + Ok(SocketAddr::V4(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in) + }))) + } + c::AF_INET6 => { + assert!(len >= mem::size_of::()); + Ok(SocketAddr::V6(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in6) + }))) + } + _ => Err(io::const_error!(ErrorKind::InvalidInput, "invalid argument")), + } +} + +#[cfg(target_os = "android")] +fn to_ipv6mr_interface(value: u32) -> c_int { + value as c_int +} + +#[cfg(not(target_os = "android"))] +fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { + value as crate::ffi::c_uint +} + +//////////////////////////////////////////////////////////////////////////////// +// get_host_addresses +//////////////////////////////////////////////////////////////////////////////// + +pub struct LookupHost { + original: *mut c::addrinfo, + cur: *mut c::addrinfo, + port: u16, +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + loop { + unsafe { + let cur = self.cur.as_ref()?; + self.cur = cur.ai_next; + match sockaddr_to_addr(mem::transmute(cur.ai_addr), cur.ai_addrlen as usize) { + Ok(addr) => return Some(addr), + Err(_) => continue, + } + } + } + } +} + +unsafe impl Sync for LookupHost {} +unsafe impl Send for LookupHost {} + +impl Drop for LookupHost { + fn drop(&mut self) { + unsafe { c::freeaddrinfo(self.original) } + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => { + match $e { + Some(r) => r, + None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), + } + }; + } + + // split the string by ':' and convert the second part to u16 + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + (host, port).try_into() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from((host, port): (&'a str, u16)) -> io::Result { + init(); + + run_with_cstr(host.as_bytes(), &|c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) + } + }) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP streams +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpStream { + inner: Socket, +} + +impl TcpStream { + pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect(addr)?; + Ok(TcpStream { inner: sock }) + } + + pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect_timeout(addr, timeout)?; + Ok(TcpStream { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.inner.read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.inner.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.inner.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.inner.shutdown(how) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpStream { inner: s }) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + self.inner.set_linger(linger) + } + + pub fn linger(&self) -> io::Result> { + self.inner.linger() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + self.inner.set_nodelay(nodelay) + } + + pub fn nodelay(&self) -> io::Result { + self.inner.nodelay() + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl AsInner for TcpStream { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + if let Ok(peer) = self.peer_addr() { + res.field("peer", &peer); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// TCP listeners +//////////////////////////////////////////////////////////////////////////////// + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_STREAM)?; + + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + + // Bind our new socket + let (addr, len) = addr.into_inner(); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + + cfg_if::cfg_if! { + if #[cfg(target_os = "horizon")] { + // The 3DS doesn't support a big connection backlog. Sometimes + // it allows up to about 37, but other times it doesn't even + // accept 32. There may be a global limitation causing this. + let backlog = 20; + } else if #[cfg(target_os = "haiku")] { + // Haiku does not support a queue length > 32 + // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 + let backlog = 32; + } else { + // The default for all other platforms + let backlog = 128; + } + } + + // Start listening + cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; + Ok(TcpListener { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut storage: c::sockaddr_storage = unsafe { mem::zeroed() }; + let mut len = mem::size_of_val(&storage) as c::socklen_t; + let sock = self.inner.accept((&raw mut storage) as *mut _, &mut len)?; + let addr = sockaddr_to_addr(&storage, len as usize)?; + Ok((TcpStream { inner: sock }, addr)) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| TcpListener { inner: s }) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY, only_v6 as c_int) + } + + pub fn only_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_V6ONLY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } +} + +impl FromInner for TcpListener { + fn from_inner(socket: Socket) -> TcpListener { + TcpListener { inner: socket } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("TcpListener"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// UDP +//////////////////////////////////////////////////////////////////////////////// + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { + let addr = addr?; + + init(); + + let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let (addr, len) = addr.into_inner(); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + Ok(UdpSocket { inner: sock }) + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } + + pub fn peer_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) + } + + pub fn socket_addr(&self) -> io::Result { + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.recv_from(buf) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.inner.peek_from(buf) + } + + pub fn send_to(&self, buf: &[u8], dst: &SocketAddr) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let (dst, dstlen) = dst.into_inner(); + let ret = cvt(unsafe { + c::sendto( + self.inner.as_raw(), + buf.as_ptr() as *const c_void, + len, + MSG_NOSIGNAL, + dst.as_ptr(), + dstlen, + ) + })?; + Ok(ret as usize) + } + + pub fn duplicate(&self) -> io::Result { + self.inner.duplicate().map(|s| UdpSocket { inner: s }) + } + + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_RCVTIMEO) + } + + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.inner.set_timeout(dur, c::SO_SNDTIMEO) + } + + pub fn read_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_RCVTIMEO) + } + + pub fn write_timeout(&self) -> io::Result> { + self.inner.timeout(c::SO_SNDTIMEO) + } + + pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { + setsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST, broadcast as c_int) + } + + pub fn broadcast(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::SOL_SOCKET, c::SO_BROADCAST)?; + Ok(raw != 0) + } + + pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_LOOP, + multicast_loop_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_loop_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { + setsockopt( + &self.inner, + c::IPPROTO_IP, + c::IP_MULTICAST_TTL, + multicast_ttl_v4 as IpV4MultiCastType, + ) + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + let raw: IpV4MultiCastType = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_MULTICAST_TTL)?; + Ok(raw as u32) + } + + pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP, multicast_loop_v6 as c_int) + } + + pub fn multicast_loop_v6(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IPV6, c::IPV6_MULTICAST_LOOP)?; + Ok(raw != 0) + } + + pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: multiaddr.into_inner(), + imr_interface: interface.into_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_ADD_MEMBERSHIP, mreq) + } + + pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: multiaddr.into_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { + let mreq = c::ip_mreq { + imr_multiaddr: multiaddr.into_inner(), + imr_interface: interface.into_inner(), + }; + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_DROP_MEMBERSHIP, mreq) + } + + pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { + let mreq = c::ipv6_mreq { + ipv6mr_multiaddr: multiaddr.into_inner(), + ipv6mr_interface: to_ipv6mr_interface(interface), + }; + setsockopt(&self.inner, c::IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, mreq) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + setsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL, ttl as c_int) + } + + pub fn ttl(&self) -> io::Result { + let raw: c_int = getsockopt(&self.inner, c::IPPROTO_IP, c::IP_TTL)?; + Ok(raw as u32) + } + + pub fn take_error(&self) -> io::Result> { + self.inner.take_error() + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.inner.set_nonblocking(nonblocking) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.inner.peek(buf) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { + let (addr, len) = addr?.into_inner(); + cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + } +} + +impl FromInner for UdpSocket { + fn from_inner(socket: Socket) -> UdpSocket { + UdpSocket { inner: socket } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut res = f.debug_struct("UdpSocket"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + let name = if cfg!(windows) { "socket" } else { "fd" }; + res.field(name, &self.inner.as_raw()).finish() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Converting SocketAddr to libc representation +//////////////////////////////////////////////////////////////////////////////// + +/// A type with the same memory layout as `c::sockaddr`. Used in converting Rust level +/// SocketAddr* types into their system representation. The benefit of this specific +/// type over using `c::sockaddr_storage` is that this type is exactly as large as it +/// needs to be and not a lot larger. And it can be initialized more cleanly from Rust. +#[repr(C)] +pub(crate) union SocketAddrCRepr { + v4: c::sockaddr_in, + v6: c::sockaddr_in6, +} + +impl SocketAddrCRepr { + pub fn as_ptr(&self) -> *const c::sockaddr { + self as *const _ as *const c::sockaddr + } +} + +impl<'a> IntoInner<(SocketAddrCRepr, c::socklen_t)> for &'a SocketAddr { + fn into_inner(self) -> (SocketAddrCRepr, c::socklen_t) { + match *self { + SocketAddr::V4(ref a) => { + let sockaddr = SocketAddrCRepr { v4: a.into_inner() }; + (sockaddr, mem::size_of::() as c::socklen_t) + } + SocketAddr::V6(ref a) => { + let sockaddr = SocketAddrCRepr { v6: a.into_inner() }; + (sockaddr, mem::size_of::() as c::socklen_t) + } + } + } +} diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs new file mode 100644 index 00000000000..42179dcc915 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -0,0 +1,346 @@ +#![allow(dead_code)] + +use core::ffi::c_int; + +pub(crate) use hermit_abi as netc; + +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::time::Instant; +pub use crate::sys::{cvt, cvt_r}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem}; + +#[expect(non_camel_case_types)] +pub type wrlen_t = usize; + +pub fn cvt_gai(err: i32) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + let detail = ""; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +#[derive(Debug)] +pub struct Socket(FileDesc); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: i32, ty: i32) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addr, len) = addr.into_inner(); + cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP rather than read readiness + if pollfd.revents & netc::POLLHUP != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result { + let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + pub fn duplicate(&self) -> io::Result { + let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; + Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.0.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut u8, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr(), + buf.len(), + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > netc::time_t::MAX as u64 { + netc::time_t::MAX + } else { + dur.as_secs() as netc::time_t + }; + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: i32) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as i32, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; + + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + let value: i32 = if nodelay { 1 } else { 0 }; + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) + } + + pub fn nodelay(&self) -> io::Result { + let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; + cvt(unsafe { + netc::ioctl( + self.as_raw_fd(), + netc::FIONBIO, + (&raw mut nonblocking) as *mut core::ffi::c_void, + ) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + unimplemented!() + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs new file mode 100644 index 00000000000..f85ecbb883e --- /dev/null +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -0,0 +1,413 @@ +use libc::{c_int, c_void, size_t}; + +use self::netc::{MSG_PEEK, sockaddr, socklen_t}; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem, ptr, str}; + +pub mod netc { + pub use crate::sys::abi::sockets::*; +} + +#[expect(non_camel_case_types)] +pub type wrlen_t = size_t; + +const fn max_iov() -> usize { + // Judging by the source code, it's unlimited, but specify a lower + // value just in case. + 1024 +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + Ok(()) + } else { + let msg: &dyn crate::fmt::Display = match err { + netc::EAI_NONAME => &"name or service not known", + netc::EAI_SERVICE => &"service not supported", + netc::EAI_FAIL => &"non-recoverable failure in name resolution", + netc::EAI_MEMORY => &"memory allocation failure", + netc::EAI_FAMILY => &"family not supported", + _ => &err, + }; + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {msg}")[..], + )) + } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} + +/// Returns the last error from the network subsystem. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) +} + +pub fn error_name(er: abi::ER) -> Option<&'static str> { + unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() +} + +#[inline] +pub fn is_interrupted(er: abi::ER) -> bool { + er == netc::SOLID_NET_ERR_BASE - libc::EINTR +} + +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { + let errno = netc::SOLID_NET_ERR_BASE - er; + match errno as libc::c_int { + libc::ECONNREFUSED => ErrorKind::ConnectionRefused, + libc::ECONNRESET => ErrorKind::ConnectionReset, + libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, + libc::EPIPE => ErrorKind::BrokenPipe, + libc::ENOTCONN => ErrorKind::NotConnected, + libc::ECONNABORTED => ErrorKind::ConnectionAborted, + libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + libc::EADDRINUSE => ErrorKind::AddrInUse, + libc::ENOENT => ErrorKind::NotFound, + libc::EINTR => ErrorKind::Interrupted, + libc::EINVAL => ErrorKind::InvalidInput, + libc::ETIMEDOUT => ErrorKind::TimedOut, + libc::EEXIST => ErrorKind::AlreadyExists, + libc::ENOSYS => ErrorKind::Unsupported, + libc::ENOMEM => ErrorKind::OutOfMemory, + libc::EAGAIN => ErrorKind::WouldBlock, + + _ => ErrorKind::Uncategorized, + } +} + +pub fn init() {} + +pub struct Socket(OwnedFd); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + let fd = cvt(netc::socket(fam, ty, 0))?; + Ok(Self::from_raw_fd(fd)) + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS + Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let mut timeout = + netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = netc::fd_set { num_fds: 1, fds: [self.as_raw_fd()] }; + + let mut writefds = fds; + let mut errorfds = fds; + + let n = unsafe { + cvt(netc::select( + self.as_raw_fd() + 1, + ptr::null_mut(), + &mut writefds, + &mut errorfds, + &mut timeout, + ))? + }; + + match n { + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + _ => { + let can_write = writefds.num_fds != 0; + if !can_write { + if let Some(e) = self.take_error()? { + return Err(e); + } + } + Ok(()) + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + unsafe { Ok(Self::from_raw_fd(fd)) } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::readv( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + &mut storage as *mut _ as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + netc::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const netc::iovec, + cmp::min(bufs.len(), max_iov()) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > netc::c_long::MAX as u64 { + netc::c_long::MAX + } else { + dur.as_secs() as netc::c_long + }; + let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = netc::linger { + l_onoff: linger.is_some() as netc::c_int, + l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, + }; + + setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + cvt(unsafe { + netc::ioctl(self.as_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) + }) + .map(drop) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This method is used by sys_common code to abstract over targets. + pub fn as_raw(&self) -> c_int { + self.as_raw_fd() + } +} + +impl FromInner for Socket { + #[inline] + fn from_inner(sock: OwnedFd) -> Socket { + Socket(sock) + } +} + +impl IntoInner for Socket { + #[inline] + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl AsFd for Socket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> c_int { + self.0.as_raw_fd() + } +} + +impl FromRawFd for Socket { + #[inline] + unsafe fn from_raw_fd(fd: c_int) -> Socket { + unsafe { Self(FromRawFd::from_raw_fd(fd)) } + } +} + +impl IntoRawFd for Socket { + #[inline] + fn into_raw_fd(self) -> c_int { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs new file mode 100644 index 00000000000..fc236b8027b --- /dev/null +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -0,0 +1,19 @@ +use super::*; +use crate::collections::HashMap; + +#[test] +fn no_lookup_host_duplicates() { + let mut addrs = HashMap::new(); + let lh = match LookupHost::try_from(("localhost", 0)) { + Ok(lh) => lh, + Err(e) => panic!("couldn't resolve `localhost`: {e}"), + }; + for sa in lh { + *addrs.entry(sa).or_insert(0) += 1; + } + assert_eq!( + addrs.iter().filter(|&(_, &v)| v > 1).collect::>(), + vec![], + "There should be no duplicate localhost entries" + ); +} diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs new file mode 100644 index 00000000000..da631605527 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -0,0 +1,651 @@ +use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; + +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use crate::{cmp, mem}; + +cfg_if::cfg_if! { + if #[cfg(target_vendor = "apple")] { + use libc::SO_LINGER_SEC as SO_LINGER; + } else { + use libc::SO_LINGER; + } +} + +pub(crate) use libc as netc; + +pub use crate::sys::{cvt, cvt_r}; + +#[expect(non_camel_case_types)] +pub type wrlen_t = size_t; + +pub struct Socket(FileDesc); + +pub fn init() {} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + // We may need to trigger a glibc workaround. See on_resolver_failure() for details. + on_resolver_failure(); + + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] + if err == libc::EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] + let detail = unsafe { + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() + }; + + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] + let detail = ""; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => libc::AF_INET, + SocketAddr::V6(..) => libc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + unsafe { + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "hurd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_os = "solaris", + ))] { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. + let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; + let socket = Socket(FileDesc::from_raw_fd(fd)); + + // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } else { + let fd = cvt(libc::socket(fam, ty, 0))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + let socket = Socket(fd); + + // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` + // flag to disable `SIGPIPE` emission on socket. + #[cfg(target_vendor = "apple")] + setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; + + Ok(socket) + } + } + } + } + + #[cfg(not(target_os = "vxworks"))] + pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { + unsafe { + let mut fds = [0, 0]; + + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + ))] { + // Like above, set cloexec atomically + cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; + Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) + } else { + cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; + let a = FileDesc::from_raw_fd(fds[0]); + let b = FileDesc::from_raw_fd(fds[1]); + a.set_cloexec()?; + b.set_cloexec()?; + Ok((Socket(a), Socket(b))) + } + } + } + } + + #[cfg(target_os = "vxworks")] + pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + loop { + let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; + if result.is_minus_one() { + let err = crate::sys::os::errno(); + match err { + libc::EINTR => continue, + libc::EISCONN => return Ok(()), + _ => return Err(io::Error::from_raw_os_error(err)), + } + } + return Ok(()); + } + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = unsafe { + let (addr, len) = addr.into_inner(); + cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), len)) + }; + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS :( + Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { libc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + if cfg!(target_os = "vxworks") { + // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the + // connection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = self.take_error()? { + return Err(e); + } + } else { + // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look + // for POLLHUP or POLLERR rather than read readiness + if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { + let e = self.take_error()?.unwrap_or_else(|| { + io::const_error!( + io::ErrorKind::Uncategorized, + "no error set after POLLHUP", + ) + }); + return Err(e); + } + } + + return Ok(()); + } + } + } + } + + pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { + // Unfortunately the only known way right now to accept a socket and + // atomically set the CLOEXEC flag is to use the `accept4` syscall on + // platforms that support it. On Linux, this was added in 2.6.28, + // glibc 2.10 and musl 0.9.5. + cfg_if::cfg_if! { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "hurd", + target_os = "netbsd", + target_os = "openbsd", + ))] { + unsafe { + let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; + Ok(Socket(FileDesc::from_raw_fd(fd))) + } + } else { + unsafe { + let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } + } + } + } + + pub fn duplicate(&self) -> io::Result { + self.0.duplicate().map(Socket) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + libc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; + + let n = cvt(unsafe { + libc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result { + let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; + Ok(n as usize) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, MSG_PEEK) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } + + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result { + let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; + Ok(n as usize) + } + + pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = if dur.as_secs() > libc::time_t::MAX as u64 { + libc::time_t::MAX + } else { + dur.as_secs() as libc::time_t + }; + let mut timeout = libc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as libc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => libc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, libc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: libc::c_int) -> io::Result> { + let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => libc::SHUT_WR, + Shutdown::Read => libc::SHUT_RD, + Shutdown::Both => libc::SHUT_RDWR, + }; + cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = libc::linger { + l_onoff: linger.is_some() as libc::c_int, + l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, + }; + + setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; + Ok(raw != 0) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn quickack(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; + Ok(raw != 0) + } + + // bionic libc makes no use of this flag + #[cfg(target_os = "linux")] + pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { + setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) + } + + #[cfg(target_os = "linux")] + pub fn deferaccept(&self) -> io::Result { + let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; + Ok(raw as u32) + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { + if !name.to_bytes().is_empty() { + const AF_NAME_MAX: usize = 16; + let mut buf = [0; AF_NAME_MAX]; + for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { + *dst = *src as libc::c_char; + } + let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; + arg.af_name = buf; + setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) + } else { + setsockopt( + self, + libc::SOL_SOCKET, + libc::SO_ACCEPTFILTER, + core::ptr::null_mut() as *mut c_void, + ) + } + } + + #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] + pub fn acceptfilter(&self) -> io::Result<&CStr> { + let arg: libc::accept_filter_arg = + getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; + let s: &[u8] = + unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; + let name = CStr::from_bytes_with_nul(s).unwrap(); + Ok(name) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { + setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) + } + + #[cfg(any(target_os = "android", target_os = "linux",))] + pub fn passcred(&self) -> io::Result { + let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; + Ok(passcred != 0) + } + + #[cfg(target_os = "netbsd")] + pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { + setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) + } + + #[cfg(target_os = "netbsd")] + pub fn local_creds(&self) -> io::Result { + let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; + Ok(local_creds != 0) + } + + #[cfg(target_os = "freebsd")] + pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { + setsockopt( + self, + libc::AF_LOCAL, + libc::LOCAL_CREDS_PERSISTENT, + local_creds_persistent as libc::c_int, + ) + } + + #[cfg(target_os = "freebsd")] + pub fn local_creds_persistent(&self) -> io::Result { + let local_creds_persistent: libc::c_int = + getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; + Ok(local_creds_persistent != 0) + } + + #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as libc::c_int; + cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) + } + + #[cfg(target_os = "vita")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let option = nonblocking as libc::c_int; + setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) + } + + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + // FIONBIO is inadequate for sockets on illumos/Solaris, so use the + // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. + self.0.set_nonblocking(nonblocking) + } + + #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] + pub fn set_mark(&self, mark: u32) -> io::Result<()> { + #[cfg(target_os = "linux")] + let option = libc::SO_MARK; + #[cfg(target_os = "freebsd")] + let option = libc::SO_USER_COOKIE; + #[cfg(target_os = "openbsd")] + let option = libc::SO_RTABLE; + setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} + +// In versions of glibc prior to 2.26, there's a bug where the DNS resolver +// will cache the contents of /etc/resolv.conf, so changes to that file on disk +// can be ignored by a long-running program. That can break DNS lookups on e.g. +// laptops where the network comes and goes. See +// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some +// distros including Debian have patched glibc to fix this for a long time. +// +// A workaround for this bug is to call the res_init libc function, to clear +// the cached configs. Unfortunately, while we believe glibc's implementation +// of res_init is thread-safe, we know that other implementations are not +// (https://github.com/rust-lang/rust/issues/43592). Code here in std could +// try to synchronize its res_init calls with a Mutex, but that wouldn't +// protect programs that call into libc in other ways. So instead of calling +// res_init unconditionally, we call it only when we detect we're linking +// against glibc version < 2.26. (That is, when we both know its needed and +// believe it's thread-safe). +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn on_resolver_failure() { + use crate::sys; + + // If the version fails to parse, we treat it the same as "not glibc". + if let Some(version) = sys::os::glibc_version() { + if version < (2, 26) { + unsafe { libc::res_init() }; + } + } +} + +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +fn on_resolver_failure() {} diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs new file mode 100644 index 00000000000..9d1c05a473e --- /dev/null +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -0,0 +1,417 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use libc::{c_int, c_void, size_t}; + +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::unsupported; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use crate::{cmp, mem, str}; + +pub extern crate libc as netc; + +#[allow(non_camel_case_types)] +pub type wrlen_t = size_t; + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt(t: T) -> crate::io::Result { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r(mut f: F) -> crate::io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + if err == netc::EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + }; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +pub struct WasiSocket(OwnedFd); + +pub struct Socket(WasiSocket); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS + Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // 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) = self.take_error()? { + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + core::ptr::addr_of_mut!(storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) + } + + fn write(&self, buf: &[u8]) -> io::Result { + let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, _linger: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner for WasiSocket { + #[inline] + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for WasiSocket { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for WasiSocket { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for WasiSocket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for WasiSocket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for WasiSocket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for WasiSocket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &WasiSocket { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiSocket { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(sock: WasiSocket) -> Socket { + Socket(sock) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs new file mode 100644 index 00000000000..80cf37eaf05 --- /dev/null +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -0,0 +1,575 @@ +#![unstable(issue = "none", feature = "windows_net")] + +use core::ffi::{c_int, c_long, c_ulong, c_ushort}; + +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, +}; +use crate::sync::OnceLock; +use crate::sys::c; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; +use crate::{cmp, mem, ptr, sys}; + +#[allow(non_camel_case_types)] +pub type wrlen_t = i32; + +pub mod netc { + //! BSD socket compatibility shim + //! + //! Some Windows API types are not quite what's expected by our cross-platform + //! net code. E.g. naming differences or different pointer types. + + use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; + + use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; + // re-exports from Windows API bindings. + pub use crate::sys::c::{ + ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IP_ADD_MEMBERSHIP, + IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, + IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, + SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, + SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, + getsockname, getsockopt, listen, setsockopt, + }; + + #[allow(non_camel_case_types)] + pub type socklen_t = c_int; + + pub const AF_INET: i32 = c::AF_INET as i32; + pub const AF_INET6: i32 = c::AF_INET6 as i32; + + // The following two structs use a union in the generated bindings but + // our cross-platform code expects a normal field so it's redefined here. + // As a consequence, we also need to redefine other structs that use this struct. + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[repr(C)] + pub struct ip_mreq { + pub imr_multiaddr: in_addr, + pub imr_interface: in_addr, + } + + #[repr(C)] + pub struct ipv6_mreq { + pub ipv6mr_multiaddr: in6_addr, + pub ipv6mr_interface: c_uint, + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: ADDRESS_FAMILY, + pub sin_port: c_ushort, + pub sin_addr: in_addr, + pub sin_zero: [c_char; 8], + } + + #[repr(C)] + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: ADDRESS_FAMILY, + pub sin6_port: c_ushort, + pub sin6_flowinfo: c_ulong, + pub sin6_addr: in6_addr, + pub sin6_scope_id: c_ulong, + } + + pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { + unsafe { c::send(socket, buf.cast::(), len, flags) } + } + pub unsafe fn sendto( + socket: SOCKET, + buf: *const c_void, + len: c_int, + flags: c_int, + addr: *const SOCKADDR, + addrlen: c_int, + ) -> c_int { + unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } + } + pub unsafe fn getaddrinfo( + node: *const c_char, + service: *const c_char, + hints: *const ADDRINFOA, + res: *mut *mut ADDRINFOA, + ) -> c_int { + unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } + } +} + +#[expect(missing_debug_implementations)] +pub struct Socket(OwnedSocket); + +static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); + +/// Checks whether the Windows socket interface has been started already, and +/// if not, starts it. +pub fn init() { + let _ = WSA_CLEANUP.get_or_init(|| unsafe { + let mut data: c::WSADATA = mem::zeroed(); + let ret = c::WSAStartup( + 0x202, // version 2.2 + &mut data, + ); + assert_eq!(ret, 0); + + // Only register `WSACleanup` if `WSAStartup` is actually ever called. + // Workaround to prevent linking to `WS2_32.dll` when no network functionality is used. + // See issue #85441. + c::WSACleanup + }); +} + +pub fn cleanup() { + // only perform cleanup if network functionality was actually initialized + if let Some(cleanup) = WSA_CLEANUP.get() { + unsafe { + cleanup(); + } + } +} + +/// Returns the last error from the Windows socket interface. +fn last_error() -> io::Error { + io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) +} + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) +/// and if so, returns the last error from the Windows socket interface. This +/// function must be called before another call to the socket API is made. +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) } +} + +/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { Ok(()) } else { Err(last_error()) } +} + +/// Just to provide the same interface as sys/pal/unix/net.rs +pub fn cvt_r(mut f: F) -> io::Result +where + T: IsMinusOne, + F: FnMut() -> T, +{ + cvt(f()) +} + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { + let family = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + let socket = unsafe { + c::WSASocketW( + family, + ty, + 0, + ptr::null_mut(), + 0, + c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, + ) + }; + + if socket != c::INVALID_SOCKET { + unsafe { Ok(Self::from_raw(socket)) } + } else { + let error = unsafe { c::WSAGetLastError() }; + + if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { + return Err(io::Error::from_raw_os_error(error)); + } + + let socket = + unsafe { c::WSASocketW(family, ty, 0, ptr::null_mut(), 0, c::WSA_FLAG_OVERLAPPED) }; + + if socket == c::INVALID_SOCKET { + return Err(last_error()); + } + + unsafe { + let socket = Self::from_raw(socket); + socket.0.set_no_inherit()?; + Ok(socket) + } + } + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; + cvt(result).map(drop) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let result = self.connect(addr); + self.set_nonblocking(false)?; + + match result { + Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let mut timeout = c::TIMEVAL { + tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, + tv_usec: timeout.subsec_micros() as c_long, + }; + + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + + let fds = { + let mut fds = unsafe { mem::zeroed::() }; + fds.fd_count = 1; + fds.fd_array[0] = self.as_raw(); + fds + }; + + let mut writefds = fds; + let mut errorfds = fds; + + let count = { + let result = unsafe { + c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout) + }; + cvt(result)? + }; + + match count { + 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), + _ => { + if writefds.fd_count != 1 { + if let Some(e) = self.take_error()? { + return Err(e); + } + } + + Ok(()) + } + } + } + _ => result, + } + } + + pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { + let socket = unsafe { c::accept(self.as_raw(), storage, len) }; + + match socket { + c::INVALID_SOCKET => Err(last_error()), + _ => unsafe { Ok(Self::from_raw(socket)) }, + } + } + + pub fn duplicate(&self) -> io::Result { + Ok(Self(self.0.try_clone()?)) + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; + let result = + unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; + + match result { + c::SOCKET_ERROR => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok(()) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + _ => { + unsafe { buf.advance_unchecked(result as usize) }; + Ok(()) + } + } + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; + let mut nread = 0; + let mut flags = 0; + let result = unsafe { + c::WSARecv( + self.as_raw(), + bufs.as_mut_ptr() as *mut c::WSABUF, + length, + &mut nread, + &mut flags, + ptr::null_mut(), + None, + ) + }; + + match result { + 0 => Ok(nread as usize), + _ => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok(0) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + } + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; + Ok(buf.len()) + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage = unsafe { mem::zeroed::() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; + + // On unix when a socket is shut down all further reads return 0, so we + // do the same on windows to map a shut down socket to returning EOF. + let result = unsafe { + c::recvfrom( + self.as_raw(), + buf.as_mut_ptr() as *mut _, + length, + flags, + (&raw mut storage) as *mut _, + &mut addrlen, + ) + }; + + match result { + c::SOCKET_ERROR => { + let error = unsafe { c::WSAGetLastError() }; + + if error == c::WSAESHUTDOWN { + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) + } else { + Err(io::Error::from_raw_os_error(error)) + } + } + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), + } + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, c::MSG_PEEK) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; + let mut nwritten = 0; + let result = unsafe { + c::WSASend( + self.as_raw(), + bufs.as_ptr() as *const c::WSABUF as *mut _, + length, + &mut nwritten, + 0, + ptr::null_mut(), + None, + ) + }; + cvt(result).map(|_| nwritten as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + let timeout = sys::dur2timeout(dur); + if timeout == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + timeout + } + None => 0, + }; + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result> { + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; + if raw == 0 { + Ok(None) + } else { + let secs = raw / 1000; + let nsec = (raw % 1000) * 1000000; + Ok(Some(Duration::new(secs as u64, nsec as u32))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => c::SD_SEND, + Shutdown::Read => c::SD_RECEIVE, + Shutdown::Both => c::SD_BOTH, + }; + let result = unsafe { c::shutdown(self.as_raw(), how) }; + cvt(result).map(drop) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_ulong; + let result = + unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) }; + cvt(result).map(drop) + } + + pub fn set_linger(&self, linger: Option) -> io::Result<()> { + let linger = c::LINGER { + l_onoff: linger.is_some() as c_ushort, + l_linger: linger.unwrap_or_default().as_secs() as c_ushort, + }; + + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + } + + pub fn linger(&self) -> io::Result> { + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + + Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + } + + pub fn nodelay(&self) -> io::Result { + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn take_error(&self) -> io::Result> { + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> c::SOCKET { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + self.as_inner().as_raw_socket() as c::SOCKET + } + pub unsafe fn from_raw(raw: c::SOCKET) -> Self { + debug_assert_eq!(mem::size_of::(), mem::size_of::()); + debug_assert_eq!(mem::align_of::(), mem::align_of::()); + unsafe { Self::from_raw_socket(raw as RawSocket) } + } +} + +#[unstable(reason = "not public", issue = "none", feature = "fd_read")] +impl<'a> Read for &'a Socket { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &OwnedSocket { + &self.0 + } +} + +impl FromInner for Socket { + fn from_inner(sock: OwnedSocket) -> Socket { + Socket(sock) + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> OwnedSocket { + self.0 + } +} + +impl AsSocket for Socket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for Socket { + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl IntoRawSocket for Socket { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl FromRawSocket for Socket { + unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { + unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } + } +} diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs new file mode 100644 index 00000000000..87e6106468f --- /dev/null +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -0,0 +1,369 @@ +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::sys::unsupported; +use crate::time::Duration; + +pub struct TcpStream(!); + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { + self.0 + } + + pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { + self.0 + } + + pub fn is_read_vectored(&self) -> bool { + self.0 + } + + pub fn write(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { + self.0 + } + + pub fn is_write_vectored(&self) -> bool { + self.0 + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn linger(&self) -> io::Result> { + self.0 + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn nodelay(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct TcpListener(!); + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn only_v6(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct UdpSocket(!); + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + self.0 + } + + pub fn socket_addr(&self) -> io::Result { + self.0 + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.0 + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + self.0 + } + + pub fn duplicate(&self) -> io::Result { + self.0 + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + self.0 + } + + pub fn read_timeout(&self) -> io::Result> { + self.0 + } + + pub fn write_timeout(&self) -> io::Result> { + self.0 + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn broadcast(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + self.0 + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn multicast_loop_v6(&self) -> io::Result { + self.0 + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + self.0 + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + self.0 + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + self.0 + } + + pub fn ttl(&self) -> io::Result { + self.0 + } + + pub fn take_error(&self) -> io::Result> { + self.0 + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + self.0 + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + self.0 + } + + pub fn send(&self, _: &[u8]) -> io::Result { + self.0 + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + self.0 + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + #[allow(dead_code)] + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + #[allow(dead_code)] + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } +} diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs new file mode 100644 index 00000000000..27e3a528af4 --- /dev/null +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -0,0 +1,542 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::Duration; + +pub struct Socket(WasiFd); + +pub struct TcpStream { + inner: Socket, +} + +impl AsInner for Socket { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(inner: WasiFd) -> Socket { + Socket(inner) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} + +impl TcpStream { + pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_vectored(&mut [IoSliceMut::new(buf)]) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.socket().as_inner().read_buf(buf) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + self.socket().as_inner().read(bufs) + } + + pub fn is_read_vectored(&self) -> bool { + true + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + self.write_vectored(&[IoSlice::new(buf)]) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + self.socket().as_inner().write(bufs) + } + + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let wasi_how = match how { + Shutdown::Read => wasi::SDFLAGS_RD, + Shutdown::Write => wasi::SDFLAGS_WR, + Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, + }; + + unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result> { + unsupported() + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn nodelay(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpStream { + fn from_inner(socket: Socket) -> TcpStream { + TcpStream { inner: socket } + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct TcpListener { + inner: Socket, +} + +impl TcpListener { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let fd = unsafe { + wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)? + }; + + Ok(( + TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }), + // WASI has no concept of SocketAddr yet + // return an unspecified IPv4Addr + SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), + )) + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn only_v6(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { + let fdstat = unsafe { + wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? + }; + + let mut flags = fdstat.fs_flags; + + if state { + flags |= wasi::FDFLAGS_NONBLOCK; + } else { + flags &= !wasi::FDFLAGS_NONBLOCK; + } + + unsafe { + wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) + .map_err(err2io) + } + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for TcpListener { + fn from_inner(inner: Socket) -> TcpListener { + TcpListener { inner } + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct UdpSocket { + inner: Socket, +} + +impl UdpSocket { + pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + unsupported() + } + + pub fn peer_addr(&self) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + unsupported() + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + unsupported() + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn duplicate(&self) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + unsupported() + } + + pub fn read_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn write_timeout(&self) -> io::Result> { + unsupported() + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn broadcast(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unsupported() + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn multicast_loop_v6(&self) -> io::Result { + unsupported() + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unsupported() + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + unsupported() + } + + pub fn ttl(&self) -> io::Result { + unsupported() + } + + pub fn take_error(&self) -> io::Result> { + unsupported() + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + unsupported() + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + unsupported() + } + + pub fn send(&self, _: &[u8]) -> io::Result { + unsupported() + } + + pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + unsupported() + } + + #[inline] + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner + } +} + +impl AsInner for UdpSocket { + #[inline] + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for UdpSocket { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner for UdpSocket { + fn from_inner(inner: Socket) -> UdpSocket { + UdpSocket { inner } + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() + } +} + +pub struct LookupHost(!); + +impl LookupHost { + pub fn port(&self) -> u16 { + self.0 + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + self.0 + } +} + +impl<'a> TryFrom<&'a str> for LookupHost { + type Error = io::Error; + + fn try_from(_v: &'a str) -> io::Result { + unsupported() + } +} + +impl<'a> TryFrom<(&'a str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(_v: (&'a str, u16)) -> io::Result { + unsupported() + } +} + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + #[allow(dead_code)] + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + #[allow(dead_code)] + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } +} diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs new file mode 100644 index 00000000000..ff6e49ed2d4 --- /dev/null +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -0,0 +1,128 @@ +use core::convert::{TryFrom, TryInto}; + +use crate::io; +use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::os::xous::ffi::lend_mut; +use crate::os::xous::services::{DnsLendMut, dns_server}; + +pub struct DnsError { + #[allow(dead_code)] + pub code: u8, +} + +#[repr(C, align(4096))] +struct LookupHostQuery([u8; 4096]); + +pub struct LookupHost { + data: LookupHostQuery, + port: u16, + offset: usize, + count: usize, +} + +impl LookupHost { + pub fn port(&self) -> u16 { + self.port + } +} + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + if self.offset >= self.data.0.len() { + return None; + } + match self.data.0.get(self.offset) { + Some(&4) => { + self.offset += 1; + if self.offset + 4 > self.data.0.len() { + return None; + } + let result = Some(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new( + self.data.0[self.offset], + self.data.0[self.offset + 1], + self.data.0[self.offset + 2], + self.data.0[self.offset + 3], + ), + self.port, + ))); + self.offset += 4; + result + } + Some(&6) => { + self.offset += 1; + if self.offset + 16 > self.data.0.len() { + return None; + } + let mut new_addr = [0u8; 16]; + for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] + .iter() + .zip(new_addr.iter_mut()) + { + *octet = *src; + } + let result = + Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0))); + self.offset += 16; + result + } + _ => None, + } + } +} + +pub fn lookup(query: &str, port: u16) -> Result { + let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; + + // Copy the query into the message that gets sent to the DNS server + for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) { + *result_byte = *query_byte; + } + + lend_mut( + dns_server(), + DnsLendMut::RawLookup.into(), + &mut result.data.0, + 0, + query.as_bytes().len(), + ) + .unwrap(); + if result.data.0[0] != 0 { + return Err(DnsError { code: result.data.0[1] }); + } + assert_eq!(result.offset, 0); + result.count = result.data.0[1] as usize; + + // Advance the offset to the first record + result.offset = 2; + Ok(result) +} + +impl TryFrom<&str> for LookupHost { + type Error = io::Error; + + fn try_from(s: &str) -> io::Result { + macro_rules! try_opt { + ($e:expr, $msg:expr) => { + match $e { + Some(r) => r, + None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), + } + }; + } + + // split the string by ':' and convert the second part to u16 + let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); + let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); + (host, port).try_into() + } +} + +impl TryFrom<(&str, u16)> for LookupHost { + type Error = io::Error; + + fn try_from(v: (&str, u16)) -> io::Result { + lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, &"DNS failure")) + } +} diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs new file mode 100644 index 00000000000..3e18ed24208 --- /dev/null +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -0,0 +1,83 @@ +mod dns; + +mod tcpstream; +pub use tcpstream::*; + +mod tcplistener; +pub use tcplistener::*; + +mod udp; +pub use udp::*; + +// this structure needs to be synchronized with what's in net/src/api.rs +#[repr(C)] +#[derive(Debug)] +enum NetError { + // Ok = 0, + Unaddressable = 1, + SocketInUse = 2, + // AccessDenied = 3, + Invalid = 4, + // Finished = 5, + LibraryError = 6, + // AlreadyUsed = 7, + TimedOut = 8, + WouldBlock = 9, +} + +#[repr(C, align(4096))] +struct ConnectRequest { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +struct SendData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct ReceiveData { + raw: [u8; 4096], +} + +#[repr(C, align(4096))] +pub struct GetAddress { + raw: [u8; 4096], +} + +pub use dns::LookupHost; + +#[allow(nonstandard_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + #[allow(dead_code)] + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + #[allow(dead_code)] + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } +} diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs new file mode 100644 index 00000000000..640a02a64f5 --- /dev/null +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -0,0 +1,243 @@ +use core::convert::TryInto; +use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; + +use super::*; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::os::xous::services; +use crate::sync::Arc; +use crate::{fmt, io}; + +macro_rules! unimpl { + () => { + return Err(io::const_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +#[derive(Clone)] +pub struct TcpListener { + fd: Arc, + local: SocketAddr, + handle_count: Arc, + nonblocking: Arc, +} + +impl TcpListener { + pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { + let mut addr = *socketaddr?; + + let fd = TcpListener::bind_inner(&mut addr)?; + return Ok(TcpListener { + fd: Arc::new(AtomicU16::new(fd)), + local: addr, + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }); + } + + /// This returns the raw fd of a Listener, so that it can also be used by the + /// accept routine to replenish the Listener object after its handle has been converted into + /// a TcpStream object. + fn bind_inner(addr: &mut SocketAddr) -> io::Result { + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + + let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpListen.into(), + &mut connect_request.raw, + 0, + 4096, + ) else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + }; + + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address")); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_error!( + io::ErrorKind::Other, + &"Unable to connect or internal error" + )); + } + } + let fd = response[1] as usize; + if addr.port() == 0 { + // oddly enough, this is a valid port and it means "give me something valid, up to you what that is" + let assigned_port = u16::from_le_bytes(response[2..4].try_into().unwrap()); + addr.set_port(assigned_port); + } + // println!("TcpListening with file handle of {}\r\n", fd); + Ok(fd.try_into().unwrap()) + } + + pub fn socket_addr(&self) -> io::Result { + Ok(self.local) + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + + if self.nonblocking.load(Ordering::Relaxed) { + // nonblocking + receive_request.raw[0] = 0; + } else { + // blocking + receive_request.raw[0] = 1; + } + + if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpAccept(self.fd.load(Ordering::Relaxed)).into(), + &mut receive_request.raw, + 0, + 0, + ) { + if receive_request.raw[0] != 0 { + // error case + if receive_request.raw[1] == NetError::TimedOut as u8 { + return Err(io::const_error!(io::ErrorKind::TimedOut, &"accept timed out",)); + } else if receive_request.raw[1] == NetError::WouldBlock as u8 { + return Err( + io::const_error!(io::ErrorKind::WouldBlock, &"accept would block",), + ); + } else if receive_request.raw[1] == NetError::LibraryError as u8 { + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + } + } else { + // accept successful + let rr = &receive_request.raw; + let stream_fd = u16::from_le_bytes(rr[1..3].try_into().unwrap()); + let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); + let addr = if rr[3] == 4 { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) + } else if rr[3] == 6 { + SocketAddr::new( + IpAddr::V6(Ipv6Addr::new( + u16::from_be_bytes(rr[4..6].try_into().unwrap()), + u16::from_be_bytes(rr[6..8].try_into().unwrap()), + u16::from_be_bytes(rr[8..10].try_into().unwrap()), + u16::from_be_bytes(rr[10..12].try_into().unwrap()), + u16::from_be_bytes(rr[12..14].try_into().unwrap()), + u16::from_be_bytes(rr[14..16].try_into().unwrap()), + u16::from_be_bytes(rr[16..18].try_into().unwrap()), + u16::from_be_bytes(rr[18..20].try_into().unwrap()), + )), + port, + ) + } else { + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + }; + + // replenish the listener + let mut local_copy = self.local.clone(); // port is non-0 by this time, but the method signature needs a mut + let new_fd = TcpListener::bind_inner(&mut local_copy)?; + self.fd.store(new_fd, Ordering::Relaxed); + + // now return a stream converted from the old stream's fd + Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr)) + } + } else { + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) + } + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn only_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.store(nonblocking, Ordering::Relaxed); + Ok(()) + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TCP listening on {:?}", self.local) + } +} + +impl Drop for TcpListener { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + crate::os::xous::services::NetBlockingScalar::StdTcpClose( + self.fd.load(Ordering::Relaxed), + ) + .into(), + ) + .unwrap(); + } + } +} diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs new file mode 100644 index 00000000000..572dd6b3b63 --- /dev/null +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -0,0 +1,424 @@ +use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; + +use super::*; +use crate::fmt; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::os::xous::services; +use crate::sync::Arc; +use crate::time::Duration; + +macro_rules! unimpl { + () => { + return Err(io::const_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +enum ReadOrPeek { + Read, + Peek, +} + +#[derive(Clone)] +pub struct TcpStream { + fd: u16, + local_port: u16, + remote_port: u16, + peer_addr: SocketAddr, + // milliseconds + read_timeout: Arc, + // milliseconds + write_timeout: Arc, + handle_count: Arc, + nonblocking: Arc, +} + +fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) { + // Construct the request. + let port_bytes = addr.port().to_le_bytes(); + buf[0] = port_bytes[0]; + buf[1] = port_bytes[1]; + for (dest, src) in buf[2..].iter_mut().zip((duration.as_millis() as u64).to_le_bytes()) { + *dest = src; + } + match addr.ip() { + IpAddr::V4(addr) => { + buf[10] = 4; + for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + buf[10] = 6; + for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } +} + +impl TcpStream { + pub(crate) fn from_listener( + fd: u16, + local_port: u16, + remote_port: u16, + peer_addr: SocketAddr, + ) -> TcpStream { + TcpStream { + fd, + local_port, + remote_port, + peer_addr, + read_timeout: Arc::new(AtomicU32::new(0)), + write_timeout: Arc::new(AtomicU32::new(0)), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + } + } + + pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result { + Self::connect_timeout(socketaddr?, Duration::ZERO) + } + + pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result { + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Construct the request. + sockaddr_to_buf(duration, &addr, &mut connect_request.raw); + + let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpConnect.into(), + &mut connect_request.raw, + 0, + 4096, + ) else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); + }; + + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + // errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly. + let errcode = response[0]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); + } else if errcode == NetError::Unaddressable as u8 { + return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address",)); + } else { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + &"Unable to connect or internal error", + )); + } + } + let fd = u16::from_le_bytes([response[2], response[3]]); + let local_port = u16::from_le_bytes([response[4], response[5]]); + let remote_port = u16::from_le_bytes([response[6], response[7]]); + // println!( + // "Connected with local port of {}, remote port of {}, file handle of {}", + // local_port, remote_port, fd + // ); + Ok(TcpStream { + fd, + local_port, + remote_port, + peer_addr: *addr, + read_timeout: Arc::new(AtomicU32::new(0)), + write_timeout: Arc::new(AtomicU32::new(0)), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(to) = timeout { + if to.is_zero() { + return Err(io::Error::ZERO_TIMEOUT); + } + } + self.read_timeout.store( + timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), + Ordering::Relaxed, + ); + Ok(()) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(to) = timeout { + if to.is_zero() { + return Err(io::Error::ZERO_TIMEOUT); + } + } + self.write_timeout.store( + timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), + Ordering::Relaxed, + ); + Ok(()) + } + + pub fn read_timeout(&self) -> io::Result> { + match self.read_timeout.load(Ordering::Relaxed) { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn write_timeout(&self) -> io::Result> { + match self.write_timeout.load(Ordering::Relaxed) { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + fn read_or_peek(&self, buf: &mut [u8], op: ReadOrPeek) -> io::Result { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + let data_to_read = buf.len().min(receive_request.raw.len()); + + let opcode = match op { + ReadOrPeek::Read => { + services::NetLendMut::StdTcpRx(self.fd, self.nonblocking.load(Ordering::Relaxed)) + } + ReadOrPeek::Peek => { + services::NetLendMut::StdTcpPeek(self.fd, self.nonblocking.load(Ordering::Relaxed)) + } + }; + + let Ok((offset, length)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + opcode.into(), + &mut receive_request.raw, + // Reuse the `offset` as the read timeout + self.read_timeout.load(Ordering::Relaxed) as usize, + data_to_read, + ) else { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + &"Library failure: wrong message type or messaging error" + )); + }; + + if offset != 0 { + for (dest, src) in buf.iter_mut().zip(receive_request.raw[..length].iter()) { + *dest = *src; + } + Ok(length) + } else { + let result = receive_request.raw; + if result[0] != 0 { + if result[1] == 8 { + // timed out + return Err(io::const_error!(io::ErrorKind::TimedOut, &"Timeout",)); + } + if result[1] == 9 { + // would block + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); + } + } + Err(io::const_error!(io::ErrorKind::Other, &"recv_slice failure")) + } + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.read_or_peek(buf, ReadOrPeek::Peek) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.read_or_peek(buf, ReadOrPeek::Read) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) + } + + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) + } + + pub fn is_read_vectored(&self) -> bool { + false + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + let mut send_request = SendData { raw: [0u8; 4096] }; + for (dest, src) in send_request.raw.iter_mut().zip(buf) { + *dest = *src; + } + let buf_len = send_request.raw.len().min(buf.len()); + + let (_offset, _valid) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdTcpTx(self.fd).into(), + &mut send_request.raw, + // Reuse the offset as the timeout + self.write_timeout.load(Ordering::Relaxed) as usize, + buf_len, + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; + + if send_request.raw[0] != 0 { + if send_request.raw[4] == 8 { + // timed out + return Err(io::const_error!( + io::ErrorKind::BrokenPipe, + &"Timeout or connection closed", + )); + } else if send_request.raw[4] == 9 { + // would block + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); + } else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Error when sending",)); + } + } + Ok(u32::from_le_bytes([ + send_request.raw[4], + send_request.raw[5], + send_request.raw[6], + send_request.raw[7], + ]) as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) + } + + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn peer_addr(&self) -> io::Result { + Ok(self.peer_addr) + } + + pub fn socket_addr(&self) -> io::Result { + let mut get_addr = GetAddress { raw: [0u8; 4096] }; + + let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdGetAddress(self.fd).into(), + &mut get_addr.raw, + 0, + 0, + ) else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")); + }; + let mut i = get_addr.raw.iter(); + match *i.next().unwrap() { + 4 => Ok(SocketAddr::V4(SocketAddrV4::new( + Ipv4Addr::new( + *i.next().unwrap(), + *i.next().unwrap(), + *i.next().unwrap(), + *i.next().unwrap(), + ), + self.local_port, + ))), + 6 => { + let mut new_addr = [0u8; 16]; + for (src, octet) in i.zip(new_addr.iter_mut()) { + *octet = *src; + } + Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) + } + _ => Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")), + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_linger(&self, _: Option) -> io::Result<()> { + unimpl!(); + } + + pub fn linger(&self) -> io::Result> { + unimpl!(); + } + + pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> { + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn nodelay(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetNodelay(self.fd).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] != 0)?) + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.store(nonblocking, Ordering::Relaxed); + Ok(()) + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "TCP connection to {:?} port {} to local port {}", + self.peer_addr, self.remote_port, self.local_port + ) + } +} + +impl Drop for TcpStream { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdTcpClose(self.fd).into(), + ) + .unwrap(); + } + } +} diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs new file mode 100644 index 00000000000..1b7ecac6d3a --- /dev/null +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -0,0 +1,459 @@ +use core::convert::TryInto; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use super::*; +use crate::cell::Cell; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::os::xous::services; +use crate::sync::Arc; +use crate::time::Duration; +use crate::{fmt, io}; + +macro_rules! unimpl { + () => { + return Err(io::const_error!( + io::ErrorKind::Unsupported, + &"This function is not yet implemented", + )); + }; +} + +#[derive(Clone)] +pub struct UdpSocket { + fd: u16, + local: SocketAddr, + remote: Cell>, + // in milliseconds. The setting applies only to `recv` calls after the timeout is set. + read_timeout: Cell, + // in milliseconds. The setting applies only to `send` calls after the timeout is set. + write_timeout: Cell, + handle_count: Arc, + nonblocking: Cell, +} + +impl UdpSocket { + pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { + let addr = socketaddr?; + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + + let response = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpBind.into(), + &mut connect_request.raw, + 0, + 4096, + ); + + if let Ok((_, valid)) = response { + // The first four bytes should be zero upon success, and will be nonzero + // for an error. + let response = connect_request.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + &"Port can't be 0 or invalid address" + )); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_error!( + io::ErrorKind::Other, + &"Unable to connect or internal error" + )); + } + } + let fd = response[1] as u16; + return Ok(UdpSocket { + fd, + local: *addr, + remote: Cell::new(None), + read_timeout: Cell::new(0), + write_timeout: Cell::new(0), + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Cell::new(false), + }); + } + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")) + } + + pub fn peer_addr(&self) -> io::Result { + match self.remote.get() { + Some(dest) => Ok(dest), + None => Err(io::const_error!(io::ErrorKind::NotConnected, &"No peer specified")), + } + } + + pub fn socket_addr(&self) -> io::Result { + Ok(self.local) + } + + fn recv_inner(&self, buf: &mut [u8], do_peek: bool) -> io::Result<(usize, SocketAddr)> { + let mut receive_request = ReceiveData { raw: [0u8; 4096] }; + + if self.nonblocking.get() { + // nonblocking + receive_request.raw[0] = 0; + } else { + // blocking + receive_request.raw[0] = 1; + for (&s, d) in self + .read_timeout + .get() + .to_le_bytes() + .iter() + .zip(receive_request.raw[1..9].iter_mut()) + { + *d = s; + } + } + if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpRx(self.fd).into(), + &mut receive_request.raw, + if do_peek { 1 } else { 0 }, + 0, + ) { + if receive_request.raw[0] != 0 { + // error case + if receive_request.raw[1] == NetError::TimedOut as u8 { + return Err(io::const_error!(io::ErrorKind::TimedOut, &"recv timed out",)); + } else if receive_request.raw[1] == NetError::WouldBlock as u8 { + return Err(io::const_error!(io::ErrorKind::WouldBlock, &"recv would block",)); + } else if receive_request.raw[1] == NetError::LibraryError as u8 { + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + } + } else { + let rr = &receive_request.raw; + let rxlen = u16::from_le_bytes(rr[1..3].try_into().unwrap()); + let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); + let addr = if rr[3] == 4 { + SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) + } else if rr[3] == 6 { + SocketAddr::new( + IpAddr::V6(Ipv6Addr::new( + u16::from_be_bytes(rr[4..6].try_into().unwrap()), + u16::from_be_bytes(rr[6..8].try_into().unwrap()), + u16::from_be_bytes(rr[8..10].try_into().unwrap()), + u16::from_be_bytes(rr[10..12].try_into().unwrap()), + u16::from_be_bytes(rr[12..14].try_into().unwrap()), + u16::from_be_bytes(rr[14..16].try_into().unwrap()), + u16::from_be_bytes(rr[16..18].try_into().unwrap()), + u16::from_be_bytes(rr[18..20].try_into().unwrap()), + )), + port, + ) + } else { + return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); + }; + for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) { + *d = s; + } + Ok((rxlen as usize, addr)) + } + } else { + Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) + } + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_inner(buf, false) + } + + pub fn recv(&self, buf: &mut [u8]) -> io::Result { + self.recv_from(buf).map(|(len, _addr)| len) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_inner(buf, true) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.peek_from(buf).map(|(len, _addr)| len) + } + + pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> { + let addr = maybe_addr?; + self.remote.set(Some(*addr)); + Ok(()) + } + + pub fn send(&self, buf: &[u8]) -> io::Result { + if let Some(addr) = self.remote.get() { + self.send_to(buf, &addr) + } else { + Err(io::const_error!(io::ErrorKind::NotConnected, &"No remote specified")) + } + } + + pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result { + let mut tx_req = SendData { raw: [0u8; 4096] }; + + // Construct the request. + let port_bytes = addr.port().to_le_bytes(); + tx_req.raw[0] = port_bytes[0]; + tx_req.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + tx_req.raw[2] = 4; + for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + tx_req.raw[2] = 6; + for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + } + let len = buf.len() as u16; + let len_bytes = len.to_le_bytes(); + tx_req.raw[19] = len_bytes[0]; + tx_req.raw[20] = len_bytes[1]; + for (&s, d) in buf.iter().zip(tx_req.raw[21..].iter_mut()) { + *d = s; + } + + // let buf = unsafe { + // xous::MemoryRange::new( + // &mut tx_req as *mut SendData as usize, + // core::mem::size_of::(), + // ) + // .unwrap() + // }; + + // write time-outs are implemented on the caller side. Basically, if the Net crate server + // is too busy to take the call immediately: retry, until the timeout is reached. + let now = crate::time::Instant::now(); + let write_timeout = if self.nonblocking.get() { + // nonblocking + core::time::Duration::ZERO + } else { + // blocking + if self.write_timeout.get() == 0 { + // forever + core::time::Duration::from_millis(u64::MAX) + } else { + // or this amount of time + core::time::Duration::from_millis(self.write_timeout.get()) + } + }; + loop { + let response = crate::os::xous::ffi::try_lend_mut( + services::net_server(), + services::NetLendMut::StdUdpTx(self.fd).into(), + &mut tx_req.raw, + 0, + 4096, + ); + match response { + Ok((_, valid)) => { + let response = &tx_req.raw; + if response[0] != 0 || valid == 0 { + let errcode = response[1]; + if errcode == NetError::SocketInUse as u8 { + return Err(io::const_error!( + io::ErrorKind::ResourceBusy, + &"Socket in use" + )); + } else if errcode == NetError::Invalid as u8 { + return Err(io::const_error!( + io::ErrorKind::InvalidInput, + &"Socket not valid" + )); + } else if errcode == NetError::LibraryError as u8 { + return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); + } else { + return Err(io::const_error!( + io::ErrorKind::Other, + &"Unable to connect" + )); + } + } else { + // no error + return Ok(len as usize); + } + } + Err(crate::os::xous::ffi::Error::ServerQueueFull) => { + if now.elapsed() >= write_timeout { + return Err(io::const_error!( + io::ErrorKind::WouldBlock, + &"Write timed out" + )); + } else { + // question: do we want to do something a bit more gentle than immediately retrying? + crate::thread::yield_now(); + } + } + _ => return Err(io::const_error!(io::ErrorKind::Other, &"Library error")), + } + } + } + + pub fn duplicate(&self) -> io::Result { + self.handle_count.fetch_add(1, Ordering::Relaxed); + Ok(self.clone()) + } + + pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(d) = timeout { + if d.is_zero() { + return Err(io::Error::ZERO_TIMEOUT); + } + } + self.read_timeout + .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); + Ok(()) + } + + pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { + if let Some(d) = timeout { + if d.is_zero() { + return Err(io::Error::ZERO_TIMEOUT); + } + } + self.write_timeout + .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); + Ok(()) + } + + pub fn read_timeout(&self) -> io::Result> { + match self.read_timeout.get() { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn write_timeout(&self) -> io::Result> { + match self.write_timeout.get() { + 0 => Ok(None), + t => Ok(Some(Duration::from_millis(t as u64))), + } + } + + pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { + if ttl > 255 { + return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); + } + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|_| ()) + } + + pub fn ttl(&self) -> io::Result { + Ok(crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(), + ) + .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) + .map(|res| res[0] as _)?) + } + + pub fn take_error(&self) -> io::Result> { + // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. + Ok(None) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.nonblocking.set(nonblocking); + Ok(()) + } + + // ------------- smoltcp base stack does not have multicast or broadcast support --------------- + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn broadcast(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + unimpl!(); + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + unimpl!(); + } + + pub fn multicast_loop_v6(&self) -> io::Result { + unimpl!(); + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unimpl!(); + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unimpl!(); + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { + unimpl!(); + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { + unimpl!(); + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UDP listening on {:?} to {:?}", self.local, self.remote.get(),) + } +} + +impl Drop for UdpSocket { + fn drop(&mut self) { + if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { + // only drop if we're the last clone + crate::os::xous::ffi::blocking_scalar( + services::net_server(), + services::NetBlockingScalar::StdUdpClose(self.fd).into(), + ) + .unwrap(); + } + } +} diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 00000000000..5aa197fbc0d --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,36 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c..032007aa4dc 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -24,7 +24,6 @@ pub mod fd; pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs deleted file mode 100644 index 4e12374203e..00000000000 --- a/library/std/src/sys/pal/hermit/net.rs +++ /dev/null @@ -1,347 +0,0 @@ -#![allow(dead_code)] - -use core::ffi::c_int; - -use super::fd::FileDesc; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; -use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; -use crate::{cmp, mem}; - -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - -pub type wrlen_t = usize; - -pub fn cvt_gai(err: i32) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - let detail = ""; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -#[derive(Debug)] -pub struct Socket(FileDesc); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: i32) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: i32, ty: i32) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn new_pair(_fam: i32, _ty: i32) -> io::Result<(Socket, Socket)> { - unimplemented!() - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(netc::connect(self.as_raw_fd(), addr.as_ptr(), len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(netc::errno::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP rather than read readiness - if pollfd.revents & netc::POLLHUP != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( - io::ErrorKind::Uncategorized, - "no error set after POLLHUP", - ) - }); - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt(unsafe { netc::accept(self.0.as_raw_fd(), storage, len) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - pub fn duplicate(&self) -> io::Result { - let fd = cvt(unsafe { netc::dup(self.0.as_raw_fd()) })?; - Ok(Socket(unsafe { FileDesc::from_raw_fd(fd) })) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: i32) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.0.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut u8, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr(), - buf.len(), - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > netc::time_t::MAX as u64 { - netc::time_t::MAX - } else { - dur.as_secs() as netc::time_t - }; - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: i32) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = netc::linger { - l_onoff: linger.is_some() as i32, - l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, - }; - - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - let value: i32 = if nodelay { 1 } else { 0 }; - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, value) - } - - pub fn nodelay(&self) -> io::Result { - let raw: i32 = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking: i32 = if nonblocking { 1 } else { 0 }; - cvt(unsafe { - netc::ioctl( - self.as_raw_fd(), - netc::FIONBIO, - (&raw mut nonblocking) as *mut core::ffi::c_void, - ) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - unimplemented!() - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc..0f5935d0c71 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -17,7 +17,6 @@ pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs deleted file mode 100644 index c966886d163..00000000000 --- a/library/std/src/sys/pal/sgx/net.rs +++ /dev/null @@ -1,536 +0,0 @@ -use super::abi::usercalls; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sync::Arc; -use crate::sys::fd::FileDesc; -use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; -use crate::time::Duration; -use crate::{error, fmt}; - -const DEFAULT_FAKE_TTL: u32 = 64; - -#[derive(Debug, Clone)] -pub struct Socket { - inner: Arc, - local_addr: Option, -} - -impl Socket { - fn new(fd: usercalls::raw::Fd, local_addr: String) -> Socket { - Socket { inner: Arc::new(FileDesc::new(fd)), local_addr: Some(local_addr) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.inner - } -} - -impl TryIntoInner for Socket { - fn try_into_inner(self) -> Result { - let Socket { inner, local_addr } = self; - Arc::try_unwrap(inner).map_err(|inner| Socket { inner, local_addr }) - } -} - -impl FromInner<(FileDesc, Option)> for Socket { - fn from_inner((inner, local_addr): (FileDesc, Option)) -> Socket { - Socket { inner: Arc::new(inner), local_addr } - } -} - -#[derive(Clone)] -pub struct TcpStream { - inner: Socket, - peer_addr: Option, -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpStream"); - - if let Some(ref addr) = self.inner.local_addr { - res.field("addr", addr); - } - - if let Some(ref peer) = self.peer_addr { - res.field("peer", peer); - } - - res.field("fd", &self.inner.inner.as_inner()).finish() - } -} - -fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { - match result { - Ok(saddr) => Ok(saddr.to_string()), - // need to downcast twice because io::Error::into_inner doesn't return the original - // value if the conversion fails - Err(e) => { - if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { - Ok(e.into_inner().unwrap().downcast::().unwrap().host) - } else { - Err(e) - } - } - } -} - -fn addr_to_sockaddr(addr: Option<&str>) -> io::Result { - addr.ok_or(io::ErrorKind::AddrNotAvailable)? - .to_socket_addrs() - // unwrap OK: if an iterator is returned, we're guaranteed to get exactly one entry - .map(|mut it| it.next().unwrap()) -} - -impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; - Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) - } - - pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { - if dur == Duration::default() { - return Err(io::Error::ZERO_TIMEOUT); - } - Self::connect(Ok(addr)) // FIXME: ignoring timeout - } - - pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - match dur { - Some(dur) if dur == Duration::default() => { - return Err(io::Error::ZERO_TIMEOUT); - } - _ => sgx_ineffective(()), - } - } - - pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - match dur { - Some(dur) if dur == Duration::default() => { - return Err(io::Error::ZERO_TIMEOUT); - } - _ => sgx_ineffective(()), - } - } - - pub fn read_timeout(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn write_timeout(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - Ok(0) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.inner.read(buf) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.inner.inner.read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.inner.inner.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.inner.inner.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.inner.inner.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.inner.inner.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.inner.inner.is_write_vectored() - } - - pub fn peer_addr(&self) -> io::Result { - addr_to_sockaddr(self.peer_addr.as_deref()) - } - - pub fn socket_addr(&self) -> io::Result { - addr_to_sockaddr(self.inner.local_addr.as_deref()) - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn duplicate(&self) -> io::Result { - Ok(self.clone()) - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn linger(&self) -> io::Result> { - sgx_ineffective(None) - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn nodelay(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpStream { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -// `Inner` includes `peer_addr` so that a `TcpStream` maybe correctly -// reconstructed if `Socket::try_into_inner` fails. -impl IntoInner<(Socket, Option)> for TcpStream { - fn into_inner(self) -> (Socket, Option) { - (self.inner, self.peer_addr) - } -} - -impl FromInner<(Socket, Option)> for TcpStream { - fn from_inner((inner, peer_addr): (Socket, Option)) -> TcpStream { - TcpStream { inner, peer_addr } - } -} - -#[derive(Clone)] -pub struct TcpListener { - inner: Socket, -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut res = f.debug_struct("TcpListener"); - - if let Some(ref addr) = self.inner.local_addr { - res.field("addr", addr); - } - - res.field("fd", &self.inner.inner.as_inner()).finish() - } -} - -impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr) = usercalls::bind_stream(&addr)?; - Ok(TcpListener { inner: Socket::new(fd, local_addr) }) - } - - pub fn socket_addr(&self) -> io::Result { - addr_to_sockaddr(self.inner.local_addr.as_deref()) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let (fd, local_addr, peer_addr) = usercalls::accept_stream(self.inner.inner.raw())?; - let peer_addr = Some(peer_addr); - let ret_peer = - addr_to_sockaddr(peer_addr.as_deref()).unwrap_or_else(|_| ([0; 4], 0).into()); - Ok((TcpStream { inner: Socket::new(fd, local_addr), peer_addr }, ret_peer)) - } - - pub fn duplicate(&self) -> io::Result { - Ok(self.clone()) - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn ttl(&self) -> io::Result { - sgx_ineffective(DEFAULT_FAKE_TTL) - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } - - pub fn only_v6(&self) -> io::Result { - sgx_ineffective(false) - } - - pub fn take_error(&self) -> io::Result> { - Ok(None) - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - sgx_ineffective(()) - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: Socket) -> TcpListener { - TcpListener { inner } - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -#[derive(Debug)] -pub struct NonIpSockAddr { - host: String, -} - -impl error::Error for NonIpSockAddr { - #[allow(deprecated)] - fn description(&self) -> &str { - "Failed to convert address to SocketAddr" - } -} - -impl fmt::Display for NonIpSockAddr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Failed to convert address to SocketAddr: {}", self.host) - } -} - -pub struct LookupHost(!); - -impl LookupHost { - fn new(host: String) -> io::Result { - Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) - } - - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - LookupHost::new(v.to_owned()) - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - LookupHost::new(format!("{host}:{port}")) - } -} - -#[allow(bad_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d..b399463c0c2 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be518..caf848a4e9b 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -24,7 +24,6 @@ pub mod env; pub(crate) mod error; pub mod fs; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +50,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/pal/solid/net.rs deleted file mode 100644 index 5f6436807e2..00000000000 --- a/library/std/src/sys/pal/solid/net.rs +++ /dev/null @@ -1,425 +0,0 @@ -use libc::{c_int, c_void, size_t}; - -use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{FromInner, IntoInner}; -use crate::time::Duration; -use crate::{cmp, mem, ptr, str}; - -pub mod netc { - pub use super::super::abi::sockets::*; -} - -pub type wrlen_t = size_t; - -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - -const fn max_iov() -> usize { - // Judging by the source code, it's unlimited, but specify a lower - // value just in case. - 1024 -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - Ok(()) - } else { - let msg: &dyn crate::fmt::Display = match err { - netc::EAI_NONAME => &"name or service not known", - netc::EAI_SERVICE => &"service not supported", - netc::EAI_FAIL => &"non-recoverable failure in name resolution", - netc::EAI_MEMORY => &"memory allocation failure", - netc::EAI_FAMILY => &"family not supported", - _ => &err, - }; - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {msg}")[..], - )) - } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - -/// Returns the last error from the network subsystem. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) -} - -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { - unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() -} - -#[inline] -pub fn is_interrupted(er: abi::ER) -> bool { - er == netc::SOLID_NET_ERR_BASE - libc::EINTR -} - -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { - let errno = netc::SOLID_NET_ERR_BASE - er; - match errno as libc::c_int { - libc::ECONNREFUSED => ErrorKind::ConnectionRefused, - libc::ECONNRESET => ErrorKind::ConnectionReset, - libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, - libc::EPIPE => ErrorKind::BrokenPipe, - libc::ENOTCONN => ErrorKind::NotConnected, - libc::ECONNABORTED => ErrorKind::ConnectionAborted, - libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - libc::EADDRINUSE => ErrorKind::AddrInUse, - libc::ENOENT => ErrorKind::NotFound, - libc::EINTR => ErrorKind::Interrupted, - libc::EINVAL => ErrorKind::InvalidInput, - libc::ETIMEDOUT => ErrorKind::TimedOut, - libc::EEXIST => ErrorKind::AlreadyExists, - libc::ENOSYS => ErrorKind::Unsupported, - libc::ENOMEM => ErrorKind::OutOfMemory, - libc::EAGAIN => ErrorKind::WouldBlock, - - _ => ErrorKind::Uncategorized, - } -} - -pub fn init() {} - -pub struct Socket(OwnedFd); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(netc::socket(fam, ty, 0))?; - Ok(Self::from_raw_fd(fd)) - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt(unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let mut timeout = - netc::timeval { tv_sec: timeout.as_secs() as _, tv_usec: timeout.subsec_micros() as _ }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - - let fds = netc::fd_set { num_fds: 1, fds: [self.as_raw_fd()] }; - - let mut writefds = fds; - let mut errorfds = fds; - - let n = unsafe { - cvt(netc::select( - self.as_raw_fd() + 1, - ptr::null_mut(), - &mut writefds, - &mut errorfds, - &mut timeout, - ))? - }; - - match n { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), - _ => { - let can_write = writefds.num_fds != 0; - if !can_write { - if let Some(e) = self.take_error()? { - return Err(e); - } - } - Ok(()) - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - unsafe { Ok(Self::from_raw_fd(fd)) } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv(self.as_raw_fd(), buf.as_mut().as_mut_ptr().cast(), buf.capacity(), flags) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::readv( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - netc::writev( - self.as_raw_fd(), - bufs.as_ptr() as *const netc::iovec, - cmp::min(bufs.len(), max_iov()) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > netc::c_long::MAX as u64 { - netc::c_long::MAX - } else { - dur.as_secs() as netc::c_long - }; - let mut timeout = netc::timeval { tv_sec: secs, tv_usec: dur.subsec_micros() as _ }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = netc::linger { - l_onoff: linger.is_some() as netc::c_int, - l_linger: linger.unwrap_or_default().as_secs() as netc::c_int, - }; - - setsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: netc::linger = getsockopt(self, netc::SOL_SOCKET, netc::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { - netc::ioctl(self.as_raw_fd(), netc::FIONBIO, (&mut nonblocking) as *mut c_int as _) - }) - .map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This method is used by sys_common code to abstract over targets. - pub fn as_raw(&self) -> c_int { - self.as_raw_fd() - } -} - -impl FromInner for Socket { - #[inline] - fn from_inner(sock: OwnedFd) -> Socket { - Socket(sock) - } -} - -impl IntoInner for Socket { - #[inline] - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl AsFd for Socket { - #[inline] - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> c_int { - self.0.as_raw_fd() - } -} - -impl FromRawFd for Socket { - #[inline] - unsafe fn from_raw_fd(fd: c_int) -> Socket { - unsafe { Self(FromRawFd::from_raw_fd(fd)) } - } -} - -impl IntoRawFd for Socket { - #[inline] - fn into_raw_fd(self) -> c_int { - self.0.into_raw_fd() - } -} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b19..a9904e66664 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -15,7 +15,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs deleted file mode 100644 index fed95205027..00000000000 --- a/library/std/src/sys/pal/teeos/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb..07025a304bf 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -19,8 +19,6 @@ pub mod fs; pub mod helpers; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c514..00000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d00..6862399b942 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -14,14 +14,8 @@ pub mod futex; pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs deleted file mode 100644 index d73b9fd5eb8..00000000000 --- a/library/std/src/sys/pal/unix/net.rs +++ /dev/null @@ -1,651 +0,0 @@ -use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; - -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem}; - -cfg_if::cfg_if! { - if #[cfg(target_vendor = "apple")] { - use libc::SO_LINGER_SEC as SO_LINGER; - } else { - use libc::SO_LINGER; - } -} - -pub use crate::sys::{cvt, cvt_r}; - -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - -pub type wrlen_t = size_t; - -pub struct Socket(FileDesc); - -pub fn init() {} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - // We may need to trigger a glibc workaround. See on_resolver_failure() for details. - on_resolver_failure(); - - #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] - if err == libc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] - let detail = unsafe { - // We can't always expect a UTF-8 environment. When we don't get that luxury, - // it's better to give a low-quality error message than none at all. - CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() - }; - - #[cfg(any(target_os = "espidf", target_os = "nuttx"))] - let detail = ""; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "hurd", - target_os = "linux", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - target_os = "solaris", - ))] { - // On platforms that support it we pass the SOCK_CLOEXEC - // flag to atomically create the socket and set it as - // CLOEXEC. On Linux this was added in 2.6.27. - let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - let socket = Socket(FileDesc::from_raw_fd(fd)); - - // DragonFlyBSD, FreeBSD and NetBSD use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "dragonfly"))] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } else { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - let socket = Socket(fd); - - // macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt` - // flag to disable `SIGPIPE` emission on socket. - #[cfg(target_vendor = "apple")] - setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?; - - Ok(socket) - } - } - } - } - - #[cfg(not(target_os = "vxworks"))] - pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { - unsafe { - let mut fds = [0, 0]; - - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "nto", - ))] { - // Like above, set cloexec atomically - cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; - Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) - } else { - cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; - let a = FileDesc::from_raw_fd(fds[0]); - let b = FileDesc::from_raw_fd(fds[1]); - a.set_cloexec()?; - b.set_cloexec()?; - Ok((Socket(a), Socket(b))) - } - } - } - } - - #[cfg(target_os = "vxworks")] - pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { - unimplemented!() - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - loop { - let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; - if result.is_minus_one() { - let err = crate::sys::os::errno(); - match err { - libc::EINTR => continue, - libc::EISCONN => return Ok(()), - _ => return Err(io::Error::from_raw_os_error(err)), - } - } - return Ok(()); - } - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addr, len) = addr.into_inner(); - cvt(libc::connect(self.as_raw_fd(), addr.as_ptr(), len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { libc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - if cfg!(target_os = "vxworks") { - // VxWorks poll does not return POLLHUP or POLLERR in revents. Check if the - // connection actually succeeded and return ok only when the socket is - // ready and no errors were found. - if let Some(e) = self.take_error()? { - return Err(e); - } - } else { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP or POLLERR rather than read readiness - if pollfd.revents & (libc::POLLHUP | libc::POLLERR) != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::const_error!( - io::ErrorKind::Uncategorized, - "no error set after POLLHUP", - ) - }); - return Err(e); - } - } - - return Ok(()); - } - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - // Unfortunately the only known way right now to accept a socket and - // atomically set the CLOEXEC flag is to use the `accept4` syscall on - // platforms that support it. On Linux, this was added in 2.6.28, - // glibc 2.10 and musl 0.9.5. - cfg_if::cfg_if! { - if #[cfg(any( - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "illumos", - target_os = "linux", - target_os = "hurd", - target_os = "netbsd", - target_os = "openbsd", - ))] { - unsafe { - let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; - Ok(Socket(FileDesc::from_raw_fd(fd))) - } - } else { - unsafe { - let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; - let fd = FileDesc::from_raw_fd(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) - } - } - } - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - libc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; - - let n = cvt(unsafe { - libc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; - Ok(n as usize) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - #[cfg(any(target_os = "android", target_os = "linux"))] - pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; - Ok(n as usize) - } - - pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = if dur.as_secs() > libc::time_t::MAX as u64 { - libc::time_t::MAX - } else { - dur.as_secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as libc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => libc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = libc::linger { - l_onoff: linger.is_some() as libc::c_int, - l_linger: linger.unwrap_or_default().as_secs() as libc::c_int, - }; - - setsockopt(self, libc::SOL_SOCKET, SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: libc::linger = getsockopt(self, libc::SOL_SOCKET, SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; - Ok(raw != 0) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn set_quickack(&self, quickack: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK, quickack as c_int) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn quickack(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_QUICKACK)?; - Ok(raw != 0) - } - - // bionic libc makes no use of this flag - #[cfg(target_os = "linux")] - pub fn set_deferaccept(&self, accept: u32) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT, accept as c_int) - } - - #[cfg(target_os = "linux")] - pub fn deferaccept(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_DEFER_ACCEPT)?; - Ok(raw as u32) - } - - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] - pub fn set_acceptfilter(&self, name: &CStr) -> io::Result<()> { - if !name.to_bytes().is_empty() { - const AF_NAME_MAX: usize = 16; - let mut buf = [0; AF_NAME_MAX]; - for (src, dst) in name.to_bytes().iter().zip(&mut buf[..AF_NAME_MAX - 1]) { - *dst = *src as libc::c_char; - } - let mut arg: libc::accept_filter_arg = unsafe { mem::zeroed() }; - arg.af_name = buf; - setsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER, &mut arg) - } else { - setsockopt( - self, - libc::SOL_SOCKET, - libc::SO_ACCEPTFILTER, - core::ptr::null_mut() as *mut c_void, - ) - } - } - - #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] - pub fn acceptfilter(&self) -> io::Result<&CStr> { - let arg: libc::accept_filter_arg = - getsockopt(self, libc::SOL_SOCKET, libc::SO_ACCEPTFILTER)?; - let s: &[u8] = - unsafe { core::slice::from_raw_parts(arg.af_name.as_ptr() as *const u8, 16) }; - let name = CStr::from_bytes_with_nul(s).unwrap(); - Ok(name) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn set_passcred(&self, passcred: bool) -> io::Result<()> { - setsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED, passcred as libc::c_int) - } - - #[cfg(any(target_os = "android", target_os = "linux",))] - pub fn passcred(&self) -> io::Result { - let passcred: libc::c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_PASSCRED)?; - Ok(passcred != 0) - } - - #[cfg(target_os = "netbsd")] - pub fn set_local_creds(&self, local_creds: bool) -> io::Result<()> { - setsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS, local_creds as libc::c_int) - } - - #[cfg(target_os = "netbsd")] - pub fn local_creds(&self) -> io::Result { - let local_creds: libc::c_int = getsockopt(self, 0 as libc::c_int, libc::LOCAL_CREDS)?; - Ok(local_creds != 0) - } - - #[cfg(target_os = "freebsd")] - pub fn set_local_creds_persistent(&self, local_creds_persistent: bool) -> io::Result<()> { - setsockopt( - self, - libc::AF_LOCAL, - libc::LOCAL_CREDS_PERSISTENT, - local_creds_persistent as libc::c_int, - ) - } - - #[cfg(target_os = "freebsd")] - pub fn local_creds_persistent(&self) -> io::Result { - let local_creds_persistent: libc::c_int = - getsockopt(self, libc::AF_LOCAL, libc::LOCAL_CREDS_PERSISTENT)?; - Ok(local_creds_persistent != 0) - } - - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "vita")))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) - } - - #[cfg(target_os = "vita")] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let option = nonblocking as libc::c_int; - setsockopt(self, libc::SOL_SOCKET, libc::SO_NONBLOCK, option) - } - - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - // FIONBIO is inadequate for sockets on illumos/Solaris, so use the - // fcntl(F_[GS]ETFL)-based method provided by FileDesc instead. - self.0.set_nonblocking(nonblocking) - } - - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] - pub fn set_mark(&self, mark: u32) -> io::Result<()> { - #[cfg(target_os = "linux")] - let option = libc::SO_MARK; - #[cfg(target_os = "freebsd")] - let option = libc::SO_USER_COOKIE; - #[cfg(target_os = "openbsd")] - let option = libc::SO_RTABLE; - setsockopt(self, libc::SOL_SOCKET, option, mark as libc::c_int) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Self { - Self(file_desc) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } -} - -// In versions of glibc prior to 2.26, there's a bug where the DNS resolver -// will cache the contents of /etc/resolv.conf, so changes to that file on disk -// can be ignored by a long-running program. That can break DNS lookups on e.g. -// laptops where the network comes and goes. See -// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some -// distros including Debian have patched glibc to fix this for a long time. -// -// A workaround for this bug is to call the res_init libc function, to clear -// the cached configs. Unfortunately, while we believe glibc's implementation -// of res_init is thread-safe, we know that other implementations are not -// (https://github.com/rust-lang/rust/issues/43592). Code here in std could -// try to synchronize its res_init calls with a Mutex, but that wouldn't -// protect programs that call into libc in other ways. So instead of calling -// res_init unconditionally, we call it only when we detect we're linking -// against glibc version < 2.26. (That is, when we both know its needed and -// believe it's thread-safe). -#[cfg(all(target_os = "linux", target_env = "gnu"))] -fn on_resolver_failure() { - use crate::sys; - - // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { - if version < (2, 26) { - unsafe { libc::res_init() }; - } - } -} - -#[cfg(not(all(target_os = "linux", target_env = "gnu")))] -fn on_resolver_failure() {} diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568..4941dd4918c 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -4,7 +4,6 @@ pub mod args; pub mod env; pub mod fs; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/pal/unsupported/net.rs deleted file mode 100644 index 87e6106468f..00000000000 --- a/library/std/src/sys/pal/unsupported/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 361802d101d..312ad28ab51 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -45,5 +44,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/pal/wasi/net.rs deleted file mode 100644 index a6486799828..00000000000 --- a/library/std/src/sys/pal/wasi/net.rs +++ /dev/null @@ -1,543 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use super::err2io; -use super::fd::WasiFd; -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; - -pub struct Socket(WasiFd); - -pub struct TcpStream { - inner: Socket, -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiFd { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(inner: WasiFd) -> Socket { - Socket(inner) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.socket().as_inner().read_buf(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.socket().as_inner().read(bufs) - } - - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.socket().as_inner().write(bufs) - } - - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let wasi_how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_RD | wasi::SDFLAGS_WR, - }; - - unsafe { wasi::sock_shutdown(self.socket().as_raw_fd() as _, wasi_how).map_err(err2io) } - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn nodelay(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { - let fdstat = unsafe { - wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? - }; - - let mut flags = fdstat.fs_flags; - - if state { - flags |= wasi::FDFLAGS_NONBLOCK; - } else { - flags &= !wasi::FDFLAGS_NONBLOCK; - } - - unsafe { - wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) - .map_err(err2io) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct TcpListener { - inner: Socket, -} - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let fd = unsafe { - wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)? - }; - - Ok(( - TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }), - // WASI has no concept of SocketAddr yet - // return an unspecified IPv4Addr - SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0), - )) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn only_v6(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, state: bool) -> io::Result<()> { - let fdstat = unsafe { - wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)? - }; - - let mut flags = fdstat.fs_flags; - - if state { - flags |= wasi::FDFLAGS_NONBLOCK; - } else { - flags &= !wasi::FDFLAGS_NONBLOCK; - } - - unsafe { - wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags) - .map_err(err2io) - } - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for TcpListener { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for TcpListener { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for TcpListener { - fn from_inner(inner: Socket) -> TcpListener { - TcpListener { inner } - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct UdpSocket { - inner: Socket, -} - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - unsupported() - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unsupported() - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unsupported() - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unsupported() - } - - pub fn read_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn write_timeout(&self) -> io::Result> { - unsupported() - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn broadcast(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unsupported() - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unsupported() - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unsupported() - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unsupported() - } - - pub fn ttl(&self) -> io::Result { - unsupported() - } - - pub fn take_error(&self) -> io::Result> { - unsupported() - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unsupported() - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unsupported() - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unsupported() - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unsupported() - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } -} - -impl AsInner for UdpSocket { - #[inline] - fn as_inner(&self) -> &Socket { - &self.inner - } -} - -impl IntoInner for UdpSocket { - fn into_inner(self) -> Socket { - self.inner - } -} - -impl FromInner for UdpSocket { - fn from_inner(inner: Socket) -> UdpSocket { - UdpSocket { inner } - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9..234e946d3b8 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -20,7 +20,6 @@ pub mod futex; #[path = "../wasi/io.rs"] pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/pal/wasip2/net.rs deleted file mode 100644 index f009a51821f..00000000000 --- a/library/std/src/sys/pal/wasip2/net.rs +++ /dev/null @@ -1,417 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -use libc::{c_int, c_void, size_t}; - -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem, str}; - -pub extern crate libc as netc; - -#[allow(non_camel_case_types)] -pub type wrlen_t = size_t; - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - if err == netc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -pub struct WasiSocket(OwnedFd); - -pub struct Socket(WasiSocket); - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // 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) = self.take_error()? { - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::default_read_vectored(|b| self.read(b), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - false - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - core::ptr::addr_of_mut!(storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - io::default_write_vectored(|b| self.write(b), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, netc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, _linger: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for WasiSocket { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for WasiSocket { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner for WasiSocket { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(owned_fd) - } -} - -impl AsFd for WasiSocket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for WasiSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for WasiSocket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for WasiSocket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiSocket { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiSocket { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: WasiSocket) -> Socket { - Socket(sock) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 41fe019f110..1280f353200 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb5493..f9aa049ca9a 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod handle; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +62,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs deleted file mode 100644 index a92853c642c..00000000000 --- a/library/std/src/sys/pal/windows/net.rs +++ /dev/null @@ -1,574 +0,0 @@ -#![unstable(issue = "none", feature = "windows_net")] - -use core::ffi::{c_int, c_long, c_ulong, c_ushort}; - -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::windows::io::{ - AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, -}; -use crate::sync::OnceLock; -use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; -use crate::time::Duration; -use crate::{cmp, mem, ptr, sys}; - -#[allow(non_camel_case_types)] -pub type wrlen_t = i32; - -pub mod netc { - //! BSD socket compatibility shim - //! - //! Some Windows API types are not quite what's expected by our cross-platform - //! net code. E.g. naming differences or different pointer types. - - use core::ffi::{c_char, c_int, c_uint, c_ulong, c_ushort, c_void}; - - use crate::sys::c::{self, ADDRESS_FAMILY, ADDRINFOA, SOCKADDR, SOCKET}; - // re-exports from Windows API bindings. - pub use crate::sys::c::{ - ADDRESS_FAMILY as sa_family_t, ADDRINFOA as addrinfo, IP_ADD_MEMBERSHIP, - IP_DROP_MEMBERSHIP, IP_MULTICAST_LOOP, IP_MULTICAST_TTL, IP_TTL, IPPROTO_IP, IPPROTO_IPV6, - IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_MULTICAST_LOOP, IPV6_V6ONLY, SO_BROADCAST, - SO_RCVTIMEO, SO_SNDTIMEO, SOCK_DGRAM, SOCK_STREAM, SOCKADDR as sockaddr, - SOCKADDR_STORAGE as sockaddr_storage, SOL_SOCKET, bind, connect, freeaddrinfo, getpeername, - getsockname, getsockopt, listen, setsockopt, - }; - - #[allow(non_camel_case_types)] - pub type socklen_t = c_int; - - pub const AF_INET: i32 = c::AF_INET as i32; - pub const AF_INET6: i32 = c::AF_INET6 as i32; - - // The following two structs use a union in the generated bindings but - // our cross-platform code expects a normal field so it's redefined here. - // As a consequence, we also need to redefine other structs that use this struct. - #[repr(C)] - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[repr(C)] - pub struct ip_mreq { - pub imr_multiaddr: in_addr, - pub imr_interface: in_addr, - } - - #[repr(C)] - pub struct ipv6_mreq { - pub ipv6mr_multiaddr: in6_addr, - pub ipv6mr_interface: c_uint, - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: ADDRESS_FAMILY, - pub sin_port: c_ushort, - pub sin_addr: in_addr, - pub sin_zero: [c_char; 8], - } - - #[repr(C)] - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: ADDRESS_FAMILY, - pub sin6_port: c_ushort, - pub sin6_flowinfo: c_ulong, - pub sin6_addr: in6_addr, - pub sin6_scope_id: c_ulong, - } - - pub unsafe fn send(socket: SOCKET, buf: *const c_void, len: c_int, flags: c_int) -> c_int { - unsafe { c::send(socket, buf.cast::(), len, flags) } - } - pub unsafe fn sendto( - socket: SOCKET, - buf: *const c_void, - len: c_int, - flags: c_int, - addr: *const SOCKADDR, - addrlen: c_int, - ) -> c_int { - unsafe { c::sendto(socket, buf.cast::(), len, flags, addr, addrlen) } - } - pub unsafe fn getaddrinfo( - node: *const c_char, - service: *const c_char, - hints: *const ADDRINFOA, - res: *mut *mut ADDRINFOA, - ) -> c_int { - unsafe { c::getaddrinfo(node.cast::(), service.cast::(), hints, res) } - } -} - -pub struct Socket(OwnedSocket); - -static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); - -/// Checks whether the Windows socket interface has been started already, and -/// if not, starts it. -pub fn init() { - let _ = WSA_CLEANUP.get_or_init(|| unsafe { - let mut data: c::WSADATA = mem::zeroed(); - let ret = c::WSAStartup( - 0x202, // version 2.2 - &mut data, - ); - assert_eq!(ret, 0); - - // Only register `WSACleanup` if `WSAStartup` is actually ever called. - // Workaround to prevent linking to `WS2_32.dll` when no network functionality is used. - // See issue #85441. - c::WSACleanup - }); -} - -pub fn cleanup() { - // only perform cleanup if network functionality was actually initialized - if let Some(cleanup) = WSA_CLEANUP.get() { - unsafe { - cleanup(); - } - } -} - -/// Returns the last error from the Windows socket interface. -fn last_error() -> io::Error { - io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) -} - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -/// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) -/// and if so, returns the last error from the Windows socket interface. This -/// function must be called before another call to the socket API is made. -pub fn cvt(t: T) -> io::Result { - if t.is_minus_one() { Err(last_error()) } else { Ok(t) } -} - -/// A variant of `cvt` for `getaddrinfo` which return 0 for a success. -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { Ok(()) } else { Err(last_error()) } -} - -/// Just to provide the same interface as sys/pal/unix/net.rs -pub fn cvt_r(mut f: F) -> io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - cvt(f()) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let family = match *addr { - SocketAddr::V4(..) => netc::AF_INET, - SocketAddr::V6(..) => netc::AF_INET6, - }; - let socket = unsafe { - c::WSASocketW( - family, - ty, - 0, - ptr::null_mut(), - 0, - c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT, - ) - }; - - if socket != c::INVALID_SOCKET { - unsafe { Ok(Self::from_raw(socket)) } - } else { - let error = unsafe { c::WSAGetLastError() }; - - if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL { - return Err(io::Error::from_raw_os_error(error)); - } - - let socket = - unsafe { c::WSASocketW(family, ty, 0, ptr::null_mut(), 0, c::WSA_FLAG_OVERLAPPED) }; - - if socket == c::INVALID_SOCKET { - return Err(last_error()); - } - - unsafe { - let socket = Self::from_raw(socket); - socket.0.set_no_inherit()?; - Ok(socket) - } - } - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = addr.into_inner(); - let result = unsafe { c::connect(self.as_raw(), addr.as_ptr(), len) }; - cvt(result).map(drop) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let result = self.connect(addr); - self.set_nonblocking(false)?; - - match result { - Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => { - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let mut timeout = c::TIMEVAL { - tv_sec: cmp::min(timeout.as_secs(), c_long::MAX as u64) as c_long, - tv_usec: timeout.subsec_micros() as c_long, - }; - - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - - let fds = { - let mut fds = unsafe { mem::zeroed::() }; - fds.fd_count = 1; - fds.fd_array[0] = self.as_raw(); - fds - }; - - let mut writefds = fds; - let mut errorfds = fds; - - let count = { - let result = unsafe { - c::select(1, ptr::null_mut(), &mut writefds, &mut errorfds, &timeout) - }; - cvt(result)? - }; - - match count { - 0 => Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")), - _ => { - if writefds.fd_count != 1 { - if let Some(e) = self.take_error()? { - return Err(e); - } - } - - Ok(()) - } - } - } - _ => result, - } - } - - pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { - let socket = unsafe { c::accept(self.as_raw(), storage, len) }; - - match socket { - c::INVALID_SOCKET => Err(last_error()), - _ => unsafe { Ok(Self::from_raw(socket)) }, - } - } - - pub fn duplicate(&self) -> io::Result { - Ok(Self(self.0.try_clone()?)) - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(buf.capacity(), i32::MAX as usize) as i32; - let result = - unsafe { c::recv(self.as_raw(), buf.as_mut().as_mut_ptr() as *mut _, length, flags) }; - - match result { - c::SOCKET_ERROR => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok(()) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - _ => { - unsafe { buf.advance_unchecked(result as usize) }; - Ok(()) - } - } - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; - let mut nread = 0; - let mut flags = 0; - let result = unsafe { - c::WSARecv( - self.as_raw(), - bufs.as_mut_ptr() as *mut c::WSABUF, - length, - &mut nread, - &mut flags, - ptr::null_mut(), - None, - ) - }; - - match result { - 0 => Ok(nread as usize), - _ => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok(0) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - } - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), c::MSG_PEEK)?; - Ok(buf.len()) - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage = unsafe { mem::zeroed::() }; - let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; - let length = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - - // On unix when a socket is shut down all further reads return 0, so we - // do the same on windows to map a shut down socket to returning EOF. - let result = unsafe { - c::recvfrom( - self.as_raw(), - buf.as_mut_ptr() as *mut _, - length, - flags, - (&raw mut storage) as *mut _, - &mut addrlen, - ) - }; - - match result { - c::SOCKET_ERROR => { - let error = unsafe { c::WSAGetLastError() }; - - if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) - } else { - Err(io::Error::from_raw_os_error(error)) - } - } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), - } - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, c::MSG_PEEK) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let length = cmp::min(bufs.len(), u32::MAX as usize) as u32; - let mut nwritten = 0; - let result = unsafe { - c::WSASend( - self.as_raw(), - bufs.as_ptr() as *const c::WSABUF as *mut _, - length, - &mut nwritten, - 0, - ptr::null_mut(), - None, - ) - }; - cvt(result).map(|_| nwritten as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - let timeout = sys::dur2timeout(dur); - if timeout == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - timeout - } - None => 0, - }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; - if raw == 0 { - Ok(None) - } else { - let secs = raw / 1000; - let nsec = (raw % 1000) * 1000000; - Ok(Some(Duration::new(secs as u64, nsec as u32))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => c::SD_SEND, - Shutdown::Read => c::SD_RECEIVE, - Shutdown::Both => c::SD_BOTH, - }; - let result = unsafe { c::shutdown(self.as_raw(), how) }; - cvt(result).map(drop) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_ulong; - let result = - unsafe { c::ioctlsocket(self.as_raw(), c::FIONBIO as c_int, &mut nonblocking) }; - cvt(result).map(drop) - } - - pub fn set_linger(&self, linger: Option) -> io::Result<()> { - let linger = c::LINGER { - l_onoff: linger.is_some() as c_ushort, - l_linger: linger.unwrap_or_default().as_secs() as c_ushort, - }; - - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) - } - - pub fn linger(&self) -> io::Result> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; - - Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> c::SOCKET { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - self.as_inner().as_raw_socket() as c::SOCKET - } - pub unsafe fn from_raw(raw: c::SOCKET) -> Self { - debug_assert_eq!(mem::size_of::(), mem::size_of::()); - debug_assert_eq!(mem::align_of::(), mem::align_of::()); - unsafe { Self::from_raw_socket(raw as RawSocket) } - } -} - -#[unstable(reason = "not public", issue = "none", feature = "fd_read")] -impl<'a> Read for &'a Socket { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &OwnedSocket { - &self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: OwnedSocket) -> Socket { - Socket(sock) - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> OwnedSocket { - self.0 - } -} - -impl AsSocket for Socket { - fn as_socket(&self) -> BorrowedSocket<'_> { - self.0.as_socket() - } -} - -impl AsRawSocket for Socket { - fn as_raw_socket(&self) -> RawSocket { - self.0.as_raw_socket() - } -} - -impl IntoRawSocket for Socket { - fn into_raw_socket(self) -> RawSocket { - self.0.into_raw_socket() - } -} - -impl FromRawSocket for Socket { - unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { - unsafe { Self(FromRawSocket::from_raw_socket(raw_socket)) } - } -} diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd068560..8ba2b6e2f20 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -7,7 +7,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/pal/xous/net/dns.rs deleted file mode 100644 index ff6e49ed2d4..00000000000 --- a/library/std/src/sys/pal/xous/net/dns.rs +++ /dev/null @@ -1,128 +0,0 @@ -use core::convert::{TryFrom, TryInto}; - -use crate::io; -use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; -use crate::os::xous::ffi::lend_mut; -use crate::os::xous::services::{DnsLendMut, dns_server}; - -pub struct DnsError { - #[allow(dead_code)] - pub code: u8, -} - -#[repr(C, align(4096))] -struct LookupHostQuery([u8; 4096]); - -pub struct LookupHost { - data: LookupHostQuery, - port: u16, - offset: usize, - count: usize, -} - -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - if self.offset >= self.data.0.len() { - return None; - } - match self.data.0.get(self.offset) { - Some(&4) => { - self.offset += 1; - if self.offset + 4 > self.data.0.len() { - return None; - } - let result = Some(SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new( - self.data.0[self.offset], - self.data.0[self.offset + 1], - self.data.0[self.offset + 2], - self.data.0[self.offset + 3], - ), - self.port, - ))); - self.offset += 4; - result - } - Some(&6) => { - self.offset += 1; - if self.offset + 16 > self.data.0.len() { - return None; - } - let mut new_addr = [0u8; 16]; - for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)] - .iter() - .zip(new_addr.iter_mut()) - { - *octet = *src; - } - let result = - Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0))); - self.offset += 16; - result - } - _ => None, - } - } -} - -pub fn lookup(query: &str, port: u16) -> Result { - let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; - - // Copy the query into the message that gets sent to the DNS server - for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) { - *result_byte = *query_byte; - } - - lend_mut( - dns_server(), - DnsLendMut::RawLookup.into(), - &mut result.data.0, - 0, - query.as_bytes().len(), - ) - .unwrap(); - if result.data.0[0] != 0 { - return Err(DnsError { code: result.data.0[1] }); - } - assert_eq!(result.offset, 0); - result.count = result.data.0[1] as usize; - - // Advance the offset to the first record - result.offset = 2; - Ok(result) -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl TryFrom<(&str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, &"DNS failure")) - } -} diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs deleted file mode 100644 index 3e18ed24208..00000000000 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ /dev/null @@ -1,83 +0,0 @@ -mod dns; - -mod tcpstream; -pub use tcpstream::*; - -mod tcplistener; -pub use tcplistener::*; - -mod udp; -pub use udp::*; - -// this structure needs to be synchronized with what's in net/src/api.rs -#[repr(C)] -#[derive(Debug)] -enum NetError { - // Ok = 0, - Unaddressable = 1, - SocketInUse = 2, - // AccessDenied = 3, - Invalid = 4, - // Finished = 5, - LibraryError = 6, - // AlreadyUsed = 7, - TimedOut = 8, - WouldBlock = 9, -} - -#[repr(C, align(4096))] -struct ConnectRequest { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -struct SendData { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -pub struct ReceiveData { - raw: [u8; 4096], -} - -#[repr(C, align(4096))] -pub struct GetAddress { - raw: [u8; 4096], -} - -pub use dns::LookupHost; - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - #[allow(dead_code)] - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - #[allow(dead_code)] - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/pal/xous/net/tcplistener.rs deleted file mode 100644 index 640a02a64f5..00000000000 --- a/library/std/src/sys/pal/xous/net/tcplistener.rs +++ /dev/null @@ -1,243 +0,0 @@ -use core::convert::TryInto; -use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; - -use super::*; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use crate::os::xous::services; -use crate::sync::Arc; -use crate::{fmt, io}; - -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - &"This function is not yet implemented", - )); - }; -} - -#[derive(Clone)] -pub struct TcpListener { - fd: Arc, - local: SocketAddr, - handle_count: Arc, - nonblocking: Arc, -} - -impl TcpListener { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { - let mut addr = *socketaddr?; - - let fd = TcpListener::bind_inner(&mut addr)?; - return Ok(TcpListener { - fd: Arc::new(AtomicU16::new(fd)), - local: addr, - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Arc::new(AtomicBool::new(false)), - }); - } - - /// This returns the raw fd of a Listener, so that it can also be used by the - /// accept routine to replenish the Listener object after its handle has been converted into - /// a TcpStream object. - fn bind_inner(addr: &mut SocketAddr) -> io::Result { - // Construct the request - let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; - - // Serialize the StdUdpBind structure. This is done "manually" because we don't want to - // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. - let port_bytes = addr.port().to_le_bytes(); - connect_request.raw[0] = port_bytes[0]; - connect_request.raw[1] = port_bytes[1]; - match addr.ip() { - IpAddr::V4(addr) => { - connect_request.raw[2] = 4; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - IpAddr::V6(addr) => { - connect_request.raw[2] = 6; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - } - - let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdTcpListen.into(), - &mut connect_request.raw, - 0, - 4096, - ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); - }; - - // The first four bytes should be zero upon success, and will be nonzero - // for an error. - let response = connect_request.raw; - if response[0] != 0 || valid == 0 { - let errcode = response[1]; - if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); - } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address")); - } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); - } else { - return Err(io::const_error!( - io::ErrorKind::Other, - &"Unable to connect or internal error" - )); - } - } - let fd = response[1] as usize; - if addr.port() == 0 { - // oddly enough, this is a valid port and it means "give me something valid, up to you what that is" - let assigned_port = u16::from_le_bytes(response[2..4].try_into().unwrap()); - addr.set_port(assigned_port); - } - // println!("TcpListening with file handle of {}\r\n", fd); - Ok(fd.try_into().unwrap()) - } - - pub fn socket_addr(&self) -> io::Result { - Ok(self.local) - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - let mut receive_request = ReceiveData { raw: [0u8; 4096] }; - - if self.nonblocking.load(Ordering::Relaxed) { - // nonblocking - receive_request.raw[0] = 0; - } else { - // blocking - receive_request.raw[0] = 1; - } - - if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdTcpAccept(self.fd.load(Ordering::Relaxed)).into(), - &mut receive_request.raw, - 0, - 0, - ) { - if receive_request.raw[0] != 0 { - // error case - if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_error!(io::ErrorKind::TimedOut, &"accept timed out",)); - } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err( - io::const_error!(io::ErrorKind::WouldBlock, &"accept would block",), - ); - } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); - } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); - } - } else { - // accept successful - let rr = &receive_request.raw; - let stream_fd = u16::from_le_bytes(rr[1..3].try_into().unwrap()); - let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); - let addr = if rr[3] == 4 { - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) - } else if rr[3] == 6 { - SocketAddr::new( - IpAddr::V6(Ipv6Addr::new( - u16::from_be_bytes(rr[4..6].try_into().unwrap()), - u16::from_be_bytes(rr[6..8].try_into().unwrap()), - u16::from_be_bytes(rr[8..10].try_into().unwrap()), - u16::from_be_bytes(rr[10..12].try_into().unwrap()), - u16::from_be_bytes(rr[12..14].try_into().unwrap()), - u16::from_be_bytes(rr[14..16].try_into().unwrap()), - u16::from_be_bytes(rr[16..18].try_into().unwrap()), - u16::from_be_bytes(rr[18..20].try_into().unwrap()), - )), - port, - ) - } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); - }; - - // replenish the listener - let mut local_copy = self.local.clone(); // port is non-0 by this time, but the method signature needs a mut - let new_fd = TcpListener::bind_inner(&mut local_copy)?; - self.fd.store(new_fd, Ordering::Relaxed); - - // now return a stream converted from the old stream's fd - Ok((TcpStream::from_listener(stream_fd, self.local.port(), port, addr), addr)) - } - } else { - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to accept")) - } - } - - pub fn duplicate(&self) -> io::Result { - self.handle_count.fetch_add(1, Ordering::Relaxed); - Ok(self.clone()) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - if ttl > 255 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); - } - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdSetTtlTcp(self.fd.load(Ordering::Relaxed), ttl).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|_| ()) - } - - pub fn ttl(&self) -> io::Result { - Ok(crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdGetTtlTcp(self.fd.load(Ordering::Relaxed)).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|res| res[0] as _)?) - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. - Ok(None) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.nonblocking.store(nonblocking, Ordering::Relaxed); - Ok(()) - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TCP listening on {:?}", self.local) - } -} - -impl Drop for TcpListener { - fn drop(&mut self) { - if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { - // only drop if we're the last clone - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - crate::os::xous::services::NetBlockingScalar::StdTcpClose( - self.fd.load(Ordering::Relaxed), - ) - .into(), - ) - .unwrap(); - } - } -} diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/pal/xous/net/tcpstream.rs deleted file mode 100644 index 572dd6b3b63..00000000000 --- a/library/std/src/sys/pal/xous/net/tcpstream.rs +++ /dev/null @@ -1,424 +0,0 @@ -use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; - -use super::*; -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; -use crate::os::xous::services; -use crate::sync::Arc; -use crate::time::Duration; - -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - &"This function is not yet implemented", - )); - }; -} - -enum ReadOrPeek { - Read, - Peek, -} - -#[derive(Clone)] -pub struct TcpStream { - fd: u16, - local_port: u16, - remote_port: u16, - peer_addr: SocketAddr, - // milliseconds - read_timeout: Arc, - // milliseconds - write_timeout: Arc, - handle_count: Arc, - nonblocking: Arc, -} - -fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) { - // Construct the request. - let port_bytes = addr.port().to_le_bytes(); - buf[0] = port_bytes[0]; - buf[1] = port_bytes[1]; - for (dest, src) in buf[2..].iter_mut().zip((duration.as_millis() as u64).to_le_bytes()) { - *dest = src; - } - match addr.ip() { - IpAddr::V4(addr) => { - buf[10] = 4; - for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - IpAddr::V6(addr) => { - buf[10] = 6; - for (dest, src) in buf[11..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - } -} - -impl TcpStream { - pub(crate) fn from_listener( - fd: u16, - local_port: u16, - remote_port: u16, - peer_addr: SocketAddr, - ) -> TcpStream { - TcpStream { - fd, - local_port, - remote_port, - peer_addr, - read_timeout: Arc::new(AtomicU32::new(0)), - write_timeout: Arc::new(AtomicU32::new(0)), - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Arc::new(AtomicBool::new(false)), - } - } - - pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result { - Self::connect_timeout(socketaddr?, Duration::ZERO) - } - - pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result { - let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; - - // Construct the request. - sockaddr_to_buf(duration, &addr, &mut connect_request.raw); - - let Ok((_, valid)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdTcpConnect.into(), - &mut connect_request.raw, - 0, - 4096, - ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")); - }; - - // The first four bytes should be zero upon success, and will be nonzero - // for an error. - let response = connect_request.raw; - if response[0] != 0 || valid == 0 { - // errcode is a u8 but stuck in a u16 where the upper byte is invalid. Mask & decode accordingly. - let errcode = response[0]; - if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use",)); - } else if errcode == NetError::Unaddressable as u8 { - return Err(io::const_error!(io::ErrorKind::AddrNotAvailable, &"Invalid address",)); - } else { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - &"Unable to connect or internal error", - )); - } - } - let fd = u16::from_le_bytes([response[2], response[3]]); - let local_port = u16::from_le_bytes([response[4], response[5]]); - let remote_port = u16::from_le_bytes([response[6], response[7]]); - // println!( - // "Connected with local port of {}, remote port of {}, file handle of {}", - // local_port, remote_port, fd - // ); - Ok(TcpStream { - fd, - local_port, - remote_port, - peer_addr: *addr, - read_timeout: Arc::new(AtomicU32::new(0)), - write_timeout: Arc::new(AtomicU32::new(0)), - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Arc::new(AtomicBool::new(false)), - }) - } - - pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { - if let Some(to) = timeout { - if to.is_zero() { - return Err(io::Error::ZERO_TIMEOUT); - } - } - self.read_timeout.store( - timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), - Ordering::Relaxed, - ); - Ok(()) - } - - pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { - if let Some(to) = timeout { - if to.is_zero() { - return Err(io::Error::ZERO_TIMEOUT); - } - } - self.write_timeout.store( - timeout.map(|t| t.as_millis().min(u32::MAX as u128) as u32).unwrap_or_default(), - Ordering::Relaxed, - ); - Ok(()) - } - - pub fn read_timeout(&self) -> io::Result> { - match self.read_timeout.load(Ordering::Relaxed) { - 0 => Ok(None), - t => Ok(Some(Duration::from_millis(t as u64))), - } - } - - pub fn write_timeout(&self) -> io::Result> { - match self.write_timeout.load(Ordering::Relaxed) { - 0 => Ok(None), - t => Ok(Some(Duration::from_millis(t as u64))), - } - } - - fn read_or_peek(&self, buf: &mut [u8], op: ReadOrPeek) -> io::Result { - let mut receive_request = ReceiveData { raw: [0u8; 4096] }; - let data_to_read = buf.len().min(receive_request.raw.len()); - - let opcode = match op { - ReadOrPeek::Read => { - services::NetLendMut::StdTcpRx(self.fd, self.nonblocking.load(Ordering::Relaxed)) - } - ReadOrPeek::Peek => { - services::NetLendMut::StdTcpPeek(self.fd, self.nonblocking.load(Ordering::Relaxed)) - } - }; - - let Ok((offset, length)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - opcode.into(), - &mut receive_request.raw, - // Reuse the `offset` as the read timeout - self.read_timeout.load(Ordering::Relaxed) as usize, - data_to_read, - ) else { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - &"Library failure: wrong message type or messaging error" - )); - }; - - if offset != 0 { - for (dest, src) in buf.iter_mut().zip(receive_request.raw[..length].iter()) { - *dest = *src; - } - Ok(length) - } else { - let result = receive_request.raw; - if result[0] != 0 { - if result[1] == 8 { - // timed out - return Err(io::const_error!(io::ErrorKind::TimedOut, &"Timeout",)); - } - if result[1] == 9 { - // would block - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); - } - } - Err(io::const_error!(io::ErrorKind::Other, &"recv_slice failure")) - } - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.read_or_peek(buf, ReadOrPeek::Peek) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_or_peek(buf, ReadOrPeek::Read) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let mut send_request = SendData { raw: [0u8; 4096] }; - for (dest, src) in send_request.raw.iter_mut().zip(buf) { - *dest = *src; - } - let buf_len = send_request.raw.len().min(buf.len()); - - let (_offset, _valid) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdTcpTx(self.fd).into(), - &mut send_request.raw, - // Reuse the offset as the timeout - self.write_timeout.load(Ordering::Relaxed) as usize, - buf_len, - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")))?; - - if send_request.raw[0] != 0 { - if send_request.raw[4] == 8 { - // timed out - return Err(io::const_error!( - io::ErrorKind::BrokenPipe, - &"Timeout or connection closed", - )); - } else if send_request.raw[4] == 9 { - // would block - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"Would block",)); - } else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Error when sending",)); - } - } - Ok(u32::from_le_bytes([ - send_request.raw[4], - send_request.raw[5], - send_request.raw[6], - send_request.raw[7], - ]) as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - Ok(self.peer_addr) - } - - pub fn socket_addr(&self) -> io::Result { - let mut get_addr = GetAddress { raw: [0u8; 4096] }; - - let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdGetAddress(self.fd).into(), - &mut get_addr.raw, - 0, - 0, - ) else { - return Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")); - }; - let mut i = get_addr.raw.iter(); - match *i.next().unwrap() { - 4 => Ok(SocketAddr::V4(SocketAddrV4::new( - Ipv4Addr::new( - *i.next().unwrap(), - *i.next().unwrap(), - *i.next().unwrap(), - *i.next().unwrap(), - ), - self.local_port, - ))), - 6 => { - let mut new_addr = [0u8; 16]; - for (src, octet) in i.zip(new_addr.iter_mut()) { - *octet = *src; - } - Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0))) - } - _ => Err(io::const_error!(io::ErrorKind::InvalidInput, &"Internal error")), - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdTcpStreamShutdown(self.fd, how).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|_| ()) - } - - pub fn duplicate(&self) -> io::Result { - self.handle_count.fetch_add(1, Ordering::Relaxed); - Ok(self.clone()) - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, enabled: bool) -> io::Result<()> { - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdSetNodelay(self.fd, enabled).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|_| ()) - } - - pub fn nodelay(&self) -> io::Result { - Ok(crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdGetNodelay(self.fd).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|res| res[0] != 0)?) - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - if ttl > 255 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); - } - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdSetTtlTcp(self.fd, ttl).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|_| ()) - } - - pub fn ttl(&self) -> io::Result { - Ok(crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdGetTtlTcp(self.fd).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|res| res[0] as _)?) - } - - pub fn take_error(&self) -> io::Result> { - // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. - Ok(None) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.nonblocking.store(nonblocking, Ordering::Relaxed); - Ok(()) - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "TCP connection to {:?} port {} to local port {}", - self.peer_addr, self.remote_port, self.local_port - ) - } -} - -impl Drop for TcpStream { - fn drop(&mut self) { - if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { - // only drop if we're the last clone - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdTcpClose(self.fd).into(), - ) - .unwrap(); - } - } -} diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/pal/xous/net/udp.rs deleted file mode 100644 index 1b7ecac6d3a..00000000000 --- a/library/std/src/sys/pal/xous/net/udp.rs +++ /dev/null @@ -1,459 +0,0 @@ -use core::convert::TryInto; -use core::sync::atomic::{AtomicUsize, Ordering}; - -use super::*; -use crate::cell::Cell; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use crate::os::xous::services; -use crate::sync::Arc; -use crate::time::Duration; -use crate::{fmt, io}; - -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - &"This function is not yet implemented", - )); - }; -} - -#[derive(Clone)] -pub struct UdpSocket { - fd: u16, - local: SocketAddr, - remote: Cell>, - // in milliseconds. The setting applies only to `recv` calls after the timeout is set. - read_timeout: Cell, - // in milliseconds. The setting applies only to `send` calls after the timeout is set. - write_timeout: Cell, - handle_count: Arc, - nonblocking: Cell, -} - -impl UdpSocket { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { - let addr = socketaddr?; - // Construct the request - let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; - - // Serialize the StdUdpBind structure. This is done "manually" because we don't want to - // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. - let port_bytes = addr.port().to_le_bytes(); - connect_request.raw[0] = port_bytes[0]; - connect_request.raw[1] = port_bytes[1]; - match addr.ip() { - IpAddr::V4(addr) => { - connect_request.raw[2] = 4; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - IpAddr::V6(addr) => { - connect_request.raw[2] = 6; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - } - - let response = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdUdpBind.into(), - &mut connect_request.raw, - 0, - 4096, - ); - - if let Ok((_, valid)) = response { - // The first four bytes should be zero upon success, and will be nonzero - // for an error. - let response = connect_request.raw; - if response[0] != 0 || valid == 0 { - let errcode = response[1]; - if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!(io::ErrorKind::ResourceBusy, &"Socket in use")); - } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - &"Port can't be 0 or invalid address" - )); - } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); - } else { - return Err(io::const_error!( - io::ErrorKind::Other, - &"Unable to connect or internal error" - )); - } - } - let fd = response[1] as u16; - return Ok(UdpSocket { - fd, - local: *addr, - remote: Cell::new(None), - read_timeout: Cell::new(0), - write_timeout: Cell::new(0), - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Cell::new(false), - }); - } - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Invalid response")) - } - - pub fn peer_addr(&self) -> io::Result { - match self.remote.get() { - Some(dest) => Ok(dest), - None => Err(io::const_error!(io::ErrorKind::NotConnected, &"No peer specified")), - } - } - - pub fn socket_addr(&self) -> io::Result { - Ok(self.local) - } - - fn recv_inner(&self, buf: &mut [u8], do_peek: bool) -> io::Result<(usize, SocketAddr)> { - let mut receive_request = ReceiveData { raw: [0u8; 4096] }; - - if self.nonblocking.get() { - // nonblocking - receive_request.raw[0] = 0; - } else { - // blocking - receive_request.raw[0] = 1; - for (&s, d) in self - .read_timeout - .get() - .to_le_bytes() - .iter() - .zip(receive_request.raw[1..9].iter_mut()) - { - *d = s; - } - } - if let Ok((_offset, _valid)) = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdUdpRx(self.fd).into(), - &mut receive_request.raw, - if do_peek { 1 } else { 0 }, - 0, - ) { - if receive_request.raw[0] != 0 { - // error case - if receive_request.raw[1] == NetError::TimedOut as u8 { - return Err(io::const_error!(io::ErrorKind::TimedOut, &"recv timed out",)); - } else if receive_request.raw[1] == NetError::WouldBlock as u8 { - return Err(io::const_error!(io::ErrorKind::WouldBlock, &"recv would block",)); - } else if receive_request.raw[1] == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); - } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); - } - } else { - let rr = &receive_request.raw; - let rxlen = u16::from_le_bytes(rr[1..3].try_into().unwrap()); - let port = u16::from_le_bytes(rr[20..22].try_into().unwrap()); - let addr = if rr[3] == 4 { - SocketAddr::new(IpAddr::V4(Ipv4Addr::new(rr[4], rr[5], rr[6], rr[7])), port) - } else if rr[3] == 6 { - SocketAddr::new( - IpAddr::V6(Ipv6Addr::new( - u16::from_be_bytes(rr[4..6].try_into().unwrap()), - u16::from_be_bytes(rr[6..8].try_into().unwrap()), - u16::from_be_bytes(rr[8..10].try_into().unwrap()), - u16::from_be_bytes(rr[10..12].try_into().unwrap()), - u16::from_be_bytes(rr[12..14].try_into().unwrap()), - u16::from_be_bytes(rr[14..16].try_into().unwrap()), - u16::from_be_bytes(rr[16..18].try_into().unwrap()), - u16::from_be_bytes(rr[18..20].try_into().unwrap()), - )), - port, - ) - } else { - return Err(io::const_error!(io::ErrorKind::Other, &"library error",)); - }; - for (&s, d) in rr[22..22 + rxlen as usize].iter().zip(buf.iter_mut()) { - *d = s; - } - Ok((rxlen as usize, addr)) - } - } else { - Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unable to recv")) - } - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_inner(buf, false) - } - - pub fn recv(&self, buf: &mut [u8]) -> io::Result { - self.recv_from(buf).map(|(len, _addr)| len) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_inner(buf, true) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.peek_from(buf).map(|(len, _addr)| len) - } - - pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> { - let addr = maybe_addr?; - self.remote.set(Some(*addr)); - Ok(()) - } - - pub fn send(&self, buf: &[u8]) -> io::Result { - if let Some(addr) = self.remote.get() { - self.send_to(buf, &addr) - } else { - Err(io::const_error!(io::ErrorKind::NotConnected, &"No remote specified")) - } - } - - pub fn send_to(&self, buf: &[u8], addr: &SocketAddr) -> io::Result { - let mut tx_req = SendData { raw: [0u8; 4096] }; - - // Construct the request. - let port_bytes = addr.port().to_le_bytes(); - tx_req.raw[0] = port_bytes[0]; - tx_req.raw[1] = port_bytes[1]; - match addr.ip() { - IpAddr::V4(addr) => { - tx_req.raw[2] = 4; - for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - IpAddr::V6(addr) => { - tx_req.raw[2] = 6; - for (dest, src) in tx_req.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - } - let len = buf.len() as u16; - let len_bytes = len.to_le_bytes(); - tx_req.raw[19] = len_bytes[0]; - tx_req.raw[20] = len_bytes[1]; - for (&s, d) in buf.iter().zip(tx_req.raw[21..].iter_mut()) { - *d = s; - } - - // let buf = unsafe { - // xous::MemoryRange::new( - // &mut tx_req as *mut SendData as usize, - // core::mem::size_of::(), - // ) - // .unwrap() - // }; - - // write time-outs are implemented on the caller side. Basically, if the Net crate server - // is too busy to take the call immediately: retry, until the timeout is reached. - let now = crate::time::Instant::now(); - let write_timeout = if self.nonblocking.get() { - // nonblocking - core::time::Duration::ZERO - } else { - // blocking - if self.write_timeout.get() == 0 { - // forever - core::time::Duration::from_millis(u64::MAX) - } else { - // or this amount of time - core::time::Duration::from_millis(self.write_timeout.get()) - } - }; - loop { - let response = crate::os::xous::ffi::try_lend_mut( - services::net_server(), - services::NetLendMut::StdUdpTx(self.fd).into(), - &mut tx_req.raw, - 0, - 4096, - ); - match response { - Ok((_, valid)) => { - let response = &tx_req.raw; - if response[0] != 0 || valid == 0 { - let errcode = response[1]; - if errcode == NetError::SocketInUse as u8 { - return Err(io::const_error!( - io::ErrorKind::ResourceBusy, - &"Socket in use" - )); - } else if errcode == NetError::Invalid as u8 { - return Err(io::const_error!( - io::ErrorKind::InvalidInput, - &"Socket not valid" - )); - } else if errcode == NetError::LibraryError as u8 { - return Err(io::const_error!(io::ErrorKind::Other, &"Library error")); - } else { - return Err(io::const_error!( - io::ErrorKind::Other, - &"Unable to connect" - )); - } - } else { - // no error - return Ok(len as usize); - } - } - Err(crate::os::xous::ffi::Error::ServerQueueFull) => { - if now.elapsed() >= write_timeout { - return Err(io::const_error!( - io::ErrorKind::WouldBlock, - &"Write timed out" - )); - } else { - // question: do we want to do something a bit more gentle than immediately retrying? - crate::thread::yield_now(); - } - } - _ => return Err(io::const_error!(io::ErrorKind::Other, &"Library error")), - } - } - } - - pub fn duplicate(&self) -> io::Result { - self.handle_count.fetch_add(1, Ordering::Relaxed); - Ok(self.clone()) - } - - pub fn set_read_timeout(&self, timeout: Option) -> io::Result<()> { - if let Some(d) = timeout { - if d.is_zero() { - return Err(io::Error::ZERO_TIMEOUT); - } - } - self.read_timeout - .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); - Ok(()) - } - - pub fn set_write_timeout(&self, timeout: Option) -> io::Result<()> { - if let Some(d) = timeout { - if d.is_zero() { - return Err(io::Error::ZERO_TIMEOUT); - } - } - self.write_timeout - .set(timeout.map(|t| t.as_millis().min(u64::MAX as u128) as u64).unwrap_or_default()); - Ok(()) - } - - pub fn read_timeout(&self) -> io::Result> { - match self.read_timeout.get() { - 0 => Ok(None), - t => Ok(Some(Duration::from_millis(t as u64))), - } - } - - pub fn write_timeout(&self) -> io::Result> { - match self.write_timeout.get() { - 0 => Ok(None), - t => Ok(Some(Duration::from_millis(t as u64))), - } - } - - pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - if ttl > 255 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "TTL must be less than 256")); - } - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdSetTtlUdp(self.fd, ttl).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|_| ()) - } - - pub fn ttl(&self) -> io::Result { - Ok(crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdGetTtlUdp(self.fd).into(), - ) - .or(Err(io::const_error!(io::ErrorKind::InvalidInput, &"Unexpected return value"))) - .map(|res| res[0] as _)?) - } - - pub fn take_error(&self) -> io::Result> { - // this call doesn't have a meaning on our platform, but we can at least not panic if it's used. - Ok(None) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - self.nonblocking.set(nonblocking); - Ok(()) - } - - // ------------- smoltcp base stack does not have multicast or broadcast support --------------- - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "UDP listening on {:?} to {:?}", self.local, self.remote.get(),) - } -} - -impl Drop for UdpSocket { - fn drop(&mut self) { - if self.handle_count.fetch_sub(1, Ordering::Relaxed) == 1 { - // only drop if we're the last clone - crate::os::xous::ffi::blocking_scalar( - services::net_server(), - services::NetBlockingScalar::StdUdpClose(self.fd).into(), - ) - .unwrap(); - } - } -} diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea05772029..9e9ae861070 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -18,8 +18,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -- cgit 1.4.1-3-g733a5