1pub mod cli;
7pub mod config;
8mod connector;
9mod error;
10mod handler;
11pub mod socks5;
12
13pub use cli::ClientArgs;
14pub use config::{ClientConfig, load_client_config};
15pub use connector::ClientState;
16pub use error::ClientError;
17
18use std::sync::Arc;
19use std::time::Duration;
20
21use tokio::net::TcpListener;
22use tokio_rustls::TlsConnector;
23use tokio_util::sync::CancellationToken;
24use tracing::{error, info};
25use trojan_auth::sha224_hex;
26use trojan_core::defaults::DEFAULT_TLS_HANDSHAKE_TIMEOUT_SECS;
27
28pub async fn run(config: ClientConfig, shutdown: CancellationToken) -> Result<(), ClientError> {
30 let hash_hex = sha224_hex(&config.client.password);
32
33 let tls_config = connector::build_tls_config(&config.client.tls)?;
35 let tls_connector = TlsConnector::from(Arc::new(tls_config));
36 let sni = connector::resolve_sni(&config.client.tls, &config.client.remote)?;
37
38 let state = Arc::new(ClientState {
39 hash_hex,
40 remote_addr: config.client.remote.clone(),
41 tls_connector,
42 sni,
43 tcp_config: config.client.tcp.clone(),
44 tls_handshake_timeout: Duration::from_secs(DEFAULT_TLS_HANDSHAKE_TIMEOUT_SECS),
45 });
46
47 let listener = TcpListener::bind(&config.client.listen).await?;
49 info!(listen = %config.client.listen, remote = %config.client.remote, "trojan client started");
50
51 loop {
52 tokio::select! {
53 result = listener.accept() => {
54 match result {
55 Ok((stream, peer)) => {
56 let state = state.clone();
57 tokio::spawn(async move {
58 handler::handle_socks5_conn(stream, peer, state).await;
59 });
60 }
61 Err(e) => {
62 error!(error = %e, "failed to accept connection");
63 }
64 }
65 }
66 _ = shutdown.cancelled() => {
67 info!("shutting down client");
68 break;
69 }
70 }
71 }
72
73 Ok(())
74}