1pub mod mc;
2
3use dashmap::DashMap;
4use futures::future::BoxFuture;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::time::{Duration, Instant};
8use t_port::{Protocol, identify, tunnel};
9use tokio::net::{TcpListener, TcpStream};
10use tokio::time::timeout;
11
12pub type WakeupCallback = Arc<dyn Fn() -> BoxFuture<'static, ()> + Send + Sync>;
13
14#[derive(Clone, Copy, Debug, PartialEq)]
15pub enum WakeupCondition {
16 Motd,
17 Join,
18 Disabled,
19}
20
21pub struct Config {
22 pub listen: String,
23 pub web: Option<String>,
24 pub mc: String,
25 pub wakeup_on: WakeupCondition,
26 pub debug: bool,
27 pub on_wakeup: Option<WakeupCallback>,
28 pub is_waking: AtomicBool,
29
30 pub msg_motd: String,
31 pub msg_starting: String,
32 pub msg_waitlist: String,
33 pub msg_online: String,
34 pub msg_timeout: String,
35}
36
37pub struct UserHistory {
38 pub attempts: u32,
39 pub last_seen: Instant,
40}
41
42pub async fn run(cfg: Arc<Config>) -> Result<(), Box<dyn std::error::Error>> {
43 let history: Arc<DashMap<String, UserHistory>> = Arc::new(DashMap::new());
44 let listener = TcpListener::bind(&cfg.listen).await?;
45 println!("Proxy active on {}", cfg.listen);
46
47 let history_gc = Arc::clone(&history);
48 tokio::spawn(async move {
49 let mut interval = tokio::time::interval(Duration::from_secs(60));
50 loop {
51 interval.tick().await;
52 history_gc.retain(|_, v| v.last_seen.elapsed() < Duration::from_secs(300));
53 }
54 });
55
56 loop {
57 let (socket, addr) = listener.accept().await?;
58 let cfg = Arc::clone(&cfg);
59 let history = Arc::clone(&history);
60 let ip = addr.ip().to_string();
61
62 tokio::spawn(async move {
63 let _ = process(socket, cfg, history, ip).await;
64 });
65 }
66}
67
68async fn process(
69 mut socket: TcpStream,
70 cfg: Arc<Config>,
71 history: Arc<DashMap<String, UserHistory>>,
72 ip: String,
73) -> tokio::io::Result<()> {
74 let mut head = [0u8; 8];
75 let n = timeout(Duration::from_secs(2), socket.peek(&mut head[..]))
76 .await
77 .map_err(|_| std::io::Error::new(std::io::ErrorKind::TimedOut, "peek timeout"))??;
78
79 match identify(&head[..n]) {
80 Protocol::Http => {
81 if let Some(web_target) = &cfg.web {
82 tunnel(socket, web_target.clone()).await
83 } else {
84 if cfg.debug {
85 println!("HTTP request received but no web target configured. Closing.");
86 }
87 Ok(())
88 }
89 }
90 Protocol::Binary => {
91 if let Ok(Ok(mut target)) =
92 timeout(Duration::from_secs(1), TcpStream::connect(&cfg.mc)).await
93 {
94 cfg.is_waking.store(false, Ordering::SeqCst);
95 tokio::io::copy_bidirectional(&mut socket, &mut target).await?;
96 return Ok(());
97 }
98
99 if cfg.on_wakeup.is_some() {
100 let state = mc::inspect_handshake(&socket).await;
101 mc::handler::McHandler::send_fallback(&mut socket, state, cfg, history, ip).await
102 } else {
103 if cfg.debug {
104 println!(
105 "Connection to {} failed and no on-wakeup command set. Closing.",
106 cfg.mc
107 );
108 }
109 Ok(())
110 }
111 }
112 }
113}