From 5324cb8c297c7a99090053562d4350c2d66b4407 Mon Sep 17 00:00:00 2001 From: tomoron Date: Wed, 6 May 2026 02:49:03 +0200 Subject: [PATCH] server list working --- Cargo.lock | 124 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/client.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++ src/handshake.rs | 30 ++++++++++++ src/main.rs | 49 ++++++++++++++++++- src/varint.rs | 47 ++++++++++++++++++ 6 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 src/client.rs create mode 100644 src/handshake.rs create mode 100644 src/varint.rs diff --git a/Cargo.lock b/Cargo.lock index dcde5fe..3ce30e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,130 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "json" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + [[package]] name = "minecraft_proxy" version = "0.1.0" +dependencies = [ + "json", + "tokio", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.52.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml index c484dbb..5018221 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2024" [dependencies] +json = "0.12.4" +tokio = { version = "1.52.1", features = ["net", "rt", "macros"] } diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..fcfbd75 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,121 @@ +use json::object; +use tokio::net::TcpStream; +use std::collections::VecDeque; +use crate::handshake::Handshake; +use crate::varint::{varint_read, varint_write}; + +pub struct Client { + pub in_stream: TcpStream, + + buffer: Vec, + out_stream: Option, + handshake: Option +} + +impl Client { + pub fn create(stream: TcpStream) -> Self { + Self { + in_stream: stream, + + buffer: vec![], + out_stream: None, + handshake: None + } + } + + pub async fn buffer_append(&mut self, data: Vec) -> Result<(),String> { + if data.len() == 0 { + return Ok(()); + } + + + if let Some(_) = &self.out_stream { + self.stream_data(data); + return Ok(()); + } + + let _ = self.buffer.append(&mut (data.clone())); + + if self.buffer[0] == 0 { + return Err("invalid packet".to_string()); + } + + if self.buffer.len() - 1 >= self.buffer[0] as usize { + let len = varint_read(&mut self.buffer.clone().into())? as usize; + self.handle_packet(self.buffer[1..=len].to_vec().into())?; + self.buffer.drain(..len + 1); + } + Ok(()) + } + + fn send_packet(&self, data: Vec) { + let mut sent_data: Vec = varint_write(data.len() as i32); + + sent_data.extend(data); + match self.in_stream.try_write(sent_data.as_slice()) { + Ok(len) => { println!("sent {} bytes to the client", len); }, + Err(e) => { println!("error while sending response {:?}", e); } + } + } + + fn stream_data(&self, _data: Vec) { + panic!("proxy not implemented") + } + + fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { + println!("len : {}" , packet.len()); + let packet_id = varint_read(&mut packet)?; + if self.handshake.is_none() { + if packet_id != 0 { + return Err("packet 0 expected. invalid packet received".to_string()); + } + self.handshake = Some(Handshake::parse(&mut packet)?); + return Ok(()); + } + + let intent = self.handshake.as_ref().unwrap().intent; + + if intent == 1 { + self.status_intent_handle(&mut packet, packet_id)?; + } else if intent == 2 { + self.login_intent_handle(&mut packet, packet_id)?; + } + Ok(()) + } + + fn status_intent_handle(&self, mut packet: &mut VecDeque, packet_id: i32) -> Result<(),String> { + let status_response = object! { + "version": { + "name": "1.21.6", + "protocol" : self.handshake.as_ref().unwrap().protocol_version, + }, + "players": { + "max": 420, + "online": 69 + }, + "description": self.handshake.as_ref().unwrap().server_address.clone(), + }; + if packet_id == 0 { + let mut response : Vec = vec![]; + + let response_json = json::stringify(status_response); + let mut response_json_len = varint_write(response_json.len() as i32); + let mut response_packet_id = varint_write(0); + + response.append(&mut response_packet_id); + response.append(&mut response_json_len); + response.append(&mut response_json.as_bytes().into()); + + self.send_packet(response); + } else if packet_id == 1 { + let mut response = varint_write(1); + response.extend(packet.drain(..)); + self.send_packet(response); + } + Ok(()) + } + + fn login_intent_handle(&self, mut _packet: &mut VecDeque, _packet_id: i32) -> Result<(),String> { + panic!("not implemented") + } +} diff --git a/src/handshake.rs b/src/handshake.rs new file mode 100644 index 0000000..e3022df --- /dev/null +++ b/src/handshake.rs @@ -0,0 +1,30 @@ +use std::collections::VecDeque; +use crate::varint::varint_read; + +pub struct Handshake { + pub protocol_version: i32, + pub server_address: String, + pub server_port: u16, + pub intent: i32 +} + +impl Handshake { + pub fn parse(mut packet: &mut VecDeque) -> Result { + let protocol_version = varint_read(&mut packet)?; + + let str_len = varint_read(&mut packet)? as usize; + let server_address : String = String::from_utf8(packet.drain(..str_len).collect::>()).expect("Invalid UTF-8"); + + let bytes: [u8; 2] = [packet[0], packet[1]]; + let server_port : u16 = u16::from_be_bytes(bytes); + packet.drain(..2); + + let intent : i32 = varint_read(&mut packet)?; + Ok(Self { + protocol_version, + server_address, + server_port, + intent + }) + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..e397be2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,48 @@ -fn main() { - println!("Hello, world!"); +use tokio::net::{TcpListener, TcpStream}; +mod client; +use client::Client; +use std::io; + +pub mod varint; +pub mod handshake; + + +async fn process_socket(stream: TcpStream) -> io::Result<()> { + let mut buf = vec![0 as u8; 1024]; + + let mut client = Client::create(stream); + + loop { + client.in_stream.readable().await?; + + match client.in_stream.try_read(&mut buf) { + Ok(n) => { + let _ = client.buffer_append((&buf[..n]).to_vec()).await; + if n == 0 { + break; + } + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + continue; + } + Err(e) => { + return Err(e.into()); + } + } + } + Ok(()) +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> io::Result<()> { + let listener = TcpListener::bind("0.0.0.0:25565").await?; + + loop { + let (socket, _) = listener.accept().await?; + tokio::spawn(async move { + if let Err(e) = process_socket(socket).await { + eprintln!("error: {:?}", e); + } + }); + } } diff --git a/src/varint.rs b/src/varint.rs new file mode 100644 index 0000000..7e4d1d0 --- /dev/null +++ b/src/varint.rs @@ -0,0 +1,47 @@ +use std::collections::VecDeque; + +const SEGMENT_BITS : i32 = 0x7F; +const CONTINUE_BIT : i32 = 0x80; + +pub fn varint_read(buffer: &mut VecDeque) -> Result { + let mut value : i32 = 0; + let mut position: i32 = 0; + + loop { + if buffer.len() == 0 { + return Err("invalid VarInt".to_string()); + } + + let current = buffer.pop_front().unwrap() as i32; + value |= (current & SEGMENT_BITS) << position; + + if (current & CONTINUE_BIT) == 0 { + break; + } + + position += 7; + + if position >= 32 { + return Err("Varint too big".to_string()); + } + } + Ok(value) +} + + + +pub fn varint_write(mut value: i32) -> Vec { + let mut result : Vec = vec![]; + + loop { + if (value & !SEGMENT_BITS) == 0 { + result.push(value as u8); + break; + } + + result.push(((value & SEGMENT_BITS) | CONTINUE_BIT) as u8); + + value = ((value as u32) >> 7) as i32; + } + result +}