Skip to main content

trojan_client/
lib.rs

1//! Trojan client with SOCKS5 proxy.
2//!
3//! This crate provides a local SOCKS5 proxy that forwards connections through
4//! the Trojan protocol over TLS to a remote trojan server.
5
6pub 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
28/// Run the trojan client with the given configuration.
29pub async fn run(config: ClientConfig, shutdown: CancellationToken) -> Result<(), ClientError> {
30    // Compute password hash
31    let hash_hex = sha224_hex(&config.client.password);
32
33    // Build TLS config
34    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    // Bind SOCKS5 listener
48    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}