From df1908bfaa691801caca45ab990fa45feb067b70 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Thu, 14 Nov 2024 18:04:53 +0100 Subject: [PATCH] try to fix some status response stuff --- Cargo.lock | 46 +++++++++++++++ Cargo.toml | 2 + src/main.rs | 9 ++- src/protocols.rs | 11 ++-- src/protocols/custom_report_details.rs | 36 +++++------- src/protocols/ping.rs | 29 ++++++++++ src/protocols/status.rs | 77 +++++++++++++++++--------- src/types/string.rs | 28 ++++++++-- src/utils.rs | 18 ++++++ 9 files changed, 193 insertions(+), 63 deletions(-) create mode 100644 src/protocols/ping.rs diff --git a/Cargo.lock b/Cargo.lock index 057666d..5e8a59e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "libc" version = "0.2.162" @@ -145,6 +151,8 @@ version = "0.1.0" dependencies = [ "num-derive", "num-traits", + "serde", + "serde_json", "tokio", "tokio-stream", "tokio-util", @@ -274,12 +282,50 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.132" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 923bdb6..23daa29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,5 @@ tokio = { version = "1.41.1", features = ["rt", "rt-multi-thread", "macros", "fu tokio-util = { version = "0.7.0", features = ["full"] } tokio-stream = "0.1" +serde = "1.0" +serde_json = "1.0" diff --git a/src/main.rs b/src/main.rs index bc5f7e2..f8e8d9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +#![allow(unused)] pub mod protocols; pub mod types; pub mod utils; @@ -5,10 +6,10 @@ pub mod utils; use crate::types::string::McString; use crate::types::var_int::VarInt; use crate::types::{McRead, McRustRepr}; -use crate::utils::RWStreamWithLimit; +use crate::utils::{MyAsyncReadExt, RWStreamWithLimit}; use num_derive::FromPrimitive; use num_traits::{FromPrimitive, ToPrimitive}; -use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::net::TcpStream; #[tokio::main] @@ -38,6 +39,7 @@ enum ConnectionState { Status = 1, Login = 2, Transfer = 3, + ///Internal use Closed = -1, } struct Connection { @@ -87,6 +89,7 @@ impl Connection { } Err(e) => { self.connection_state = ConnectionState::Closed; + dbg!(&self.tcp_stream.shutdown().await); println!("Got an error during package handling: {e}"); } } @@ -106,7 +109,7 @@ impl Connection { .await .map_err(|_| "Could not read string".to_string())?; println!("address: '{}'", address.as_rs()); - stream.read_exact(&mut [0, 2]).await.unwrap(); //server port. Unused + stream.discard(2).await.unwrap(); //server port. Unused let next_state_id = VarInt::read_stream(stream).await?; println!("next state: {}", next_state_id.as_rs()); let next_state = FromPrimitive::from_i32(next_state_id.to_rs()); diff --git a/src/protocols.rs b/src/protocols.rs index 7df1155..d0f2534 100644 --- a/src/protocols.rs +++ b/src/protocols.rs @@ -3,22 +3,23 @@ use num_derive::FromPrimitive; use tokio::io::{AsyncRead, AsyncWrite}; #[derive(FromPrimitive)] -pub enum Protocols { +pub enum Protocol { Status = 0x00, Ping = 0x01, CustomReportDetails = 0x7a, } pub async fn handle( - protocol: Protocols, + protocol: Protocol, stream: &mut RWStreamWithLimit<'_, T>, // bytes_left_in_package: &mut i32, ) -> Result<(), bool> { match protocol { - Protocols::Status => status::Protocol::handle(stream).await?, - Protocols::Ping => {} - Protocols::CustomReportDetails => custom_report_details::Protocol::handle(stream).await?, + Protocol::Status => status::Protocol::handle(stream).await?, + Protocol::Ping => ping::Protocol::handle(stream).await?, + Protocol::CustomReportDetails => custom_report_details::Protocol::handle(stream).await?, }; Ok(()) } mod custom_report_details; +mod ping; mod status; diff --git a/src/protocols/custom_report_details.rs b/src/protocols/custom_report_details.rs index ea7bd28..7000f58 100644 --- a/src/protocols/custom_report_details.rs +++ b/src/protocols/custom_report_details.rs @@ -1,3 +1,4 @@ +use crate::types::string::McString; use crate::types::var_int::VarInt; use crate::types::McRead; use crate::utils::RWStreamWithLimit; @@ -10,33 +11,22 @@ impl Protocol { stream: &mut RWStreamWithLimit<'_, T>, // bytes_left_in_package: &mut i32, ) -> Result<(), bool> { + println!("Some custom report detail stuff..."); let count = VarInt::read_stream(stream).await.map_err(|x| { dbg!(x); true })?; dbg!(&count); - let string_size = VarInt::read_stream(stream).await.map_err(|x| { - dbg!(x); - true - })?; - dbg!(&string_size); - stream.discard_unread().await.map_err(|x| { - dbg!(x); - true - })?; - // for i in 0..*count { - // let title = McString::read_stream(stream).await.map_err(|x| { - // dbg!(x); - // })?; - // let description = McString::read_stream(stream).await.map_err(|x| { - // dbg!(x); - // })?; - // println!( - // "Read title & description fo some custom report ({i}): {}\n{}", - // title.as_rs(), - // description.as_rs() - // ); - // } - Ok(()) + for i in 0..*count { + McString::<128>::read_stream(stream).await.map_err(|x| { + dbg!(x); + true + })?; + McString::<4096>::read_stream(stream).await.map_err(|x| { + dbg!(x); + true + })?; + } + Err(true) } } diff --git a/src/protocols/ping.rs b/src/protocols/ping.rs new file mode 100644 index 0000000..11afc10 --- /dev/null +++ b/src/protocols/ping.rs @@ -0,0 +1,29 @@ +use crate::types::var_int::VarInt; +use crate::types::var_long::VarLong; +use crate::types::{McRead, McWrite}; +use crate::utils::RWStreamWithLimit; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub struct Protocol {} + +impl Protocol { + pub async fn handle( + stream: &mut RWStreamWithLimit<'_, T>, + ) -> Result<(), bool> { + println!("Ping"); + let v = stream.read_i64().await.map_err(|e| { + dbg!(e); + false + })?; + VarInt(0x01).write_stream(stream).await.map_err(|x| { + dbg!(x); + false + })?; + stream.write_i64(v).await.map_err(|e| { + dbg!(e); + false + })?; + + Ok(()) + } +} diff --git a/src/protocols/status.rs b/src/protocols/status.rs index 6404cf6..099accd 100644 --- a/src/protocols/status.rs +++ b/src/protocols/status.rs @@ -1,58 +1,83 @@ use crate::types::string::McString; use crate::types::var_int::VarInt; +use crate::types::var_long::VarLong; use crate::types::McWrite; use crate::utils::RWStreamWithLimit; -use tokio::io::{AsyncRead, AsyncWrite}; +use serde_json::json; +use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufWriter}; pub struct Protocol {} impl Protocol { pub async fn handle( stream: &mut RWStreamWithLimit<'_, T>, - // bytes_left_in_package: &mut i32, ) -> Result<(), bool> { println!("Status"); - VarInt(0x01).write_stream(stream).await.map_err(|x| { + stream.discard_unread().await.map_err(|x| { + dbg!(x); + false + })?; + let string = Self::get_sample_result(); + let mut total_size = 0; + let mut v = Vec::new(); + let mut writer = BufWriter::new(&mut v); + + //Package ID + total_size += VarInt(0x00).write_stream(&mut writer).await.map_err(|x| { dbg!(x); false })?; - McString::<32767>::from_string(Self::get_sample_result()) + //Status JSON + total_size += McString::<32767>::from_string(string) + .write_stream(&mut writer) + .await + .map_err(|x| { + dbg!(x); + false + })?; + writer.flush().await.unwrap(); + + println!("total size: {}: {:?}", total_size, &v); + //Size in front + VarInt(total_size as i32) .write_stream(stream) .await .map_err(|x| { dbg!(x); false })?; - // stream.discard_unread().await.map_err(|x| { - // dbg!(x); - // false - // })?; - // *bytes_left_in_package = 0; + //actually write the content to the stream, not just a local buffer + stream.write_all(&v).await.map_err(|x| { + dbg!(x); + false + })?; + Ok(()) } + fn get_sample_result() -> String { - "{ - \"version\": { - \"name\": \"1.21.2\", - \"protocol\": 768 + json!({ + "version": { + "name": "1.21.2", + "protocol": 768 }, - \"players\": { - \"max\": 100, - \"online\": 5, - \"sample\": [ + "players": { + "max": 100, + "online": 5, + "sample": [ { - \"name\": \"thinkofdeath\", - \"id\": \"4566e69f-c907-48ee-8d71-d7ba5aa00d20\" - } - ] + "name": "thinkofdeath", + "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20", + }, + ], }, - \"description\": { - \"text\": \"Hello, world!\" + "description": { + "text": "Hello, world!" }, - \"favicon\": \"data:image/png;base64,\", - \"enforcesSecureChat\": false - }" + // "favicon": "data:image/png;base64,", + "enforcesSecureChat": false, + }) .to_string() } } diff --git a/src/types/string.rs b/src/types/string.rs index f261259..cc45d1e 100644 --- a/src/types/string.rs +++ b/src/types/string.rs @@ -7,8 +7,17 @@ pub struct McString { pub value: String, } impl McString { - fn measure_size(s: &str) -> usize { - s.len() + pub fn measure_size(s: &str) -> usize { + // 3. UTF-8 encoded byte length + let utf8_len = s.bytes().len(); + + // 5. Calculate total length (including VarInt prefix) + let varint_size = VarInt(utf8_len as i32).get_size(); + if varint_size > 3 { + //TODO: This is not allowed + } + // println!("strlen: {}+({}?{})", varint_size, utf8_len, utf16_len); + varint_size + utf8_len } pub fn from_string(s: String) -> Self { Self { value: s } @@ -25,6 +34,7 @@ impl McRead for McString { dbg!(x); })?; let size = *max_size as usize; + println!("Reading string of length: {}", size); // Check if the size exceeds the maximum allowed length (n) if size > (MAX_SIZE * 3) + 3 { @@ -35,10 +45,14 @@ impl McRead for McString { let actual_size = b.read(&mut bytes).await.map_err(|x| { dbg!(x); })?; - assert_eq!(size, actual_size); let value = String::from_utf8(bytes).map_err(|x| { dbg!(x); })?; + assert_eq!( + size, actual_size, + "Did not read all that was to read {}", + value + ); Ok(Self { value }) } } @@ -54,10 +68,12 @@ impl McWrite for McString { { let buf = self.value.as_bytes(); let length = Self::measure_size(&self.value); - VarInt(length as i32).write_stream(stream).await?; - + println!("string length: {}", length); + let length_var_int = VarInt(length as i32); + let written = length_var_int.write_stream(stream).await?; + println!("Writing String to stream: '{}'", self.value); stream.write_all(buf).await?; - Ok(length) + Ok(buf.len() + written) } } impl McRustRepr for McString { diff --git a/src/utils.rs b/src/utils.rs index b1dc1f0..243dfb2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,3 +1,4 @@ +use std::cmp::min; use std::io::ErrorKind; use std::pin::Pin; use std::task::{Context, Poll}; @@ -8,6 +9,23 @@ pub struct RWStreamWithLimit<'a, T: AsyncRead + AsyncWrite> { read_bytes_left: usize, } +impl<'a, T: AsyncRead + AsyncWrite + Unpin> RWStreamWithLimit<'a, T> { + pub(crate) async fn discard(&mut self, bytes: usize) -> std::io::Result { + let bytes = min(bytes, self.read_bytes_left); + let read = self.stream.read_exact(&mut vec![0; bytes]).await?; + self.read_bytes_left -= read; + Ok(read) + } +} +pub(crate) trait MyAsyncReadExt { + async fn discard(&mut self, bytes: usize) -> std::io::Result; +} +impl MyAsyncReadExt for T { + async fn discard(&mut self, bytes: usize) -> std::io::Result { + self.read_exact(&mut vec![0; bytes]).await + } +} + impl<'a, T: AsyncRead + AsyncWrite + Unpin> RWStreamWithLimit<'a, T> { pub(crate) fn new(stream: &'a mut T, read_limit: usize) -> Self { Self {