From d742eca6325810c8d056ba8c0c5342c2720a058c Mon Sep 17 00:00:00 2001
From: timvisee <tim@visee.me>
Date: Sun, 7 Nov 2021 18:07:24 +0100
Subject: [PATCH] Hijack login start packet to disconnect payer if server is
 sleeping

---
 src/config.rs   |  8 ++++++--
 src/main.rs     | 29 +++++++++++++++++++++++++++--
 src/protocol.rs |  1 +
 3 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/src/config.rs b/src/config.rs
index e20e244..87e4cdd 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -5,7 +5,11 @@ pub const ADDRESS_PUBLIC: &str = "127.0.0.1:9090";
 pub const ADDRESS_PROXY: &str = "127.0.0.1:9091";
 
 /// Server description shown when server is starting.
-pub const LABEL_SERVER_SLEEPING: &str = "Server sleeping...\nJoin to start it up §c♥";
+pub const LABEL_SERVER_SLEEPING: &str = "☠ Server is sleeping...\n§2☻ Join to start it up";
 
 /// Server description shown when server is starting.
-pub const LABEL_SERVER_STARTING: &str = "Server starting...\nPlease wait §c♥";
+pub const LABEL_SERVER_STARTING: &str = "§2☻ Server is starting...\n§7⌛ Please wait...";
+
+/// Kick message shown when user tries to connect to starting server.
+pub const LABEL_SERVER_STARTING_MESSAGE: &str =
+    "Server is starting... §c♥§r\n\nThis may take some time.\n\nPlease try to reconnect in a minute.";
diff --git a/src/main.rs b/src/main.rs
index d1dd355..4e05fab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,6 +9,7 @@ use minecraft_protocol::data::server_status::*;
 use minecraft_protocol::decoder::Decoder;
 use minecraft_protocol::encoder::Encoder;
 use minecraft_protocol::version::v1_14_4::handshake::Handshake;
+use minecraft_protocol::version::v1_14_4::login::LoginDisconnect;
 use minecraft_protocol::version::v1_14_4::status::StatusResponse;
 use tokio::io;
 use tokio::io::AsyncReadExt;
@@ -34,12 +35,15 @@ async fn main() -> Result<(), ()> {
     // Proxy all incomming connections
     while let Ok((inbound, _)) = listener.accept().await {
         let client = Client::default();
-        eprintln!("New client");
+        eprintln!("Client connected");
 
         let transfer = proxy(client, inbound, ADDRESS_PROXY.to_string()).map(|r| {
             if let Err(e) = r {
                 println!("Failed to proxy: {:?}", e);
             }
+
+            // TODO: proxy isn't closed for disconnected clients!
+            eprintln!("Client disconnected");
         });
 
         tokio::spawn(transfer);
@@ -109,13 +113,15 @@ async fn proxy(client: Client, mut inbound: TcpStream, addr_target: String) -> R
 
     let (client_send_queue, mut client_to_send) = unbounded_channel::<Vec<u8>>();
 
+    let server_available = false;
+
     let client_to_server = async {
         // Incoming buffer
         let mut buf = BytesMut::new();
 
         loop {
             // In login state, proxy raw data
-            if client.state() == ClientState::Login {
+            if server_available && client.state() == ClientState::Login {
                 eprintln!("STARTED FULL PROXY");
 
                 wo.writable().await.map_err(|_| ())?;
@@ -149,6 +155,25 @@ async fn proxy(client: Client, mut inbound: TcpStream, addr_target: String) -> R
             eprintln!("PACKET ID: {}", packet.id);
             eprintln!("PACKET DATA: {:?}", packet.data);
 
+            // Hijack login start
+            if client.state() == ClientState::Login
+                && packet.id == protocol::LOGIN_PACKET_ID_LOGIN_START
+            {
+                let packet = LoginDisconnect {
+                    reason: Message::new(Payload::text(LABEL_SERVER_STARTING_MESSAGE)),
+                };
+
+                let mut data = Vec::new();
+                packet.encode(&mut data).map_err(|_| ())?;
+
+                let response = RawPacket::new(0, data).encode()?;
+                client_send_queue
+                    .send(response)
+                    .expect("failed to queue logout response");
+
+                break;
+            }
+
             // Hijack handshake
             if client.state() == ClientState::Handshake
                 && packet.id == protocol::STATUS_PACKET_ID_STATUS
diff --git a/src/protocol.rs b/src/protocol.rs
index 9a85f41..35d1770 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -4,6 +4,7 @@ use crate::types;
 
 pub const STATUS_PACKET_ID_STATUS: i32 = 0;
 pub const STATUS_PACKET_ID_PING: i32 = 1;
+pub const LOGIN_PACKET_ID_LOGIN_START: i32 = 0;
 
 /// Client state.
 // TODO: add encryption/compression state