fkm_proxy/utils/
client.rs

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; // ping/pong
187                } 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}