diff --git a/Cargo.lock b/Cargo.lock index 3f71ad5..1dc208b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,9 +421,8 @@ dependencies = [ [[package]] name = "monoio" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "762fe3b52f2193a2b28ac992576f60b282b63e2fe80549f906e8d2f4a48e1c81" +version = "0.1.3" +source = "git+https://github.com/ihciah/monoio?branch=tfo-mac#805f5d252111cc333316737c43d5f11c27b1a43b" dependencies = [ "auto-const-array", "bytes", @@ -440,12 +439,11 @@ dependencies = [ [[package]] name = "monoio-macros" version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85c9d2114c190b1ec18da29bfbe399a95d81657633b0d2333d4084515aa9f95" +source = "git+https://github.com/ihciah/monoio?branch=tfo-mac#805f5d252111cc333316737c43d5f11c27b1a43b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] @@ -685,7 +683,7 @@ dependencies = [ [[package]] name = "shadow-tls" -version = "0.2.21" +version = "0.2.22" dependencies = [ "anyhow", "byteorder", diff --git a/Cargo.toml b/Cargo.toml index 42e8523..e400c8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,10 @@ license = "MIT/Apache-2.0" name = "shadow-tls" readme = "README.md" repository = "https://github.com/ihciah/shadow-tls" -version = "0.2.21" +version = "0.2.22" [dependencies] -monoio = { version = "0.1.2" } +monoio = { version = "0.1.3" } monoio-rustls-fork-shadow-tls = { version = "0.1.1-mod.0" } rustls-fork-shadow-tls = { version = "0.20.9-mod.2", default-features = false } @@ -32,3 +32,6 @@ webpki-roots = "0.22" [profile.release] lto = true opt-level = 3 + +[patch.crates-io] +monoio = { git = "https://github.com/ihciah/monoio", branch = "tfo-mac" } diff --git a/Dockerfile b/Dockerfile index 2f4c1d9..2fd9c33 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,6 +15,7 @@ ENV THREADS="" ENV PASSWORD="" ENV ALPN="" ENV DISABLE_NODELAY="" +ENV FASTOPEN="" ENV V3="" ENV STRICT="" diff --git a/Dockerfile.action b/Dockerfile.action index e8c9525..0b39536 100644 --- a/Dockerfile.action +++ b/Dockerfile.action @@ -16,6 +16,7 @@ ENV THREADS="" ENV PASSWORD="" ENV ALPN="" ENV DISABLE_NODELAY="" +ENV FASTOPEN="" ENV V3="" ENV STRICT="" diff --git a/docker-compose.yml b/docker-compose.yml index 61eb462..42a9d17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: # ALPN(optional): set alpns(like http/1.1, http/1.1;h2, recommend to leave it blank if you don't know it) # THREADS(optional): set threads number(recommend to leave it blank) # DISABLE_NODELAY(optional): disable TCP_NODELAY(recommend to leave it blank) +# FASTOPEN(optional): enable TCP_FASTOPEN # WILDCARD_SNI: Use sni:443 as handshake server(off/authed/all) # Note: diff --git a/entrypoint.sh b/entrypoint.sh index 426eb68..76c0382 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -10,6 +10,11 @@ then parameter="$parameter --disable-nodelay" fi +if [ ! -z "$FASTOPEN" ] +then + parameter="$parameter --fastopen" +fi + if [ ! -z "$V3" ] then parameter="$parameter --v3" diff --git a/src/client.rs b/src/client.rs index be4e0be..d8bc364 100644 --- a/src/client.rs +++ b/src/client.rs @@ -9,7 +9,7 @@ use byteorder::{BigEndian, WriteBytesExt}; use monoio::{ buf::IoBufMut, io::{AsyncReadRent, AsyncReadRentExt, AsyncWriteRent, AsyncWriteRentExt, Splitable}, - net::TcpStream, + net::{TcpConnectOpts, TcpStream}, }; use monoio_rustls_fork_shadow_tls::TlsConnector; use rand::{prelude::Distribution, seq::SliceRandom, Rng}; @@ -34,6 +34,7 @@ pub struct ShadowTlsClient { tls_names: Arc, password: Arc, nodelay: bool, + fastopen: bool, v3: V3Mode, } @@ -110,6 +111,7 @@ impl std::fmt::Display for TlsExtConfig { impl ShadowTlsClient { /// Create new ShadowTlsClient. + #[allow(clippy::too_many_arguments)] pub fn new( listen_addr: LA, target_addr: TA, @@ -117,6 +119,7 @@ impl ShadowTlsClient { tls_ext_config: TlsExtConfig, password: String, nodelay: bool, + fastopen: bool, v3: V3Mode, ) -> anyhow::Result { let mut root_store = RootCertStore::empty(); @@ -147,6 +150,7 @@ impl ShadowTlsClient { tls_names: Arc::new(tls_names), password: Arc::new(password), nodelay, + fastopen, v3, }) } @@ -157,7 +161,7 @@ impl ShadowTlsClient { LA: std::net::ToSocketAddrs + 'static, TA: std::net::ToSocketAddrs + 'static, { - let listener = bind_with_pretty_error(self.listen_addr.as_ref())?; + let listener = bind_with_pretty_error(self.listen_addr.as_ref(), self.fastopen)?; let shared = Rc::new(self); loop { match listener.accept().await { @@ -204,7 +208,16 @@ impl ShadowTlsClient { where TA: std::net::ToSocketAddrs, { - let mut stream = TcpStream::connect(self.target_addr.as_ref()).await?; + let addr = self + .target_addr + .to_socket_addrs()? + .next() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "empty address"))?; + let mut stream = TcpStream::connect_addr_with_config( + addr, + &TcpConnectOpts::default().tcp_fast_open(self.fastopen), + ) + .await?; mod_tcp_conn(&mut stream, true, self.nodelay); tracing::debug!("tcp connected, start handshaking"); @@ -269,7 +282,16 @@ impl ShadowTlsClient { where TA: std::net::ToSocketAddrs, { - let mut stream = TcpStream::connect(self.target_addr.as_ref()).await?; + let addr = self + .target_addr + .to_socket_addrs()? + .next() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "empty address"))?; + let mut stream = TcpStream::connect_addr_with_config( + addr, + &TcpConnectOpts::default().tcp_fast_open(self.fastopen), + ) + .await?; mod_tcp_conn(&mut stream, true, self.nodelay); tracing::debug!("tcp connected, start handshaking"); let stream = HashedReadStream::new(stream, self.password.as_bytes())?; diff --git a/src/lib.rs b/src/lib.rs index 32fb505..ed1c1bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,7 @@ pub enum RunningArgs { tls_ext: TlsExtConfig, password: String, nodelay: bool, + fastopen: bool, v3: V3Mode, }, Server { @@ -30,6 +31,7 @@ pub enum RunningArgs { tls_addr: TlsAddrs, password: String, nodelay: bool, + fastopen: bool, v3: V3Mode, }, } @@ -45,6 +47,7 @@ impl RunningArgs { tls_ext, password, nodelay, + fastopen, v3, } => Ok(Runnable::Client(ShadowTlsClient::new( listen_addr, @@ -53,6 +56,7 @@ impl RunningArgs { tls_ext, password, nodelay, + fastopen, v3, )?)), RunningArgs::Server { @@ -61,6 +65,7 @@ impl RunningArgs { tls_addr, password, nodelay, + fastopen, v3, } => Ok(Runnable::Server(ShadowTlsServer::new( listen_addr, @@ -68,6 +73,7 @@ impl RunningArgs { tls_addr, password, nodelay, + fastopen, v3, ))), } diff --git a/src/main.rs b/src/main.rs index 3897f5e..d4b089d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,8 +28,10 @@ struct Args { struct Opts { #[clap(short, long, help = "Set parallelism manually")] threads: Option, - #[clap(short, long, help = "Disable TCP_NODELAY")] + #[clap(long, help = "Disable TCP_NODELAY")] disable_nodelay: bool, + #[clap(long, help = "Enable TCP_FASTOPEN")] + fastopen: bool, #[clap(long, help = "Use v3 protocol")] v3: bool, #[clap(long, help = "Strict mode(only for v3 protocol)")] @@ -126,6 +128,7 @@ impl From for RunningArgs { tls_ext: TlsExtConfig::from(alpn), password, nodelay: !args.opts.disable_nodelay, + fastopen: args.opts.fastopen, v3, }, Commands::Server { @@ -142,6 +145,7 @@ impl From for RunningArgs { tls_addr, password, nodelay: !args.opts.disable_nodelay, + fastopen: args.opts.fastopen, v3, } } diff --git a/src/server.rs b/src/server.rs index 365664c..ac2e6ce 100644 --- a/src/server.rs +++ b/src/server.rs @@ -38,6 +38,7 @@ pub struct ShadowTlsServer { tls_addr: Arc, password: Arc, nodelay: bool, + fastopen: bool, v3: V3Mode, } @@ -141,6 +142,7 @@ impl ShadowTlsServer { tls_addr: TlsAddrs, password: String, nodelay: bool, + fastopen: bool, v3: V3Mode, ) -> Self { Self { @@ -149,6 +151,7 @@ impl ShadowTlsServer { tls_addr: Arc::new(tls_addr), password: Arc::new(password), nodelay, + fastopen, v3, } } @@ -161,7 +164,7 @@ impl ShadowTlsServer { LA: std::net::ToSocketAddrs + 'static, TA: std::net::ToSocketAddrs + 'static, { - let listener = bind_with_pretty_error(self.listen_addr.as_ref())?; + let listener = bind_with_pretty_error(self.listen_addr.as_ref(), self.fastopen)?; let shared = Rc::new(self); loop { match listener.accept().await { diff --git a/src/sip003.rs b/src/sip003.rs index a5adf67..5e13ddb 100644 --- a/src/sip003.rs +++ b/src/sip003.rs @@ -36,7 +36,7 @@ fn index_unescaped(s: &str, term: &[u8]) -> Result<(usize, String), anyhow::Erro while i < s.len() { let mut b: u8 = s.as_bytes()[i]; - if let Some(..) = term.iter().find(|&&e| b == e) { + if term.iter().any(|&e| b == e) { break; } if b == b'\\' { diff --git a/src/util.rs b/src/util.rs index fbddecc..eae4ba8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -10,7 +10,7 @@ use local_sync::oneshot::{Receiver, Sender}; use monoio::{ buf::IoBufMut, io::{AsyncReadRent, AsyncWriteRent, AsyncWriteRentExt, Splitable}, - net::{TcpListener, TcpStream}, + net::{ListenerOpts, TcpListener, TcpStream}, }; use hmac::Mac; @@ -216,8 +216,12 @@ pub(crate) async fn verified_relay( } /// Bind with pretty error. -pub(crate) fn bind_with_pretty_error(addr: A) -> anyhow::Result { - TcpListener::bind(addr).map_err(|e| match e.kind() { +pub(crate) fn bind_with_pretty_error( + addr: A, + fastopen: bool, +) -> anyhow::Result { + let cfg = ListenerOpts::default().tcp_fast_open(fastopen); + TcpListener::bind_with_config(addr, &cfg).map_err(|e| match e.kind() { ErrorKind::AddrInUse => { anyhow::anyhow!("bind failed, check if the port is used: {e}") } diff --git a/tests/sni.rs b/tests/sni.rs index 9cb1e09..c8504f4 100644 --- a/tests/sni.rs +++ b/tests/sni.rs @@ -36,6 +36,7 @@ async fn sni() { tls_addr: TlsAddrs::try_from("captive.apple.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; server.build().expect("build server failed").start(1); diff --git a/tests/tls12.rs b/tests/tls12.rs index 3009edd..eb90d1b 100644 --- a/tests/tls12.rs +++ b/tests/tls12.rs @@ -16,6 +16,7 @@ fn tls12_v2() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Disabled, }; let server = RunningArgs::Server { @@ -24,6 +25,7 @@ fn tls12_v2() { tls_addr: TlsAddrs::try_from("bing.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Disabled, }; test_ok(client, server, CAPTIVE_HTTP_REQUEST, CAPTIVE_HTTP_RESP); @@ -41,6 +43,7 @@ fn tls12_v3_lossy() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; let server = RunningArgs::Server { @@ -49,6 +52,7 @@ fn tls12_v3_lossy() { tls_addr: TlsAddrs::try_from("bing.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; utils::test_ok(client, server, CAPTIVE_HTTP_REQUEST, CAPTIVE_HTTP_RESP); @@ -68,6 +72,7 @@ fn tls12_v3_strict() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; let server = RunningArgs::Server { @@ -76,6 +81,7 @@ fn tls12_v3_strict() { tls_addr: TlsAddrs::try_from("bing.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; utils::test_ok(client, server, CAPTIVE_HTTP_REQUEST, CAPTIVE_HTTP_RESP); @@ -97,6 +103,7 @@ fn tls12_v2_hijack() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Disabled, }; test_hijack(client); @@ -119,6 +126,7 @@ fn tls12_v3_lossy_hijack() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; test_hijack(client); diff --git a/tests/tls13.rs b/tests/tls13.rs index 207c8f4..d97655c 100644 --- a/tests/tls13.rs +++ b/tests/tls13.rs @@ -16,6 +16,7 @@ fn tls13_v2() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Disabled, }; let server = RunningArgs::Server { @@ -24,6 +25,7 @@ fn tls13_v2() { tls_addr: TlsAddrs::try_from("captive.apple.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Disabled, }; test_ok(client, server, BING_HTTP_REQUEST, BING_HTTP_RESP); @@ -41,6 +43,7 @@ fn tls13_v3_lossy() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; let server = RunningArgs::Server { @@ -49,6 +52,7 @@ fn tls13_v3_lossy() { tls_addr: TlsAddrs::try_from("captive.apple.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; utils::test_ok(client, server, BING_HTTP_REQUEST, BING_HTTP_RESP); @@ -66,6 +70,7 @@ fn tls13_v3_strict() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; let server = RunningArgs::Server { @@ -74,6 +79,7 @@ fn tls13_v3_strict() { tls_addr: TlsAddrs::try_from("captive.apple.com").unwrap(), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; utils::test_ok(client, server, BING_HTTP_REQUEST, BING_HTTP_RESP); @@ -91,6 +97,7 @@ fn tls13_v3_lossy_hijack() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Lossy, }; test_hijack(client); @@ -108,6 +115,7 @@ fn tls13_v3_strict_hijack() { tls_ext: TlsExtConfig::new(None), password: "test".to_string(), nodelay: true, + fastopen: true, v3: V3Mode::Strict, }; test_hijack(client);