Skip to content

Commit 0b5016e

Browse files
authored
m: 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 <[email protected]>
1 parent 8c3c3bd commit 0b5016e

File tree

3 files changed

+130
-37
lines changed

3 files changed

+130
-37
lines changed

Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ futures-io = { version = "0.3.28", default-features = false, features = ["std"]
3030
futures-lite = { version = "1.11.0", default-features = false }
3131
parking = "2.0.0"
3232
polling = "3.0.0"
33-
rustix = { version = "0.38.2", default-features = false, features = ["std", "fs"] }
33+
rustix = { version = "0.38.2", default-features = false, features = ["fs", "net", "std"] }
3434
slab = "0.4.2"
35-
socket2 = { version = "0.5.3", features = ["all"] }
3635
tracing = { version = "0.1.37", default-features = false }
3736
waker-fn = "1.1.0"
3837

38+
[target.'cfg(windows)'.dependencies]
39+
windows-sys = { version = "0.48.0", features = ["Win32_Foundation"] }
40+
3941
[dev-dependencies]
4042
async-channel = "1"
4143
async-net = "1"

src/lib.rs

+124-34
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, R
8383
use futures_io::{AsyncRead, AsyncWrite};
8484
use futures_lite::stream::{self, Stream};
8585
use futures_lite::{future, pin, ready};
86-
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
86+
87+
use rustix::io as rio;
88+
use rustix::net as rn;
8789

8890
use crate::reactor::{Reactor, Registration, Source};
8991

@@ -656,23 +658,7 @@ impl<T: AsFd> Async<T> {
656658
pub fn new(io: T) -> io::Result<Async<T>> {
657659
// Put the file descriptor in non-blocking mode.
658660
let fd = io.as_fd();
659-
cfg_if::cfg_if! {
660-
// ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux
661-
// for now, as with the standard library, because it seems to behave
662-
// differently depending on the platform.
663-
// https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d
664-
// https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80
665-
// https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a
666-
if #[cfg(target_os = "linux")] {
667-
rustix::io::ioctl_fionbio(fd, true)?;
668-
} else {
669-
let previous = rustix::fs::fcntl_getfl(fd)?;
670-
let new = previous | rustix::fs::OFlags::NONBLOCK;
671-
if new != previous {
672-
rustix::fs::fcntl_setfl(fd, new)?;
673-
}
674-
}
675-
}
661+
set_nonblocking(fd)?;
676662

677663
// SAFETY: It is impossible to drop the I/O source while it is registered through
678664
// this type.
@@ -1487,10 +1473,15 @@ impl Async<TcpStream> {
14871473
/// # std::io::Result::Ok(()) });
14881474
/// ```
14891475
pub async fn connect<A: Into<SocketAddr>>(addr: A) -> io::Result<Async<TcpStream>> {
1490-
// Begin async connect.
1476+
// Figure out how to handle this address.
14911477
let addr = addr.into();
1492-
let domain = Domain::for_address(addr);
1493-
let socket = connect(addr.into(), domain, Some(Protocol::TCP))?;
1478+
let (domain, sock_addr) = match addr {
1479+
SocketAddr::V4(v4) => (rn::AddressFamily::INET, rn::SocketAddrAny::V4(v4)),
1480+
SocketAddr::V6(v6) => (rn::AddressFamily::INET6, rn::SocketAddrAny::V6(v6)),
1481+
};
1482+
1483+
// Begin async connect.
1484+
let socket = connect(sock_addr, domain, Some(rn::ipproto::TCP))?;
14941485
let stream = Async::new(TcpStream::from(socket))?;
14951486

14961487
// The stream becomes writable when connected.
@@ -1819,7 +1810,11 @@ impl Async<UnixStream> {
18191810
/// ```
18201811
pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<Async<UnixStream>> {
18211812
// Begin async connect.
1822-
let socket = connect(SockAddr::unix(path)?, Domain::UNIX, None)?;
1813+
let socket = connect(
1814+
rn::SocketAddrUnix::new(path.as_ref())?.into(),
1815+
rn::AddressFamily::UNIX,
1816+
None,
1817+
)?;
18231818
let stream = Async::new(UnixStream::from(socket))?;
18241819

18251820
// The stream becomes writable when connected.
@@ -2029,8 +2024,14 @@ async fn optimistic(fut: impl Future<Output = io::Result<()>>) -> io::Result<()>
20292024
.await
20302025
}
20312026

2032-
fn connect(addr: SockAddr, domain: Domain, protocol: Option<Protocol>) -> io::Result<Socket> {
2033-
let sock_type = Type::STREAM;
2027+
fn connect(
2028+
addr: rn::SocketAddrAny,
2029+
domain: rn::AddressFamily,
2030+
protocol: Option<rn::Protocol>,
2031+
) -> io::Result<rustix::fd::OwnedFd> {
2032+
#[cfg(windows)]
2033+
use rustix::fd::AsFd;
2034+
20342035
#[cfg(any(
20352036
target_os = "android",
20362037
target_os = "dragonfly",
@@ -2041,10 +2042,13 @@ fn connect(addr: SockAddr, domain: Domain, protocol: Option<Protocol>) -> io::Re
20412042
target_os = "netbsd",
20422043
target_os = "openbsd"
20432044
))]
2044-
// If we can, set nonblocking at socket creation for unix
2045-
let sock_type = sock_type.nonblocking();
2046-
// This automatically handles cloexec on unix, no_inherit on windows and nosigpipe on macos
2047-
let socket = Socket::new(domain, sock_type, protocol)?;
2045+
let socket = rn::socket_with(
2046+
domain,
2047+
rn::SocketType::STREAM,
2048+
rn::SocketFlags::CLOEXEC | rn::SocketFlags::NONBLOCK,
2049+
protocol,
2050+
)?;
2051+
20482052
#[cfg(not(any(
20492053
target_os = "android",
20502054
target_os = "dragonfly",
@@ -2055,14 +2059,100 @@ fn connect(addr: SockAddr, domain: Domain, protocol: Option<Protocol>) -> io::Re
20552059
target_os = "netbsd",
20562060
target_os = "openbsd"
20572061
)))]
2058-
// If the current platform doesn't support nonblocking at creation, enable it after creation
2059-
socket.set_nonblocking(true)?;
2060-
match socket.connect(&addr) {
2062+
let socket = {
2063+
#[cfg(not(any(
2064+
target_os = "macos",
2065+
target_os = "ios",
2066+
target_os = "tvos",
2067+
target_os = "watchos",
2068+
target_os = "espidf",
2069+
windows,
2070+
)))]
2071+
let flags = rn::SocketFlags::CLOEXEC;
2072+
#[cfg(any(
2073+
target_os = "macos",
2074+
target_os = "ios",
2075+
target_os = "tvos",
2076+
target_os = "watchos",
2077+
target_os = "espidf",
2078+
windows,
2079+
))]
2080+
let flags = rn::SocketFlags::empty();
2081+
2082+
// Create the socket.
2083+
let socket = rn::socket_with(domain, rn::SocketType::STREAM, flags, protocol)?;
2084+
2085+
// Set cloexec if necessary.
2086+
#[cfg(any(
2087+
target_os = "macos",
2088+
target_os = "ios",
2089+
target_os = "tvos",
2090+
target_os = "watchos",
2091+
))]
2092+
rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?;
2093+
2094+
// Set non-blocking mode.
2095+
set_nonblocking(socket.as_fd())?;
2096+
2097+
socket
2098+
};
2099+
2100+
// Set nosigpipe if necessary.
2101+
#[cfg(any(
2102+
target_os = "macos",
2103+
target_os = "ios",
2104+
target_os = "tvos",
2105+
target_os = "watchos",
2106+
target_os = "freebsd"
2107+
))]
2108+
rn::sockopt::set_socket_nosigpipe(&socket, true)?;
2109+
2110+
// Set the handle information to HANDLE_FLAG_INHERIT.
2111+
#[cfg(windows)]
2112+
unsafe {
2113+
if windows_sys::Win32::Foundation::SetHandleInformation(
2114+
socket.as_raw_socket() as _,
2115+
windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT,
2116+
windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT,
2117+
) == 0
2118+
{
2119+
return Err(io::Error::last_os_error());
2120+
}
2121+
}
2122+
2123+
#[allow(unreachable_patterns)]
2124+
match rn::connect_any(&socket, &addr) {
20612125
Ok(_) => {}
20622126
#[cfg(unix)]
2063-
Err(err) if err.raw_os_error() == Some(rustix::io::Errno::INPROGRESS.raw_os_error()) => {}
2064-
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
2065-
Err(err) => return Err(err),
2127+
Err(rio::Errno::INPROGRESS) => {}
2128+
Err(rio::Errno::AGAIN) | Err(rio::Errno::WOULDBLOCK) => {}
2129+
Err(err) => return Err(err.into()),
20662130
}
20672131
Ok(socket)
20682132
}
2133+
2134+
#[inline]
2135+
fn set_nonblocking(
2136+
#[cfg(unix)] fd: BorrowedFd<'_>,
2137+
#[cfg(windows)] fd: BorrowedSocket<'_>,
2138+
) -> io::Result<()> {
2139+
cfg_if::cfg_if! {
2140+
// ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux
2141+
// for now, as with the standard library, because it seems to behave
2142+
// differently depending on the platform.
2143+
// https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d
2144+
// https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80
2145+
// https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a
2146+
if #[cfg(any(windows, target_os = "linux"))] {
2147+
rustix::io::ioctl_fionbio(fd, true)?;
2148+
} else {
2149+
let previous = rustix::fs::fcntl_getfl(fd)?;
2150+
let new = previous | rustix::fs::OFlags::NONBLOCK;
2151+
if new != previous {
2152+
rustix::fs::fcntl_setfl(fd, new)?;
2153+
}
2154+
}
2155+
}
2156+
2157+
Ok(())
2158+
}

tests/async.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ fn udp_connect() -> io::Result<()> {
178178
})
179179
}
180180

181-
#[cfg(unix)]
181+
// This test is broken for now on OpenBSD: https://github.com/rust-lang/rust/issues/116523
182+
#[cfg(all(unix, not(target_os = "openbsd")))]
182183
#[test]
183184
fn uds_connect() -> io::Result<()> {
184185
future::block_on(async {

0 commit comments

Comments
 (0)