diff --git a/clash_lib/src/proxy/shadowsocks/mod.rs b/clash_lib/src/proxy/shadowsocks/mod.rs index 280b30926..725c176b3 100644 --- a/clash_lib/src/proxy/shadowsocks/mod.rs +++ b/clash_lib/src/proxy/shadowsocks/mod.rs @@ -32,6 +32,7 @@ use super::{ AnyOutboundHandler, AnyStream, ConnectorType, OutboundType, }; +#[derive(Clone, Copy)] pub enum SimpleOBFSMode { Http, Tls, @@ -451,4 +452,71 @@ mod tests { // see: https://github.com/ihciah/shadow-tls/issues/54 run_test_suites_and_cleanup(handler, chained, Suite::tcp_tests()).await } + + async fn get_obfs_runner( + ss_port: u16, + obfs_port: u16, + mode: SimpleOBFSMode, + ) -> anyhow::Result { + let ss_server_env = format!("127.0.0.1:{}", ss_port); + let port = format!("{}", obfs_port); + let mode = match mode { + SimpleOBFSMode::Http => "http", + SimpleOBFSMode::Tls => "tls", + }; + DockerTestRunnerBuilder::new() + .image(IMAGE_OBFS) + .cmd(&[ + "obfs-server", + "-p", + &port, + "--obfs", + &mode, + "-r", + &ss_server_env, + ]) + .build() + .await + } + + async fn test_ss_obfs_inner(mode: SimpleOBFSMode) -> anyhow::Result<()> { + let obfs_port = 10002; + let ss_port = 10004; + let opts = HandlerOptions { + name: "test-ss".to_owned(), + common_opts: Default::default(), + server: LOCAL_ADDR.to_owned(), + port: obfs_port, + password: PASSWORD.to_owned(), + cipher: CIPHER.to_owned(), + plugin_opts: Some(OBFSOption::Simple(SimpleOBFSOption { + host: "www.bing.com".to_owned(), + mode, + })), + udp: false, + }; + + let handler: Arc = Handler::new(opts); + let mut chained = MultiDockerTestRunner::default(); + chained.add(get_ss_runner(ss_port)).await; + chained.add(get_obfs_runner(ss_port, obfs_port, mode)).await; + run_test_suites_and_cleanup(handler, chained, &Suite::tcp_tests()).await + } + + #[cfg(target_arch = "x86_64")] + #[tokio::test] + #[serial_test::serial] + async fn test_ss_obfs_http() -> anyhow::Result<()> { + test_ss_obfs_inner(SimpleOBFSMode::Http).await + } + + #[cfg(target_arch = "x86_64")] + #[tokio::test] + #[serial_test::serial] + async fn test_ss_obfs_tls() -> anyhow::Result<()> { + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + test_ss_obfs_inner(SimpleOBFSMode::Tls).await + } } diff --git a/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs b/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs index bd1921420..2b6ffa319 100644 --- a/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs +++ b/clash_lib/src/proxy/shadowsocks/simple_obfs/tls.rs @@ -144,51 +144,53 @@ fn reading( let this = this.get_mut(); let mut inner = Pin::new(&mut this.inner); - match this.read_state { - ReadState::Idle => { - // 1. discard n bytes - let mut buffer = vec![0; discard_n]; - let fut = inner.read_exact(&mut buffer); - pin_mut!(fut); - match ready!(fut.poll(cx)) { - Ok(_) => { - this.read_state = ReadState::Parsing; - Poll::Pending // do next step + loop { + match this.read_state { + ReadState::Idle => { + // 1. discard n bytes + let mut buffer = vec![0; discard_n]; + let fut = inner.read_exact(&mut buffer); + pin_mut!(fut); + match ready!(fut.poll(cx)) { + Ok(_) => { + this.read_state = ReadState::Parsing; + continue; + } + Err(e) => return Poll::Ready(Err(e)), } - Err(e) => Poll::Ready(Err(e)), } - } - ReadState::Parsing => { - // 2. read 2 bytes as length - let mut buffer = vec![0; 2]; - let fut = inner.read_exact(&mut buffer); - pin_mut!(fut); - match ready!(fut.poll(cx)) { - Ok(_) => { - let length = BigEndian::read_u16(&buffer[..2]) as usize; - this.read_state = ReadState::Reading(length); - Poll::Pending // do next step + ReadState::Parsing => { + // 2. read 2 bytes as length + let mut buffer = vec![0; 2]; + let fut = inner.read_exact(&mut buffer); + pin_mut!(fut); + match ready!(fut.poll(cx)) { + Ok(_) => { + let length = BigEndian::read_u16(&buffer[..2]) as usize; + this.read_state = ReadState::Reading(length); + continue; + } + Err(e) => return Poll::Ready(Err(e)), } - Err(e) => Poll::Ready(Err(e)), } - } - ReadState::Reading(length) => { - // 3. read length bytes - let remaining = buf.remaining(); - let len = length.min(remaining); - let mut buffer = vec![0; len]; - let fut = inner.read_exact(&mut buffer); - pin_mut!(fut); - match ready!(fut.poll(cx)) { - Ok(_) => { - buf.put_slice(&buffer); - if length > remaining { - this.remain = length - remaining; + ReadState::Reading(length) => { + // 3. read length bytes + let remaining = buf.remaining(); + let len = length.min(remaining); + let mut buffer = vec![0; len]; + let fut = inner.read_exact(&mut buffer); + pin_mut!(fut); + match ready!(fut.poll(cx)) { + Ok(_) => { + buf.put_slice(&buffer); + if length > remaining { + this.remain = length - remaining; + } + this.read_state = ReadState::Idle; + return Poll::Ready(Ok(())); } - this.read_state = ReadState::Idle; - Poll::Ready(Ok(())) + Err(e) => return Poll::Ready(Err(e)), } - Err(e) => Poll::Ready(Err(e)), } } } diff --git a/clash_lib/src/proxy/utils/test_utils/consts.rs b/clash_lib/src/proxy/utils/test_utils/consts.rs index c56cc8b4a..d2e6ce587 100644 --- a/clash_lib/src/proxy/utils/test_utils/consts.rs +++ b/clash_lib/src/proxy/utils/test_utils/consts.rs @@ -3,6 +3,7 @@ pub const LOCAL_ADDR: &str = "127.0.0.1"; pub const IMAGE_WG: &str = "lscr.io/linuxserver/wireguard:1.0.20210914-legacy"; pub const IMAGE_SS_RUST: &str = "ghcr.io/shadowsocks/ssserver-rust:latest"; pub const IMAGE_SHADOW_TLS: &str = "ghcr.io/ihciah/shadow-tls:latest"; +pub const IMAGE_OBFS: &str = "liaohuqiu/simple-obfs:latest"; pub const IMAGE_TROJAN_GO: &str = "p4gefau1t/trojan-go:latest"; pub const IMAGE_VMESS: &str = "v2fly/v2fly-core:v4.45.2"; pub const IMAGE_XRAY: &str = "teddysun/xray:latest";