1use anyhow::{Result, anyhow};
2use quinn::{ClientConfig, Connection, Endpoint, crypto::rustls::QuicClientConfig};
3use std::{
4 net::{IpAddr, Ipv4Addr, SocketAddr},
5 sync::Arc,
6 time::Duration,
7};
8use tokio::{
9 io::{AsyncReadExt as _, AsyncWriteExt as _},
10 net::TcpStream,
11 time::Instant,
12};
13use tokio_rustls::TlsConnector;
14
15use crate::{
16 get_version,
17 utils::{
18 ConnectorPacket, ConnectorPacketType, ConnectorStream, HelloPacket, HelloPacketType,
19 certs::{NoCertVerification, SkipQuicServerVerification},
20 http::write_http_resp,
21 read_string_from_stream,
22 ssh::{SshPacketHeader, SshPacketType},
23 },
24};
25
26pub struct Options {
27 pub proxy: SocketAddr,
28 pub local: SocketAddr,
29 pub local_ssl: Option<SocketAddr>,
30 pub token: u128,
31 pub redirect_ssl: bool,
32 pub serve_files: bool,
33 pub files_index: bool,
34 pub quic: bool,
35 pub ssh_cmd: Option<String>,
36
37 pub consts: Consts,
38}
39
40#[derive(Debug, Clone)]
41pub struct Consts {
42 pub max_req_time: u128,
43 pub error_html: &'static str,
44 pub list_html: &'static str,
45}
46
47#[derive(Debug)]
48#[allow(dead_code)]
49struct TunnelSettings {
50 proxy_addr: SocketAddr,
51 ssl_addr: SocketAddr,
52 nonssl_addr: SocketAddr,
53 use_quic: bool,
54 ssh_cmd: Option<String>,
55
56 serve_files: bool,
57 files_index: bool,
58
59 consts: Arc<Consts>,
60}
61
62#[derive(Clone)]
63struct ConnectionOpener {
64 quic_endpoint: Endpoint,
65 quic_connection: Option<Connection>,
66 tls_connector: Arc<TlsConnector>,
67}
68
69pub async fn spawn_connector(options: Options) {
70 loop {
71 if let Err(e) = connector(&options).await {
72 tracing::error!("Connector error: {e}");
73 }
74
75 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
76 }
77}
78
79async fn connector(options: &Options) -> Result<()> {
80 let mut endpoint = Endpoint::client(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0))?;
81 endpoint.set_default_client_config(ClientConfig::new(Arc::new(QuicClientConfig::try_from(
82 rustls::ClientConfig::builder()
83 .dangerous()
84 .with_custom_certificate_verifier(SkipQuicServerVerification::new())
85 .with_no_client_auth(),
86 )?)));
87
88 let config = tokio_rustls::rustls::ClientConfig::builder()
89 .dangerous()
90 .with_custom_certificate_verifier(Arc::new(NoCertVerification))
91 .with_no_client_auth();
92 let connector = TlsConnector::from(Arc::new(config));
93
94 let mut opener = ConnectionOpener {
95 quic_endpoint: endpoint,
96 quic_connection: None,
97 tls_connector: Arc::new(connector),
98 };
99
100 if options.quic {
101 opener.quic_connection = Some(
102 opener
103 .quic_endpoint
104 .connect(options.proxy, "proxy.lan")?
105 .await?,
106 );
107 }
108
109 let opener = Arc::new(opener);
110 let mut stream = if options.quic {
111 let quic_bi = opener
112 .quic_connection
113 .as_ref()
114 .ok_or(anyhow!("Quic Connection ref get"))?
115 .open_bi()
116 .await
117 .map_err(|e| anyhow!("failed to open stream: {}", e))?;
118
119 ConnectorStream::Quic(quic_bi)
120 } else {
121 let stream = TcpStream::connect(&options.proxy).await?;
122 stream.set_nodelay(true)?;
123 let stream = opener
124 .tls_connector
125 .connect(
126 rustls::pki_types::ServerName::try_from("proxy.lan")?,
127 stream,
128 )
129 .await?;
130
131 ConnectorStream::TcpTlsClient(Box::new(stream))
132 };
133
134 let mut buf = [0; ConnectorPacket::buf_size()];
135
136 let mut hello_packet = HelloPacket {
137 hp_type: HelloPacketType::Connector,
138 token: options.token,
139 own_ssl: options.local_ssl.is_some(),
140 redirect_ssl: options.redirect_ssl,
141 ssh_enabled: options.ssh_cmd.is_some(),
142 tunnel_id: 0,
143 version: get_version(),
144 };
145
146 stream.write_all(&hello_packet.to_buf()).await?;
147 let res = stream.read_exact(&mut buf).await;
148 if res.is_err() {
149 tracing::error!("Connector read error: {res:?}. Closing connection.");
150 return Ok(());
151 }
152 let packet = ConnectorPacket::from_buf(&buf);
153 match packet.packet_type {
154 ConnectorPacketType::ConnectorConnected => {}
155 ConnectorPacketType::Close => {
156 let reason = read_string_from_stream(&mut stream).await?;
157 tracing::error!("Closing connector! Close reason: {reason}");
158 return Ok(());
159 }
160 _ => {
161 tracing::error!("Closing connector! Wrong packet response!");
162 return Ok(());
163 }
164 }
165
166 let nonssl_port = stream.read_u16().await?;
167 let ssl_port = stream.read_u16().await?;
168 let domain = read_string_from_stream(&mut stream).await?;
169 tracing::info!(
170 "Access through:\n - http://{domain}:{nonssl_port}\n - https://{domain}:{ssl_port}"
171 );
172
173 hello_packet.hp_type = HelloPacketType::Tunnel;
174
175 let mut last_ping = tokio::time::interval_at(
176 Instant::now() + Duration::from_secs(30),
177 Duration::from_secs(30),
178 );
179
180 let consts = Arc::new(options.consts.clone());
181 loop {
182 tokio::select! {
183 res = stream.read_exact(&mut buf) => {
184 if res.is_err() {
185 tracing::error!("Connector read error: {res:?}. Closing connection.");
186 return Ok(());
187 }
188
189 let packet = ConnectorPacket::from_buf(&buf);
190 if packet.packet_type == ConnectorPacketType::Ping {
191 stream.write_u8(0x69).await?;
192 last_ping.reset();
193
194 continue; } else if packet.packet_type == ConnectorPacketType::Close {
196 let reason = read_string_from_stream(&mut stream).await?;
197 tracing::error!("Closing connector! Close reason: {reason}");
198 return Ok(());
199 }
200
201 let opener = opener.clone();
202 let requested_time = Instant::now();
203 let settings = TunnelSettings {
204 proxy_addr: options.proxy,
205 ssl_addr: options.local_ssl.unwrap_or(options.local),
206 nonssl_addr: options.local,
207 use_quic: options.quic,
208 ssh_cmd: options.ssh_cmd.clone(),
209
210 serve_files: options.serve_files,
211 files_index: options.files_index,
212 consts: consts.clone()
213 };
214
215 hello_packet.tunnel_id = packet.tunnel_id;
216 let hello_packet = hello_packet.to_buf();
217
218 tokio::task::spawn(async move {
219 let res = if packet.ssh {
220 spawn_ssh_tunnel(
221 opener,
222 hello_packet,
223 settings,
224 requested_time,
225 )
226 .await
227 } else {
228 spawn_tunnel(
229 opener,
230 hello_packet,
231 settings,
232 packet.ssl,
233 requested_time,
234 )
235 .await
236 };
237
238 if let Err(e) = res {
239 tracing::error!("Tunnel Error: {e}");
240 }
241 });
242 }
243 _ = last_ping.tick() => {
244 tracing::error!("No ping for 30s! Closing connector");
245 return Ok(());
246 }
247 }
248 }
249}
250
251async fn establish_connection(
252 opener: Arc<ConnectionOpener>,
253 hello_packet: [u8; HelloPacket::buf_size()],
254 settings: &TunnelSettings,
255 request_time: Instant,
256) -> Result<ConnectorStream> {
257 if request_time.elapsed().as_millis() > settings.consts.max_req_time {
258 return Err(anyhow!("Requested time exceeded max request time."));
259 }
260
261 let mut tunnel_stream = if settings.use_quic {
262 let quic_bi = opener
263 .quic_connection
264 .as_ref()
265 .ok_or(anyhow::anyhow!("Quic Connection ref get"))?
266 .open_bi()
267 .await
268 .map_err(|e| anyhow!("failed to open stream: {}", e))?;
269 ConnectorStream::Quic(quic_bi)
270 } else {
271 let stream = TcpStream::connect(settings.proxy_addr).await?;
272 stream.set_nodelay(true)?;
273 let stream = opener
274 .tls_connector
275 .connect(
276 rustls::pki_types::ServerName::try_from("proxy.lan")?,
277 stream,
278 )
279 .await?;
280
281 ConnectorStream::TcpTlsClient(Box::new(stream))
282 };
283
284 tunnel_stream.write_all(&hello_packet).await?;
285 Ok(tunnel_stream)
286}
287
288async fn spawn_tunnel(
289 opener: Arc<ConnectionOpener>,
290 hello_packet: [u8; HelloPacket::buf_size()],
291 settings: TunnelSettings,
292 ssl: bool,
293 request_time: Instant,
294) -> Result<()> {
295 let mut tunnel_stream =
296 establish_connection(opener, hello_packet, &settings, request_time).await?;
297
298 if settings.serve_files {
299 _ = super::serve::serve_files(&mut tunnel_stream, settings.files_index, &settings.consts)
300 .await;
301 tunnel_stream.flush().await?;
302 _ = tunnel_stream.shutdown().await;
303 return Ok(());
304 }
305
306 let local_addr = match ssl {
307 true => settings.ssl_addr,
308 false => settings.nonssl_addr,
309 };
310
311 let Ok(mut local_stream) = TcpStream::connect(local_addr).await else {
312 write_http_resp(
313 &mut tunnel_stream,
314 500,
315 &settings
316 .consts
317 .error_html
318 .replace("{MSG}", "Local server not running!"),
319 "text/html",
320 )
321 .await?;
322 _ = tunnel_stream.shutdown().await;
323
324 return Ok(());
325 };
326
327 local_stream.set_nodelay(true)?;
328 _ = tokio::io::copy_bidirectional(&mut local_stream, &mut tunnel_stream).await;
329 _ = local_stream.shutdown().await;
330 _ = tunnel_stream.shutdown().await;
331 Ok(())
332}
333
334async fn spawn_ssh_tunnel(
335 opener: Arc<ConnectionOpener>,
336 hello_packet: [u8; HelloPacket::buf_size()],
337 settings: TunnelSettings,
338 request_time: Instant,
339) -> Result<()> {
340 let Some(ref ssh_cmd) = settings.ssh_cmd else {
341 return Err(anyhow!("Ssh tunnel not enabled by client!"));
342 };
343
344 let mut tunnel_stream =
345 establish_connection(opener, hello_packet, &settings, request_time).await?;
346
347 let mut header_buf = [0; SshPacketHeader::HEADER_LENGTH];
348 let mut buf = [0u8; 4096];
349 tunnel_stream.read_exact(&mut header_buf).await?;
350 let header = SshPacketHeader::from_buf(&header_buf);
351
352 let tunnel_user = if header.packet_type == SshPacketType::User {
353 tunnel_stream
354 .read_exact(&mut buf[..header.length as usize])
355 .await?;
356
357 core::str::from_utf8(&buf[..header.length as usize])?
358 } else {
359 return Err(anyhow!("Wrong first ssh tunnel packet!"));
360 };
361
362 let (mut pty, pts) = pty_process::open()?;
363 let cmd = pty_process::Command::new(ssh_cmd).env("PROXY_USER", tunnel_user);
364 let mut child = cmd.spawn(pts)?;
365
366 loop {
367 tokio::select! {
368 recv = tunnel_stream.read_exact(&mut header_buf) => {
369 if let Ok(n) = recv {
370 if n == 0 {
371 break;
372 }
373
374 let header = SshPacketHeader::from_buf(&header_buf);
375 match header.packet_type {
376 crate::utils::ssh::SshPacketType::PtyResize => {
377 let rows = tunnel_stream.read_u16().await?;
378 let cols = tunnel_stream.read_u16().await?;
379 pty.resize(pty_process::Size::new(rows, cols))?;
380 },
381 crate::utils::ssh::SshPacketType::Data => {
382 tunnel_stream.read_exact(&mut buf[..header.length as usize]).await?;
383 pty.write_all(&buf[..header.length as usize]).await?;
384
385 },
386 _ => {}
387 }
388 }
389 }
390 res = pty.read(&mut buf) => {
391 if let Ok(n) = res {
392 if n == 0 {
393 break;
394 }
395
396 tunnel_stream
397 .write_all(
398 &SshPacketHeader {
399 packet_type: super::ssh::SshPacketType::Data,
400 length: n as u32,
401 }
402 .to_buf(),
403 )
404 .await?;
405 tunnel_stream.write_all(&buf[..n]).await?;
406 } else {
407 break;
408 }
409 }
410 p_res = child.wait() => {
411 if p_res.is_err() {
412 break;
413 }
414
415 let _p_res = p_res?;
416 break;
417 }
418 }
419 }
420
421 _ = tunnel_stream.shutdown().await;
422 Ok(())
423}