1cfg_if::cfg_if! {
2 if #[cfg(feature = "acl")] {
3 mod acl;
4 pub use acl::AccessControl;
5 }
6}
7
8#[cfg(feature = "sockshub")]
16mod config;
17#[cfg(feature = "sockshub")]
18pub use config::{ArgVerbosity, Config};
19
20#[cfg(feature = "httpproxy")]
21mod tokiort;
22#[cfg(feature = "httpproxy")]
23pub use socks5_impl::protocol::{Address, Command, ProxyParameters, ProxyType, UserKey};
24#[cfg(feature = "sockshub")]
25use socks5_impl::protocol::{AsyncStreamOperation, Reply, Response as SocksResponse, UdpHeader};
26#[cfg(feature = "sockshub")]
27use tokio::{io::AsyncWriteExt, net::UdpSocket};
28#[cfg(feature = "httpproxy")]
29use tokiort::TokioIo;
30
31#[cfg(feature = "sockshub")]
32mod http2socks;
33#[cfg(feature = "sockshub")]
34mod socks2socks;
35
36#[cfg(feature = "httpproxy")]
37mod httpproxy;
38#[cfg(feature = "httpproxy")]
39pub use httpproxy::run_http_service;
40
41#[cfg(feature = "sockshub")]
42mod api;
43#[cfg(feature = "sockshub")]
44mod dump_logger;
45#[cfg(feature = "sockshub")]
46mod ffi;
47
48#[cfg(feature = "httpproxy")]
49pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
50#[cfg(feature = "sockshub")]
51pub type Result<T, E = BoxError> = std::result::Result<T, E>;
52
53#[cfg(feature = "sockshub")]
54pub async fn main_entry<F>(config: &Config, cancel_token: tokio_util::sync::CancellationToken, callback: Option<F>) -> Result<(), BoxError>
55where
56 F: FnOnce(std::net::SocketAddr) + Send + Sync + 'static,
57{
58 if config.remote_server.proxy_type != ProxyType::Socks5 {
59 return Err("remote server must be socks5".into());
60 }
61 if let Some(middle_server) = &config.middle_server {
62 if middle_server.proxy_type != ProxyType::Socks5 {
63 return Err("middle server must be socks5".into());
64 }
65 }
66 match config.listen_proxy_role.proxy_type {
67 ProxyType::Http => http2socks::main_entry(config, cancel_token, callback).await,
68 ProxyType::Socks5 => socks2socks::main_entry(config, cancel_token, callback).await,
69 ProxyType::None => mixed_main_entry(config, cancel_token, callback).await,
70 _ => Err("listen proxy must be http, socks5, or none (mixed)".into()),
71 }
72}
73
74#[cfg(feature = "sockshub")]
75pub async fn mixed_main_entry<F>(
76 config: &Config,
77 cancel_token: tokio_util::sync::CancellationToken,
78 callback: Option<F>,
79) -> Result<(), BoxError>
80where
81 F: FnOnce(std::net::SocketAddr) + Send + Sync + 'static,
82{
83 let listen_addr: std::net::SocketAddr = config.listen_proxy_role.addr.clone().try_into()?;
84
85 let listener = tokio::net::TcpListener::bind(listen_addr).await?;
86
87 if let Some(callback) = callback {
88 callback(listener.local_addr()?);
89 } else {
90 log::info!("Listening on {}", config.listen_proxy_role);
91 }
92
93 let config = std::sync::Arc::new(config.clone());
94
95 loop {
96 let config = config.clone();
97 tokio::select! {
98 _ = cancel_token.cancelled() => {
99 log::info!("quit signal received");
100 break;
101 }
102 result = listener.accept() => {
103 let (stream, incoming) = result?;
104 tokio::task::spawn(async move {
105 if let Err(err) = service_connection(stream, config).await {
106 log::error!("service on incoming {incoming} error: {err}");
107 }
108 });
109 }
110 }
111 }
112 Ok(())
113}
114
115#[cfg(feature = "sockshub")]
116async fn service_connection(mut stream: tokio::net::TcpStream, config: std::sync::Arc<Config>) -> Result<(), BoxError> {
117 let mut peek_buf = [0u8; 10];
118 let n = stream.peek(&mut peek_buf).await?;
119 if n == 0 {
120 return Ok(());
121 }
122
123 match peek_buf[0] {
124 0x05 => {
125 log::trace!("socks5 client detected");
126 let credentials = config.get_listen_credentials();
127 let auth: socks5_impl::server::auth::AuthAdaptor = if credentials.to_string().is_empty() {
128 std::sync::Arc::new(socks5_impl::server::auth::NoAuth)
129 } else {
130 std::sync::Arc::new(socks5_impl::server::auth::UserKeyAuth::from(credentials))
131 };
132 let req = socks5_impl::server::socks5_service_handshake(&mut stream, auth).await?;
133 match req.command {
134 Command::Connect => crate::socks2socks::handle_socks5_connect(stream, req.address, config.remote_server.clone()).await,
135 Command::Bind => {
136 let resp = SocksResponse::new(Reply::CommandNotSupported, Address::unspecified());
137 resp.write_to_async_stream(&mut stream).await.map_err(std_io_error_other)?;
138 stream.shutdown().await.map_err(std_io_error_other)?;
139 Ok(())
140 }
141 Command::UdpAssociate => {
142 let client_ip = stream.local_addr()?.ip();
143 let udp_listener = UdpSocket::bind(std::net::SocketAddr::from((client_ip, 0))).await?;
144 let listen_addr = udp_listener.local_addr()?;
145 let resp = SocksResponse::new(Reply::Succeeded, Address::from(listen_addr));
146 resp.write_to_async_stream(&mut stream).await.map_err(std_io_error_other)?;
147
148 let buf_size = 1500 - UdpHeader::max_serialized_len();
149 let listen_udp = std::sync::Arc::new(socks5_impl::server::AssociatedUdpSocket::from((udp_listener, buf_size)));
150 let s5_udp_client =
151 create_s5_udp_client(config.remote_server.clone(), CONNECT_TIMEOUT, config.middle_server.clone()).await?;
152
153 crate::socks2socks::run_udp_associate_relay(listen_udp, s5_udp_client, &mut stream, listen_addr).await
154 }
155 }
156 }
157 0x04 => {
158 log::warn!("socks4 client detected, but only SOCKS5/HTTP mixed mode is supported");
159 let _ = stream.shutdown().await;
160 Ok(())
161 }
162 _ => {
163 let first_bytes = &peek_buf[..n];
164 let is_http = if let Ok(text) = std::str::from_utf8(first_bytes) {
165 let methods = [
166 "CONNECT", "GET", "POST", "HEAD", "PUT", "OPTIONS", "DELETE", "TRACE", "PATCH", "LOCK", "UNLOCK", "PROPFIND", "MKCOL",
167 "COPY", "MOVE",
168 ];
169 methods
170 .iter()
171 .any(|method| text[..method.len()].eq_ignore_ascii_case(method) && text.as_bytes().get(method.len()) == Some(&b' '))
172 } else {
173 false
174 };
175
176 if !is_http {
177 log::warn!("unknown client type detected, first byte: 0x{:02x}", first_bytes[0]);
178 let _ = stream.shutdown().await;
179 return Ok(());
180 }
181 log::trace!("http client detected by method peek");
182
183 let server = config.remote_server.clone();
184 let credentials = config.listen_proxy_role.credentials.clone().unwrap_or_default();
185 let middle_server = config.middle_server.clone();
186 let middle_server_for_conn = middle_server.clone();
187 let connector: crate::httpproxy::HttpConnector = std::sync::Arc::new(move |dst| {
188 let server = server.clone();
189 let middle_server = middle_server_for_conn.clone();
190 Box::pin(async move {
191 let stream = crate::create_s5_connect(server, CONNECT_TIMEOUT, &dst, middle_server).await?;
192 Ok(Box::new(stream) as crate::httpproxy::BoxedStream)
193 })
194 });
195 httpproxy::run_http_service(stream, connector, credentials).await?;
196 Ok(())
197 }
198 }
199}
200
201#[cfg(feature = "sockshub")]
202pub(crate) const CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
203
204#[cfg(feature = "sockshub")]
205pub(crate) async fn create_s5_connect(
206 server: ProxyParameters,
207 dur: std::time::Duration,
208 dst: &socks5_impl::protocol::Address,
209 middle_server: Option<ProxyParameters>,
210) -> std::io::Result<tokio::io::BufStream<tokio::net::TcpStream>> {
211 let auth = server.credentials.clone();
212 let mut stream = connect_proxy_stream(server, dur, middle_server).await?;
213 socks5_impl::client::connect(&mut stream, dst, auth)
214 .await
215 .map_err(std_io_error_other)?;
216 Ok(stream)
217}
218
219#[cfg(feature = "sockshub")]
220async fn connect_proxy_stream(
221 server: ProxyParameters,
222 dur: std::time::Duration,
223 middle_server: Option<ProxyParameters>,
224) -> std::io::Result<tokio::io::BufStream<tokio::net::TcpStream>> {
225 let stream = if let Some(middle_server) = middle_server {
226 let middle_addr: std::net::SocketAddr = middle_server.addr.try_into()?;
227 let stream = tokio::time::timeout(dur, tokio::net::TcpStream::connect(middle_addr)).await??;
228 let mut stream = tokio::io::BufStream::new(stream);
229 let middle_auth = middle_server.credentials.clone();
230 socks5_impl::client::connect(&mut stream, server.addr, middle_auth)
231 .await
232 .map_err(std_io_error_other)?;
233 stream
234 } else {
235 let server_addr: std::net::SocketAddr = server.addr.try_into()?;
236 let stream = tokio::time::timeout(dur, tokio::net::TcpStream::connect(server_addr)).await??;
237 tokio::io::BufStream::new(stream)
238 };
239 Ok(stream)
240}
241
242#[cfg(feature = "sockshub")]
243pub(crate) async fn create_s5_udp_client(
244 server: ProxyParameters,
245 dur: std::time::Duration,
246 middle_server: Option<ProxyParameters>,
247) -> std::io::Result<socks5_impl::client::SocksUdpClient> {
248 let stream = connect_proxy_stream(server.clone(), dur, middle_server).await?;
249 let client_addr = if server.addr.is_ipv4() { "0.0.0.0:0" } else { "[::]:0" };
250 let client = tokio::net::UdpSocket::bind(client_addr).await?;
251 let auth = server.credentials.clone();
252 socks5_impl::client::SocksDatagram::udp_associate(stream, client, auth)
253 .await
254 .map_err(std_io_error_other)
255}
256
257#[cfg(feature = "httpproxy")]
258pub(crate) fn std_io_error_other<E: Into<BoxError>>(err: E) -> std::io::Error {
259 std::io::Error::other(err)
260}
261
262