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::utils::{
16 ConnectorPacket, ConnectorPacketType, ConnectorStream, HelloPacket, HelloPacketType,
17 certs::{NoCertVerification, SkipQuicServerVerification},
18 http::write_http_resp,
19 read_string_from_stream,
20};
21
22pub struct Options {
23 pub proxy: SocketAddr,
24 pub local: SocketAddr,
25 pub local_ssl: Option<SocketAddr>,
26 pub token: u128,
27 pub redirect_ssl: bool,
28 pub serve_files: bool,
29 pub files_index: bool,
30 pub quic: bool,
31
32 pub consts: Consts,
33}
34
35#[derive(Debug, Clone)]
36pub struct Consts {
37 pub max_req_time: u128,
38 pub error_html: &'static str,
39 pub list_html: &'static str,
40}
41
42#[derive(Debug)]
43#[allow(dead_code)]
44struct TunnelSettings {
45 proxy_addr: SocketAddr,
46 ssl_addr: SocketAddr,
47 nonssl_addr: SocketAddr,
48 use_quic: bool,
49
50 serve_files: bool,
51 files_index: bool,
52
53 consts: Arc<Consts>,
54}
55
56#[derive(Clone)]
57struct ConnectionOpener {
58 quic_endpoint: Endpoint,
59 quic_connection: Option<Connection>,
60 tls_connector: Arc<TlsConnector>,
61}
62
63pub async fn spawn_connector(options: Options) {
64 loop {
65 if let Err(e) = connector(&options).await {
66 tracing::error!("Connector error: {e}");
67 }
68
69 tokio::time::sleep(std::time::Duration::from_secs(1)).await;
70 }
71}
72
73async fn connector(options: &Options) -> Result<()> {
74 let mut endpoint = Endpoint::client(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0))?;
75 endpoint.set_default_client_config(ClientConfig::new(Arc::new(QuicClientConfig::try_from(
76 rustls::ClientConfig::builder()
77 .dangerous()
78 .with_custom_certificate_verifier(SkipQuicServerVerification::new())
79 .with_no_client_auth(),
80 )?)));
81
82 let config = tokio_rustls::rustls::ClientConfig::builder()
83 .dangerous()
84 .with_custom_certificate_verifier(Arc::new(NoCertVerification))
85 .with_no_client_auth();
86 let connector = TlsConnector::from(Arc::new(config));
87
88 let mut opener = ConnectionOpener {
89 quic_endpoint: endpoint,
90 quic_connection: None,
91 tls_connector: Arc::new(connector),
92 };
93
94 if options.quic {
95 opener.quic_connection = Some(
96 opener
97 .quic_endpoint
98 .connect(options.proxy, "proxy.lan")?
99 .await?,
100 );
101 }
102
103 let opener = Arc::new(opener);
104 let mut stream = if options.quic {
105 let quic_bi = opener
106 .quic_connection
107 .as_ref()
108 .ok_or(anyhow!("Quic Connection ref get"))?
109 .open_bi()
110 .await
111 .map_err(|e| anyhow!("failed to open stream: {}", e))?;
112
113 ConnectorStream::Quic(quic_bi)
114 } else {
115 let stream = TcpStream::connect(&options.proxy).await?;
116 stream.set_nodelay(true)?;
117 let stream = opener
118 .tls_connector
119 .connect(
120 rustls::pki_types::ServerName::try_from("proxy.lan")?,
121 stream,
122 )
123 .await?;
124
125 ConnectorStream::TcpTlsClient(Box::new(stream))
126 };
127
128 let mut buf = [0; ConnectorPacket::buf_size()];
129
130 let mut hello_packet = HelloPacket {
131 hp_type: HelloPacketType::Connector,
132 token: options.token,
133 own_ssl: options.local_ssl.is_some(),
134 redirect_ssl: options.redirect_ssl,
135 tunnel_id: 0,
136 };
137
138 stream.write_all(&hello_packet.to_buf()).await?;
139 let res = stream.read_exact(&mut buf).await;
140 if res.is_err() {
141 tracing::error!("Connector read error: {res:?}. Closing connection.");
142 return Ok(());
143 }
144 let packet = ConnectorPacket::from_buf(&buf);
145 match packet.packet_type {
146 ConnectorPacketType::ConnectorConnected => {}
147 ConnectorPacketType::Close => {
148 let reason = read_string_from_stream(&mut stream).await?;
149 tracing::error!("Closing connector! Close reason: {reason}");
150 return Ok(());
151 }
152 _ => {
153 tracing::error!("Closing connector! Wrong packet response!");
154 return Ok(());
155 }
156 }
157
158 let nonssl_port = stream.read_u16().await?;
159 let ssl_port = stream.read_u16().await?;
160 let domain = read_string_from_stream(&mut stream).await?;
161 tracing::info!(
162 "Access through:\n - http://{domain}:{nonssl_port}\n - https://{domain}:{ssl_port}"
163 );
164
165 hello_packet.hp_type = HelloPacketType::Tunnel;
166
167 let mut last_ping = tokio::time::interval_at(
168 Instant::now() + Duration::from_secs(30),
169 Duration::from_secs(30),
170 );
171
172 let consts = Arc::new(options.consts.clone());
173 loop {
174 tokio::select! {
175 res = stream.read_exact(&mut buf) => {
176 if res.is_err() {
177 tracing::error!("Connector read error: {res:?}. Closing connection.");
178 return Ok(());
179 }
180
181 let packet = ConnectorPacket::from_buf(&buf);
182 if packet.packet_type == ConnectorPacketType::Ping {
183 stream.write_u8(0x69).await?;
184 last_ping.reset();
185
186 continue; } else if packet.packet_type == ConnectorPacketType::Close {
188 let reason = read_string_from_stream(&mut stream).await?;
189 tracing::error!("Closing connector! Close reason: {reason}");
190 return Ok(());
191 }
192
193 let opener = opener.clone();
194 let requested_time = Instant::now();
195 let settings = TunnelSettings {
196 proxy_addr: options.proxy,
197 ssl_addr: options.local_ssl.unwrap_or(options.local),
198 nonssl_addr: options.local,
199 use_quic: options.quic,
200
201 serve_files: options.serve_files,
202 files_index: options.files_index,
203 consts: consts.clone()
204 };
205
206 hello_packet.tunnel_id = packet.tunnel_id;
207 let hello_packet = hello_packet.to_buf();
208
209 tokio::task::spawn(async move {
210 let res = spawn_tunnel(
211 opener,
212 hello_packet,
213 settings,
214 packet.ssl,
215 requested_time,
216 )
217 .await;
218
219 if let Err(e) = res {
220 tracing::error!("Tunnel Error: {e}");
221 }
222 });
223 }
224 _ = last_ping.tick() => {
225 tracing::error!("No ping for 30s! Closing connector");
226 return Ok(());
227 }
228 }
229 }
230}
231
232async fn spawn_tunnel(
233 opener: Arc<ConnectionOpener>,
234 hello_packet: [u8; HelloPacket::buf_size()],
235 settings: TunnelSettings,
236 ssl: bool,
237 request_time: Instant,
238) -> Result<()> {
239 if request_time.elapsed().as_millis() > settings.consts.max_req_time {
240 return Err(anyhow!("Requested time exceeded max request time."));
241 }
242
243 let mut tunnel_stream = if settings.use_quic {
244 let quic_bi = opener
245 .quic_connection
246 .as_ref()
247 .ok_or(anyhow::anyhow!("Quic Connection ref get"))?
248 .open_bi()
249 .await
250 .map_err(|e| anyhow!("failed to open stream: {}", e))?;
251 ConnectorStream::Quic(quic_bi)
252 } else {
253 let stream = TcpStream::connect(settings.proxy_addr).await?;
254 stream.set_nodelay(true)?;
255 let stream = opener
256 .tls_connector
257 .connect(
258 rustls::pki_types::ServerName::try_from("proxy.lan")?,
259 stream,
260 )
261 .await?;
262
263 ConnectorStream::TcpTlsClient(Box::new(stream))
264 };
265
266 tunnel_stream.write_all(&hello_packet).await?;
267 if settings.serve_files {
268 _ = super::serve::serve_files(&mut tunnel_stream, settings.files_index, &settings.consts)
269 .await;
270 tunnel_stream.flush().await?;
271 _ = tunnel_stream.shutdown().await;
272 return Ok(());
273 }
274
275 let local_addr = match ssl {
276 true => settings.ssl_addr,
277 false => settings.nonssl_addr,
278 };
279
280 let Ok(mut local_stream) = TcpStream::connect(local_addr).await else {
281 write_http_resp(
282 &mut tunnel_stream,
283 500,
284 &settings
285 .consts
286 .error_html
287 .replace("{MSG}", "Local server not running!"),
288 "text/html",
289 )
290 .await?;
291 _ = tunnel_stream.shutdown().await;
292
293 return Ok(());
294 };
295
296 local_stream.set_nodelay(true)?;
297 _ = tokio::io::copy_bidirectional(&mut local_stream, &mut tunnel_stream).await;
298 _ = local_stream.shutdown().await;
299 _ = tunnel_stream.shutdown().await;
300 Ok(())
301}