diff --git a/Cargo.toml b/Cargo.toml index 04e8f51..d1ec4da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" [dependencies] tokio = { version = "1", features = ["full"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" diff --git a/src/handshake.rs b/src/handshake.rs new file mode 100644 index 0000000..0ea0330 --- /dev/null +++ b/src/handshake.rs @@ -0,0 +1,44 @@ +// Yeahbut December 2023 + +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +use crate::mc_types; + +pub struct Handshake { + pub protocol_version: i32, + pub server_address: String, + pub server_port: u16, + pub next_state: i32, +} + +pub async fn read_handshake(stream: &mut OwnedReadHalf) -> Option { + Some(Handshake { + protocol_version: mc_types::read_var_int(stream) + .await, + server_address: mc_types::read_string(stream) + .await, + server_port: stream.read_u16() + .await.expect("Error reading from stream"), + next_state: mc_types::read_var_int(stream) + .await, + }) +} + +pub async fn write_handshake( + stream: &mut OwnedWriteHalf, + handshake: Handshake, +) { + let mut data: Vec = vec![0]; + mc_types::write_var_int_bytes(&mut data, handshake.protocol_version); + mc_types::write_string_bytes(&mut data, &handshake.server_address); + data.append(&mut vec![ + ((handshake.server_port & 0xFF00) >> 8) as u8, + (handshake.server_port & 0xFF) as u8, + ]); + mc_types::write_var_int_bytes(&mut data, handshake.next_state); + + mc_types::write_var_int(stream, data.len() as i32).await; + stream.write_all(&mut data) + .await.expect("Error writing to stream"); +} diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 0000000..94ebbff --- /dev/null +++ b/src/login.rs @@ -0,0 +1,6 @@ +// Yeahbut December 2023 + +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + +use crate::mc_types; diff --git a/src/main.rs b/src/main.rs index 4205ea2..ad2404b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,14 @@ // Yeahbut December 2023 use tokio::net::{TcpListener, TcpStream}; -use tokio::io; +use tokio::io::{self, AsyncReadExt}; //, AsyncWriteExt}; use std::error::Error; +mod mc_types; +mod handshake; +mod status; +mod login; + #[tokio::main] async fn main() -> Result<(), Box> { let listener = TcpListener::bind("127.0.0.1:25565").await?; @@ -17,26 +22,54 @@ async fn main() -> Result<(), Box> { } async fn handle_client(client_socket: TcpStream) { + println!("Accepting Connection"); let backend_addr = "127.0.0.1:25566"; - if let Ok(mut backend_socket) = TcpStream::connect(backend_addr).await { - let (mut client_reader, mut client_writer) = client_socket.into_split(); - let (mut backend_reader, mut backend_writer) = backend_socket.into_split(); + let (mut client_reader, mut client_writer) = client_socket.into_split(); + if let Ok(backend_socket) = TcpStream::connect(backend_addr).await { + let (mut server_reader, mut server_writer) = backend_socket.into_split(); + let packet_id = client_reader.read_u8() + .await.expect("Error reading from stream"); + + println!("Packet ID: {}", packet_id); + if packet_id == 0 { + let handshake_packet = handshake::read_handshake(&mut client_reader) + .await.unwrap(); + println!("Next state: {}", handshake_packet.next_state); + if handshake_packet.next_state == 1 { + println!("Receiving Status Request"); + status::respond_status( + &mut client_reader, + &mut client_writer, + &mut server_reader, + &mut server_writer, + ).await; + return; + } else if handshake_packet.next_state == 2 { + + } else { + return; + } + } else if packet_id == 0xFE { + status::respond_legacy_status(&mut client_writer).await; + return; + } else { + return; + } // Forward from client to backend tokio::spawn(async move { - if let Err(e) = io::copy(&mut client_reader, &mut backend_writer).await { - eprintln!("Error copying from client to backend: {}", e); - } + io::copy(&mut client_reader, &mut server_writer) + .await.expect("Error copying from client to backend"); }); // Forward from backend to client tokio::spawn(async move { - if let Err(e) = io::copy(&mut backend_reader, &mut client_writer).await { - eprintln!("Error copying from backend to client: {}", e); - } + io::copy(&mut server_reader, &mut client_writer) + .await.expect("Error copying from backend to client"); }); } else { eprintln!("Failed to connect to the backend server"); } + println!("Connection Closed"); } diff --git a/src/mc_types.rs b/src/mc_types.rs new file mode 100644 index 0000000..15cbcc3 --- /dev/null +++ b/src/mc_types.rs @@ -0,0 +1,130 @@ +// Yeahbut December 2023 + +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use serde::Serialize; + +pub const VERSION_NAME: &str = "1.19.4"; +pub const VERSION_PROTOCOL: i32 = 762; + +const SEGMENT_BITS: u8 = 0x7F; +const CONTINUE_BIT: u8 = 0x80; + +#[derive(Serialize)] +pub struct Chat { + pub text: String, +} + +pub async fn read_var_int(stream: &mut OwnedReadHalf) -> i32 { + let mut value: i32 = 0; + let mut position: u32 = 0; + + loop { + let current_byte = stream.read_u8() + .await + .expect("Error reading from stream"); + value |= ((current_byte & SEGMENT_BITS) as i32) << position; + + if (current_byte & CONTINUE_BIT) == 0 { + break; + } + + position += 7; + + if position >= 32 { + eprintln!("VarInt is too big"); + } + } + + value +} + +pub async fn write_var_int(stream: &mut OwnedWriteHalf, mut value: i32) { + loop { + if (value & !(SEGMENT_BITS as i32)) == 0 { + stream.write_u8(value as u8) + .await.expect("Error writing to stream"); + return; + } + + stream.write_u8((value & (SEGMENT_BITS as i32)) as u8 | CONTINUE_BIT) + .await + .expect("Error writing to stream"); + + value >>= 7; + } +} + +pub fn write_var_int_bytes(stream: &mut Vec, mut value: i32) { + loop { + if (value & !(SEGMENT_BITS as i32)) == 0 { + stream.append(&mut vec![value as u8]); + return; + } + stream.append( + &mut vec![(value & (SEGMENT_BITS as i32)) as u8 | CONTINUE_BIT]); + value >>= 7; + } +} + +pub async fn read_var_long(stream: &mut OwnedReadHalf) -> i64 { + let mut value: i64 = 0; + let mut position: u32 = 0; + + loop { + let current_byte = stream.read_u8() + .await + .expect("Error reading from stream"); + value |= ((current_byte & SEGMENT_BITS) as i64) << position; + + if (current_byte & CONTINUE_BIT) == 0 { + break; + } + + position += 7; + + if position >= 64 { + eprintln!("VarLong is too big"); + } + } + + value +} + +pub async fn write_var_long(stream: &mut OwnedWriteHalf, mut value: i64) { + loop { + if (value & !(SEGMENT_BITS as i64)) == 0 { + stream.write_u8(value as u8) + .await + .expect("Error writing to stream"); + return; + } + + stream.write_u8((value & SEGMENT_BITS as i64) as u8 | CONTINUE_BIT) + .await + .expect("Error writing to stream"); + + value >>= 7; + } +} + +pub async fn read_string(stream: &mut OwnedReadHalf) -> String { + let length = read_var_int(stream).await; + let mut buffer = vec![0; length as usize]; + stream.read_exact(&mut buffer) + .await.expect("Error reading string from stream"); + String::from_utf8_lossy(&buffer).to_string() +} + +pub async fn write_string(stream: &mut OwnedWriteHalf, s: &str) { + let length = s.len() as i32; + write_var_int(stream, length).await; + stream.write_all(s.as_bytes()) + .await.expect("Error writing string to stream"); +} + +pub fn write_string_bytes(stream: &mut Vec, s: &str) { + let length = s.len() as i32; + write_var_int_bytes(stream, length); + stream.append(&mut s.as_bytes().to_vec()); +} diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000..50b4716 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,181 @@ +// Yeahbut December 2023 + +use std::str::Bytes; + +use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use serde::Serialize; + +use crate::mc_types; +use crate::handshake; + +#[derive(Serialize)] +pub struct StatusVersion { + pub name: String, + pub protocol: i32, +} + +#[derive(Serialize)] +pub struct StatusPlayerInfo { + pub name: String, + pub id: String, +} + +#[derive(Serialize)] +pub struct StatusPlayers { + pub max: i32, + pub online: i32, + pub sample: Vec +} + +#[derive(Serialize)] +pub struct StatusResponse { + pub version: StatusVersion, + pub description: mc_types::Chat, + pub players: StatusPlayers, + #[serde(skip_serializing_if = "Option::is_none")] + pub favicon: Option, + pub enforcesSecureChat: bool, + pub previewsChat: bool, +} + +async fn online_players( + server_reader: &mut OwnedReadHalf, + server_writer: &mut OwnedWriteHalf, +) -> StatusPlayers { + get_upstream_status(server_reader, server_writer).await.players +} + +fn motd() -> String { + "A Minecraft Server Proxy".to_string() +} + +fn favicon() -> Option { + None +} + +pub async fn respond_status( + client_reader: &mut OwnedReadHalf, + client_writer: &mut OwnedWriteHalf, + server_reader: &mut OwnedReadHalf, + server_writer: &mut OwnedWriteHalf, +) { + loop { + let packet_id = client_reader.read_u8() + .await.expect("Error reading from stream"); + + let online_players = online_players(server_reader, server_writer).await; + println!("Status Packet ID: {}", packet_id); + + if packet_id == 0x00 { + println!("Handling Status"); + let status_response = StatusResponse { + version: StatusVersion { + name: mc_types::VERSION_NAME.to_string(), + protocol: mc_types::VERSION_PROTOCOL, + }, + description: mc_types::Chat { + text: motd(), + }, + players: StatusPlayers { + max: -13, + online: online_players.online, + sample: online_players.sample, + }, + favicon: favicon(), + enforcesSecureChat: false, + previewsChat: false, + }; + + let json_result = serde_json::to_string(&status_response); + + match json_result { + Ok(json) => { + client_writer.write_u8(0) + .await.expect("Error writing to stream"); + mc_types::write_string(client_writer, &json).await; + }, + Err(err) => { + eprintln!("Error serializing to JSON: {}", err); + break; + }, + } + } else if packet_id == 0x01 { + println!("Handling Ping"); + let payload = client_reader.read_i64() + .await.expect("Error reading from stream"); + client_writer.write_u8(0) + .await.expect("Error writing to stream"); + client_writer.write_i64(payload) + .await.expect("Error writing to stream"); + break; + } else { + break; + } + } +} + +pub async fn get_upstream_status( + server_reader: &mut OwnedReadHalf, + server_writer: &mut OwnedWriteHalf, +) -> StatusResponse { + StatusResponse { + version: StatusVersion { + name: "1.19.4".to_string(), + protocol: 762, + }, + description: mc_types::Chat { + text: motd(), + }, + players: StatusPlayers { + max: 0, + online: 0, + sample: vec![], + }, + favicon: favicon(), + enforcesSecureChat: false, + previewsChat: false, + } +} + +pub async fn respond_legacy_status(client_writer: &mut OwnedWriteHalf) { + println!("Old Style Status"); + client_writer.write_u8(0xFF) + .await.expect("Error writing to stream"); + + // let s = "§1\0127\01.12.2\0YTD Proxy§0§10"; + // println!("String length: {}", s.len()); + // client_writer.write_u16(s.len() as u16) + // .await.expect("Error writing to stream"); + // let utf16_bytes: Vec = s.encode_utf16().collect(); + // for utf16_char in utf16_bytes { + // client_writer.write_u16(utf16_char) + // .await.expect("Error writing to stream"); + // } + + let s = "§1\0127\0".to_string() + + mc_types::VERSION_NAME + + "\0YTD Proxy§0§10"; + let utf16_vec: Vec = s + .encode_utf16() + .flat_map(|c| std::iter::once(c).chain(std::iter::once(0))) + .collect(); + println!("String length: {}", (utf16_vec.len() / 2)); + client_writer.write_u16((utf16_vec.len() / 2) as u16) + .await.expect("Error writing to stream"); + for utf16_char in utf16_vec { + client_writer.write_u16(utf16_char) + .await.expect("Error writing to stream"); + } + + // let s = b"\x00\xa7\x001\x00\x00\x001\x002\x007\x00\x00\x001\x00.\x001\x0 + // 02\x00.\x002\x00\x00\x00Y\x00T\x00D\x00 \x00P\x00r\x00o\x00x\x00y\x00 + // \xa7\x000\xa7\x001\x000"; + // println!("String length: {}", s.len()); + // client_writer.write_u16(25) + // .await.expect("Error writing to stream"); + // for b in s { + // client_writer.write_u8(b) + // .await.expect("Error writing to stream"); + // } +}