use std::sync::Arc;
use std::time::Duration;
use bytes::Bytes;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::{Instant, timeout_at};
use crate::address::NetLocation;
use crate::async_stream::AsyncStream;
use crate::client_proxy_chain::ClientProxyChain;
use crate::client_proxy_selector::ClientProxySelector;
use crate::crypto::{CryptoConnection, CryptoTlsStream, perform_crypto_handshake};
use crate::resolver::Resolver;
use crate::shadow_tls::{ParsedClientHello, parse_server_hello};
use crate::tcp::tcp_handler::{TcpClientSetupResult, TcpServerSetupResult};
use crate::tls_server_handler::InnerProtocol;
use crate::util::{allocate_vec, write_all};
use crate::vless::tls_deframer::TlsDeframer;
use super::{RealityServerConfig, RealityServerConnection};
#[derive(Debug)]
pub struct RealityServerTarget {
pub private_key: [u8; 32],
pub short_ids: Vec<[u8; 8]>,
pub dest: NetLocation,
pub max_time_diff: Option<u64>, pub min_client_version: Option<[u8; 3]>,
pub max_client_version: Option<[u8; 3]>,
pub cipher_suites: Vec<super::CipherSuite>,
pub effective_selector: Arc<ClientProxySelector>,
pub inner_protocol: InnerProtocol,
pub dest_client_chain: ClientProxyChain,
}
#[inline]
pub async fn setup_reality_server_stream(
mut server_stream: Box<dyn AsyncStream>,
target: &RealityServerTarget,
parsed_client_hello: ParsedClientHello,
resolver: &Arc<dyn Resolver>,
) -> std::io::Result<TcpServerSetupResult> {
let client_hello_frame = &parsed_client_hello.client_hello_frame;
log::debug!(
"REALITY ClientHello frame length: {}",
client_hello_frame.len()
);
let TcpClientSetupResult {
client_stream: mut dest_stream,
early_data,
} = target
.dest_client_chain
.connect_tcp(target.dest.clone().into(), resolver)
.await
.map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::ConnectionRefused,
format!("REALITY: Failed to connect to dest {}: {}", target.dest, e),
)
})?;
debug_assert!(
early_data.is_none(),
"unexpected early_data from dest connection"
);
log::debug!(
"REALITY: Connected to dest {}, forwarding ClientHello ({} bytes)",
target.dest,
client_hello_frame.len()
);
write_all(&mut dest_stream, client_hello_frame).await?;
dest_stream.flush().await?;
if !parsed_client_hello.supports_tls13 {
log::warn!("REALITY: Client does not support TLS 1.3, falling back to dest");
return Ok(start_forward_to_dest(
server_stream,
dest_stream,
vec![],
Bytes::new(),
));
}
let reality_config = RealityServerConfig {
private_key: target.private_key,
short_ids: target.short_ids.clone(),
dest: target.dest.clone(),
max_time_diff: target.max_time_diff,
min_client_version: target.min_client_version,
max_client_version: target.max_client_version,
cipher_suites: target.cipher_suites.clone(),
};
let mut reality_conn = RealityServerConnection::new(reality_config)?;
let auth_result = reality_conn.validate_client_hello(client_hello_frame);
let deadline = Instant::now() + Duration::from_secs(5);
let mut deframer = TlsDeframer::new();
let mut dest_records: Vec<Bytes> = Vec::new();
let mut buf = allocate_vec(8192).into_boxed_slice();
let mut dest_handshake_success = false;
loop {
let new_records = match timeout_at(deadline, dest_stream.read(&mut buf)).await {
Ok(Ok(0)) => {
return Err(std::io::Error::other(
"REALITY: Dest connection closed during TLS handshake",
));
}
Ok(Ok(n)) => {
deframer.feed(&buf[..n]);
match deframer.next_records() {
Ok(records) => records,
Err(e) => {
log::error!("REALITY: Error parsing dest records: {}", e);
break;
}
}
}
Ok(Err(e)) => {
return Err(std::io::Error::other(format!(
"REALITY: Error reading from dest: {}",
e
)));
}
Err(_) => {
log::debug!("REALITY: Timeout reading from dest");
break;
}
};
if dest_records.is_empty() && !new_records.is_empty() {
match parse_server_hello(&new_records[0]) {
Ok(parsed) => {
if !parsed.is_tls13 {
log::error!(
"REALITY: Dest {} is TLS 1.2, falling back to transparent forward",
target.dest
);
return Ok(start_forward_to_dest(
server_stream,
dest_stream,
new_records,
deframer.into_remaining_data(),
));
}
log::debug!("REALITY: Dest confirmed TLS 1.3");
}
Err(e) => {
log::error!("REALITY: Failed to parse dest ServerHello: {}", e);
return Ok(start_forward_to_dest(
server_stream,
dest_stream,
new_records,
deframer.into_remaining_data(),
));
}
}
}
dest_records.extend(new_records);
if dest_records.len() >= 6 {
log::debug!(
"REALITY: Separate mode detected, got {} records",
dest_records.len()
);
dest_handshake_success = true;
break;
} else if dest_records.len() >= 3 {
let first_encrypted = &dest_records[2];
if first_encrypted.len() > 512 {
log::debug!(
"REALITY: Combined mode detected (first encrypted record {} bytes > 512)",
first_encrypted.len()
);
dest_handshake_success = true;
break;
}
}
}
let remaining_data = deframer.into_remaining_data();
if !dest_handshake_success {
log::warn!(
"REALITY: Dest handshake failed (got {} records), falling back to transparent forward",
dest_records.len()
);
return Ok(start_forward_to_dest(
server_stream,
dest_stream,
dest_records,
remaining_data,
));
}
log::debug!(
"REALITY: Read {} records from dest ({} bytes remaining)",
dest_records.len(),
remaining_data.len()
);
match auth_result {
Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => {
log::warn!(
"REALITY: Auth failed ({}), forwarding to dest transparently",
e
);
return Ok(start_forward_to_dest(
server_stream,
dest_stream,
dest_records,
remaining_data,
));
}
Err(e) => {
log::error!("REALITY: Unexpected error during auth: {}", e);
return Err(e);
}
Ok(()) => {}
}
log::debug!("REALITY: Auth succeeded, building response matching dest structure");
drop(dest_stream);
reality_conn.build_server_response(dest_records)?;
let mut connection = CryptoConnection::new_reality_server(reality_conn);
perform_crypto_handshake(&mut connection, &mut server_stream, 16384).await?;
let tls_stream = CryptoTlsStream::new(server_stream, connection);
log::debug!("REALITY: TLS 1.3 handshake completed successfully");
match &target.inner_protocol {
InnerProtocol::Normal(handler) => handler.setup_server_stream(Box::new(tls_stream)).await,
InnerProtocol::VisionVless(vision_cfg) => {
crate::vless::vless_server_handler::setup_custom_tls_vision_vless_server_stream(
tls_stream,
&vision_cfg.user_id,
vision_cfg.udp_enabled,
target.effective_selector.clone(),
resolver,
vision_cfg.fallback.clone(),
)
.await
}
InnerProtocol::Naive(naive_cfg) => {
crate::naiveproxy::setup_naive_server_stream(
tls_stream,
naive_cfg,
target.effective_selector.clone(),
resolver.clone(),
)
.await
}
}
}
fn start_forward_to_dest(
mut client_stream: Box<dyn AsyncStream>,
mut dest_stream: Box<dyn AsyncStream>,
dest_records: Vec<Bytes>,
remaining_data: Bytes,
) -> TcpServerSetupResult {
TcpServerSetupResult::Session(Box::pin(async move {
for record in &dest_records {
if let Err(e) = write_all(&mut client_stream, record).await {
log::debug!("REALITY FALLBACK: Error forwarding record: {}", e);
futures::join!(
crate::util::shutdown_stream(&mut client_stream),
crate::util::shutdown_stream(&mut dest_stream),
);
return;
}
if let Err(e) = client_stream.flush().await {
log::debug!("REALITY FALLBACK: Error flushing record: {}", e);
futures::join!(
crate::util::shutdown_stream(&mut client_stream),
crate::util::shutdown_stream(&mut dest_stream),
);
return;
}
}
if !remaining_data.is_empty()
&& let Err(e) = write_all(&mut client_stream, &remaining_data).await
{
log::debug!("REALITY FALLBACK: Error forwarding remaining data: {}", e);
futures::join!(
crate::util::shutdown_stream(&mut client_stream),
crate::util::shutdown_stream(&mut dest_stream),
);
return;
}
log::debug!(
"REALITY FALLBACK: Forwarded {} records + {} remaining bytes, starting bidirectional copy",
dest_records.len(),
remaining_data.len()
);
let result = crate::copy_bidirectional::copy_bidirectional(
&mut *client_stream,
&mut dest_stream,
!remaining_data.is_empty(), false,
)
.await;
futures::join!(
crate::util::shutdown_stream(&mut client_stream),
crate::util::shutdown_stream(&mut dest_stream),
);
if let Err(e) = result {
log::debug!("REALITY FALLBACK: Connection ended with error: {}", e);
}
}))
}
#[cfg(test)]
mod cleanup_tests {
use super::*;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
struct StalledShutdown {
fail_write: bool,
fail_flush: bool,
_marker: Arc<()>,
}
impl AsyncRead for StalledShutdown {
fn poll_read(
self: Pin<&mut Self>,
_: &mut Context<'_>,
_: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl AsyncWrite for StalledShutdown {
fn poll_write(
self: Pin<&mut Self>,
_: &mut Context<'_>,
bytes: &[u8],
) -> Poll<io::Result<usize>> {
if self.fail_write {
Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
} else {
Poll::Ready(Ok(bytes.len()))
}
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
if self.fail_flush {
Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
} else {
Poll::Ready(Ok(()))
}
}
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Pending
}
}
impl crate::async_stream::AsyncPing for StalledShutdown {
fn supports_ping(&self) -> bool {
false
}
fn poll_write_ping(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<bool>> {
Poll::Ready(Ok(false))
}
}
impl AsyncStream for StalledShutdown {}
#[tokio::test(start_paused = true)]
async fn fallback_cleanup_is_bounded_after_copy_and_early_errors() {
let bytes = Bytes::from_static(b"handshake");
let cases = [
(false, false, vec![], Bytes::new(), 10),
(true, false, vec![bytes.clone()], Bytes::new(), 5),
(false, true, vec![bytes.clone()], Bytes::new(), 5),
(true, false, vec![], bytes, 5),
];
for (fail_write, fail_flush, records, remaining, seconds) in cases {
let marker = Arc::new(());
let client = StalledShutdown {
fail_write,
fail_flush,
_marker: marker.clone(),
};
let dest = StalledShutdown {
fail_write: false,
fail_flush: false,
_marker: marker.clone(),
};
let TcpServerSetupResult::Session(session) =
start_forward_to_dest(Box::new(client), Box::new(dest), records, remaining)
else {
panic!("fallback must remain owned by its caller")
};
let started = tokio::time::Instant::now();
tokio::time::timeout(std::time::Duration::from_secs(11), session)
.await
.expect("fallback cleanup retained the streams indefinitely");
assert_eq!(started.elapsed(), std::time::Duration::from_secs(seconds));
assert_eq!(Arc::strong_count(&marker), 1);
}
}
}