diff --git a/Cargo.lock b/Cargo.lock index 63e77bcaf..e4af8705c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,6 +98,7 @@ dependencies = [ "async-ctrlc", "config", "futures", + "libc", "once_cell", "serde", "tokio", @@ -107,6 +108,7 @@ dependencies = [ "tracing", "tracing-journald", "tracing-subscriber", + "winapi", ] [[package]] diff --git a/crates/arti/Cargo.toml b/crates/arti/Cargo.toml index cf1b1d1a9..0a75e8f4f 100644 --- a/crates/arti/Cargo.toml +++ b/crates/arti/Cargo.toml @@ -34,3 +34,9 @@ tracing-subscriber = "0.2.19" tokio-crate = { package="tokio", version = "1.7.0", optional = true, features = ["signal"] } argh = "0.1.6" tracing-journald = { version = "0.1.0", optional = true } + +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2.103", default-features = false } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.8", features = [ "winerror" ] } diff --git a/crates/arti/src/proxy.rs b/crates/arti/src/proxy.rs index 72573b986..2cfcb9ee9 100644 --- a/crates/arti/src/proxy.rs +++ b/crates/arti/src/proxy.rs @@ -4,7 +4,7 @@ //! connections and then runs use futures::future::FutureExt; -use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, Error as IoError}; use futures::lock::Mutex; use futures::stream::StreamExt; use futures::task::SpawnExt; @@ -334,6 +334,21 @@ where loop_result.or(flush_result) } +/// Return true if a given IoError, when received from accept, is a fatal +/// error. +fn accept_err_is_fatal(err: &IoError) -> bool { + #![allow(clippy::match_like_matches_macro)] + // Currently, EMFILE and ENFILE aren't distinguished by ErrorKind; + // we need to use OS-specific errors. :P + match err.raw_os_error() { + #[cfg(unix)] + Some(libc::EMFILE) | Some(libc::ENFILE) => false, + #[cfg(windows)] + Some(winapi::shared::winerror::WSAEMFILE) => false, + _ => true, + } +} + /// Launch a SOCKS proxy to listen on a given localhost port, and run /// indefinitely. /// @@ -386,7 +401,17 @@ pub(crate) async fn run_socks_proxy( // Loop over all incoming connections. For each one, call // handle_socks_conn() in a new task. while let Some((stream, sock_id)) = incoming.next().await { - let (stream, addr) = stream.context("Failed to receive incoming stream on SOCKS port")?; + let (stream, addr) = match stream { + Ok((s, a)) => (s, a), + Err(err) => { + if accept_err_is_fatal(&err) { + return Err(err).context("Failed to receive incoming stream on SOCKS port"); + } else { + warn!("Incoming stream failed: {}", err); + continue; + } + } + }; let client_ref = Arc::clone(&tor_client); let runtime_copy = runtime.clone(); let isolation_map_ref = Arc::clone(&isolation_map);