From d66b558636859d0b0153299bba137bf1889f7649 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Wed, 6 Sep 2023 20:28:55 -0700 Subject: [PATCH 1/7] Replace socket2 calls with rustix socket2 is our last libc-using dependency. By replacing it with rustix calls, we can make this crate libc-free. This doesn't set the inherit disable property on Windows yet, as rustix does not support it. Signed-off-by: John Nunley --- Cargo.toml | 3 +- src/lib.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 83 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 701b1d0..3c4f271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,9 +30,8 @@ futures-io = { version = "0.3.28", default-features = false, features = ["std"] futures-lite = { version = "1.11.0", default-features = false } parking = "2.0.0" polling = "3.0.0" -rustix = { version = "0.38.2", default-features = false, features = ["std", "fs"] } +rustix = { version = "0.38.2", default-features = false, features = ["fs", "net", "std"] } slab = "0.4.2" -socket2 = { version = "0.5.3", features = ["all"] } tracing = { version = "0.1.37", default-features = false } waker-fn = "1.1.0" diff --git a/src/lib.rs b/src/lib.rs index fd671b1..0abbf03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,9 @@ use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, R use futures_io::{AsyncRead, AsyncWrite}; use futures_lite::stream::{self, Stream}; use futures_lite::{future, pin, ready}; -use socket2::{Domain, Protocol, SockAddr, Socket, Type}; + +use rustix::io as rio; +use rustix::net as rn; use crate::reactor::{Reactor, Registration, Source}; @@ -1487,10 +1489,15 @@ impl Async { /// # std::io::Result::Ok(()) }); /// ``` pub async fn connect>(addr: A) -> io::Result> { - // Begin async connect. + // Figure out how to handle this address. let addr = addr.into(); - let domain = Domain::for_address(addr); - let socket = connect(addr.into(), domain, Some(Protocol::TCP))?; + let (domain, sock_addr) = match addr { + SocketAddr::V4(v4) => (rn::AddressFamily::INET, rn::SocketAddrAny::V4(v4)), + SocketAddr::V6(v6) => (rn::AddressFamily::INET6, rn::SocketAddrAny::V6(v6)), + }; + + // Begin async connect. + let socket = connect(sock_addr, domain, Some(rn::ipproto::TCP))?; let stream = Async::new(TcpStream::from(socket))?; // The stream becomes writable when connected. @@ -1819,7 +1826,11 @@ impl Async { /// ``` pub async fn connect>(path: P) -> io::Result> { // Begin async connect. - let socket = connect(SockAddr::unix(path)?, Domain::UNIX, None)?; + let socket = connect( + rn::SocketAddrUnix::new(path.as_ref())?.into(), + rn::AddressFamily::UNIX, + None, + )?; let stream = Async::new(UnixStream::from(socket))?; // The stream becomes writable when connected. @@ -2029,8 +2040,11 @@ async fn optimistic(fut: impl Future>) -> io::Result<()> .await } -fn connect(addr: SockAddr, domain: Domain, protocol: Option) -> io::Result { - let sock_type = Type::STREAM; +fn connect( + addr: rn::SocketAddrAny, + domain: rn::AddressFamily, + protocol: Option, +) -> io::Result { #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -2041,10 +2055,13 @@ fn connect(addr: SockAddr, domain: Domain, protocol: Option) -> io::Re target_os = "netbsd", target_os = "openbsd" ))] - // If we can, set nonblocking at socket creation for unix - let sock_type = sock_type.nonblocking(); - // This automatically handles cloexec on unix, no_inherit on windows and nosigpipe on macos - let socket = Socket::new(domain, sock_type, protocol)?; + let socket = rn::socket_with( + domain, + rn::SocketType::STREAM, + rn::SocketFlags::CLOEXEC | rn::SocketFlags::NONBLOCK, + protocol, + )?; + #[cfg(not(any( target_os = "android", target_os = "dragonfly", @@ -2055,14 +2072,62 @@ fn connect(addr: SockAddr, domain: Domain, protocol: Option) -> io::Re target_os = "netbsd", target_os = "openbsd" )))] - // If the current platform doesn't support nonblocking at creation, enable it after creation - socket.set_nonblocking(true)?; - match socket.connect(&addr) { + let socket = { + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + windows, + target_os = "aix", + target_os = "haiku" + )))] + let flags = rn::SocketFlags::CLOEXEC; + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + windows, + target_os = "aix", + target_os = "haiku" + ))] + let flags = rn::SocketFlags::empty(); + + // Create the socket. + let socket = rn::socket_with(domain, rn::SocketFlags::STREAM, flags, protocol)?; + + // Set cloexec if necessary. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos" + ))] + rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; + + // Set non-blocking mode. + rio::ioctl_fionbio(&socket, true)?; + + socket + }; + + // Set nosigpipe if necessary. + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + target_os = "freebsd" + ))] + rn::sockopt::set_socket_nosigpipe(&socket, true)?; + + match rn::connect_any(&socket, &addr) { Ok(_) => {} #[cfg(unix)] - Err(err) if err.raw_os_error() == Some(rustix::io::Errno::INPROGRESS.raw_os_error()) => {} - Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} - Err(err) => return Err(err), + Err(rio::Errno::INPROGRESS) => {} + Err(rio::Errno::AGAIN) => {} + Err(err) => return Err(err.into()), } Ok(socket) } From e6e5c91aa37dd3014285c29f1c8c1cd01a5b4c76 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Wed, 6 Sep 2023 20:33:26 -0700 Subject: [PATCH 2/7] Fix Windows build Signed-off-by: John Nunley --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0abbf03..663ef93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2095,7 +2095,7 @@ fn connect( let flags = rn::SocketFlags::empty(); // Create the socket. - let socket = rn::socket_with(domain, rn::SocketFlags::STREAM, flags, protocol)?; + let socket = rn::socket_with(domain, rn::SocketType::STREAM, flags, protocol)?; // Set cloexec if necessary. #[cfg(any( From 7eeecbcd4dc904927e336db18c61fe0744347c58 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 30 Sep 2023 14:39:23 -0700 Subject: [PATCH 3/7] Add SetHandleInformation to Windows Signed-off-by: John Nunley --- Cargo.toml | 3 +++ src/lib.rs | 73 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c4f271..7b68168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,9 @@ slab = "0.4.2" tracing = { version = "0.1.37", default-features = false } waker-fn = "1.1.0" +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.48.0", features = ["Win32_Foundation"] } + [dev-dependencies] async-channel = "1" async-net = "1" diff --git a/src/lib.rs b/src/lib.rs index 663ef93..437ec39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -658,23 +658,7 @@ impl Async { pub fn new(io: T) -> io::Result> { // Put the file descriptor in non-blocking mode. let fd = io.as_fd(); - cfg_if::cfg_if! { - // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux - // for now, as with the standard library, because it seems to behave - // differently depending on the platform. - // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d - // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 - // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a - if #[cfg(target_os = "linux")] { - rustix::io::ioctl_fionbio(fd, true)?; - } else { - let previous = rustix::fs::fcntl_getfl(fd)?; - let new = previous | rustix::fs::OFlags::NONBLOCK; - if new != previous { - rustix::fs::fcntl_setfl(fd, new)?; - } - } - } + set_nonblocking(fd)?; // SAFETY: It is impossible to drop the I/O source while it is registered through // this type. @@ -2045,6 +2029,8 @@ fn connect( domain: rn::AddressFamily, protocol: Option, ) -> io::Result { + use rustix::fd::AsFd; + #[cfg(any( target_os = "android", target_os = "dragonfly", @@ -2078,9 +2064,8 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "esp-idf", windows, - target_os = "aix", - target_os = "haiku" )))] let flags = rn::SocketFlags::CLOEXEC; #[cfg(any( @@ -2088,9 +2073,8 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", + target_os = "esp-idf", windows, - target_os = "aix", - target_os = "haiku" ))] let flags = rn::SocketFlags::empty(); @@ -2107,7 +2091,7 @@ fn connect( rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; // Set non-blocking mode. - rio::ioctl_fionbio(&socket, true)?; + set_nonblocking(socket.as_fd())?; socket }; @@ -2118,16 +2102,57 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", - target_os = "freebsd" + target_os = "freebsd", + target_os = "netbsd" ))] rn::sockopt::set_socket_nosigpipe(&socket, true)?; + // Set the handle information to HANDLE_FLAG_INHERIT. + #[cfg(windows)] + unsafe { + if windows_sys::Win32::Foundation::SetHandleInformation( + socket.as_raw_socket() as _, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, + ) == 0 + { + return Err(io::Error::last_os_error()); + } + } + + #[allow(unreachable_patterns)] match rn::connect_any(&socket, &addr) { Ok(_) => {} #[cfg(unix)] Err(rio::Errno::INPROGRESS) => {} - Err(rio::Errno::AGAIN) => {} + Err(rio::Errno::AGAIN) | Err(rio::Errno::WOULDBLOCK) => {} Err(err) => return Err(err.into()), } Ok(socket) } + +#[inline] +fn set_nonblocking( + #[cfg(unix)] fd: BorrowedFd<'_>, + #[cfg(windows)] fd: BorrowedSocket<'_>, +) -> io::Result<()> { + cfg_if::cfg_if! { + // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux + // for now, as with the standard library, because it seems to behave + // differently depending on the platform. + // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d + // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 + // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a + if #[cfg(any(windows, target_os = "linux"))] { + rustix::io::ioctl_fionbio(fd, true)?; + } else { + let previous = rustix::fs::fcntl_getfl(fd)?; + let new = previous | rustix::fs::OFlags::NONBLOCK; + if new != previous { + rustix::fs::fcntl_setfl(fd, new)?; + } + } + } + + Ok(()) +} From a812af26fa655b5d285b79388a2c4dba22034321 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 30 Sep 2023 14:57:25 -0700 Subject: [PATCH 4/7] Fix redundant import Signed-off-by: John Nunley --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 437ec39..11dfd67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2029,6 +2029,7 @@ fn connect( domain: rn::AddressFamily, protocol: Option, ) -> io::Result { + #[cfg(windows)] use rustix::fd::AsFd; #[cfg(any( From 21c35fb6c0b2bafb8a0b5124398cc2626d03d775 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 30 Sep 2023 15:11:42 -0700 Subject: [PATCH 5/7] Fix NetBSD/OpenBSD Signed-off-by: John Nunley --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 11dfd67..dca4be7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2040,7 +2040,6 @@ fn connect( target_os = "illumos", target_os = "linux", target_os = "netbsd", - target_os = "openbsd" ))] let socket = rn::socket_with( domain, @@ -2057,7 +2056,6 @@ fn connect( target_os = "illumos", target_os = "linux", target_os = "netbsd", - target_os = "openbsd" )))] let socket = { #[cfg(not(any( @@ -2066,6 +2064,7 @@ fn connect( target_os = "tvos", target_os = "watchos", target_os = "esp-idf", + target_os = "openbsd", windows, )))] let flags = rn::SocketFlags::CLOEXEC; @@ -2075,6 +2074,7 @@ fn connect( target_os = "tvos", target_os = "watchos", target_os = "esp-idf", + target_os = "openbsd", windows, ))] let flags = rn::SocketFlags::empty(); @@ -2087,7 +2087,8 @@ fn connect( target_os = "macos", target_os = "ios", target_os = "tvos", - target_os = "watchos" + target_os = "watchos", + target_os = "openbsd" ))] rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; @@ -2103,8 +2104,7 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", - target_os = "freebsd", - target_os = "netbsd" + target_os = "freebsd" ))] rn::sockopt::set_socket_nosigpipe(&socket, true)?; From 36205ac733e69fd3a9984120430542e8761f2d90 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 7 Oct 2023 16:20:53 -0700 Subject: [PATCH 6/7] Fix cfg guards Signed-off-by: John Nunley --- src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dca4be7..e9b3d2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2040,6 +2040,7 @@ fn connect( target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd" ))] let socket = rn::socket_with( domain, @@ -2056,6 +2057,7 @@ fn connect( target_os = "illumos", target_os = "linux", target_os = "netbsd", + target_os = "openbsd" )))] let socket = { #[cfg(not(any( @@ -2063,8 +2065,7 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", - target_os = "esp-idf", - target_os = "openbsd", + target_os = "espidf", windows, )))] let flags = rn::SocketFlags::CLOEXEC; @@ -2073,8 +2074,7 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", - target_os = "esp-idf", - target_os = "openbsd", + target_os = "espidf", windows, ))] let flags = rn::SocketFlags::empty(); @@ -2088,7 +2088,6 @@ fn connect( target_os = "ios", target_os = "tvos", target_os = "watchos", - target_os = "openbsd" ))] rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; From d5bdbcbf02b20584b0d2419f18363a7b916ce744 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Sat, 7 Oct 2023 20:57:00 -0700 Subject: [PATCH 7/7] Temporarily disable test on OpenBSD Signed-off-by: John Nunley --- tests/async.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/async.rs b/tests/async.rs index c856760..45c2e3b 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -178,7 +178,8 @@ fn udp_connect() -> io::Result<()> { }) } -#[cfg(unix)] +// This test is broken for now on OpenBSD: https://github.com/rust-lang/rust/issues/116523 +#[cfg(all(unix, not(target_os = "openbsd")))] #[test] fn uds_connect() -> io::Result<()> { future::block_on(async {