Skip to content

Commit 89b329c

Browse files
committed
send syn after 500ms if TFO is enabled for server
- ref #232
1 parent 8a27bb2 commit 89b329c

File tree

1 file changed

+67
-30
lines changed

1 file changed

+67
-30
lines changed

crates/shadowsocks-service/src/server/tcprelay.rs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Shadowsocks TCP server
22
33
use std::{
4+
future::Future,
45
io::{self, ErrorKind},
56
net::SocketAddr,
67
sync::Arc,
@@ -22,7 +23,11 @@ use shadowsocks::{
2223
ProxyListener,
2324
ServerConfig,
2425
};
25-
use tokio::{net::TcpStream as TokioTcpStream, time};
26+
use tokio::{
27+
io::{AsyncReadExt, AsyncWriteExt},
28+
net::TcpStream as TokioTcpStream,
29+
time,
30+
};
2631

2732
use crate::net::{utils::ignore_until_end, MonProxyStream};
2833

@@ -77,6 +82,20 @@ impl TcpServer {
7782
}
7883
}
7984

85+
#[inline]
86+
async fn timeout_fut<F, R>(duration: Option<Duration>, f: F) -> io::Result<R>
87+
where
88+
F: Future<Output = io::Result<R>>,
89+
{
90+
match duration {
91+
None => f.await,
92+
Some(d) => match time::timeout(d, f).await {
93+
Ok(o) => o,
94+
Err(..) => Err(ErrorKind::TimedOut.into()),
95+
},
96+
}
97+
}
98+
8099
struct TcpServerClient {
81100
context: Arc<ServiceContext>,
82101
method: CipherKind,
@@ -123,38 +142,56 @@ impl TcpServerClient {
123142
return Ok(());
124143
}
125144

126-
let mut remote_stream = match self.timeout {
127-
Some(d) => {
128-
match time::timeout(
129-
d,
130-
OutboundTcpStream::connect_remote_with_opts(
131-
self.context.context_ref(),
132-
&target_addr,
133-
self.context.connect_opts_ref(),
134-
),
135-
)
136-
.await
137-
{
138-
Ok(Ok(s)) => s,
139-
Ok(Err(e)) => return Err(e),
140-
Err(..) => {
141-
return Err(io::Error::new(
142-
ErrorKind::TimedOut,
143-
format!("connect {} timeout", target_addr),
144-
))
145-
}
146-
}
147-
}
148-
None => {
149-
OutboundTcpStream::connect_remote_with_opts(
150-
self.context.context_ref(),
151-
&target_addr,
152-
self.context.connect_opts_ref(),
153-
)
154-
.await?
145+
let mut remote_stream = match timeout_fut(
146+
self.timeout,
147+
OutboundTcpStream::connect_remote_with_opts(
148+
self.context.context_ref(),
149+
&target_addr,
150+
self.context.connect_opts_ref(),
151+
),
152+
)
153+
.await
154+
{
155+
Ok(s) => s,
156+
Err(err) => {
157+
error!(
158+
"tcp tunnel {} -> {} connect failed, error: {}",
159+
self.peer_addr, target_addr, err
160+
);
161+
return Err(err);
155162
}
156163
};
157164

165+
// https://github.com/shadowsocks/shadowsocks-rust/issues/232
166+
//
167+
// Protocols like FTP, clients will wait for servers to send Welcome Message without sending anything.
168+
//
169+
// Wait at most 500ms, and then sends handshake packet to remote servers.
170+
if self.context.connect_opts_ref().tcp.fastopen {
171+
let mut buffer = [0u8; 8192];
172+
match time::timeout(Duration::from_millis(500), self.stream.read(&mut buffer)).await {
173+
Ok(Ok(0)) => {
174+
// EOF. Just terminate right here.
175+
return Ok(());
176+
}
177+
Ok(Ok(n)) => {
178+
// Send the first packet.
179+
timeout_fut(self.timeout, remote_stream.write_all(&buffer[..n])).await?;
180+
}
181+
Ok(Err(err)) => return Err(err),
182+
Err(..) => {
183+
// Timeout. Send handshake to server.
184+
timeout_fut(self.timeout, remote_stream.write(&[])).await?;
185+
186+
trace!(
187+
"tcp tunnel {} -> {} sent TFO connect without data",
188+
self.peer_addr,
189+
target_addr
190+
);
191+
}
192+
}
193+
}
194+
158195
let (mut lr, mut lw) = self.stream.into_split();
159196
let (mut rr, mut rw) = remote_stream.split();
160197

0 commit comments

Comments
 (0)