From ceb718d4754725c3ccce0d48e337abca5ae984e2 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Thu, 15 Aug 2024 15:00:41 +0200 Subject: [PATCH] Setup Base for Velocity (Proxy) --- Cargo.lock | 23 +++++++ README.md | 1 + pumpkin-protocol/Cargo.toml | 2 +- pumpkin-protocol/src/bytebuf/mod.rs | 5 ++ .../src/client/login/c_plugin_request.rs | 22 ++++++ pumpkin-protocol/src/client/login/mod.rs | 2 + .../src/server/login/s_plugin_response.rs | 9 +-- pumpkin/Cargo.toml | 4 ++ pumpkin/src/client/client_packet.rs | 19 ++++- pumpkin/src/config/mod.rs | 6 +- pumpkin/src/config/proxy.rs | 22 ++++++ pumpkin/src/main.rs | 3 +- pumpkin/src/proxy/mod.rs | 1 + pumpkin/src/proxy/velocity.rs | 69 +++++++++++++++++++ 14 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 pumpkin-protocol/src/client/login/c_plugin_request.rs create mode 100644 pumpkin/src/config/proxy.rs create mode 100644 pumpkin/src/proxy/mod.rs create mode 100644 pumpkin/src/proxy/velocity.rs diff --git a/Cargo.lock b/Cargo.lock index 8fd3b3c60..2f856d5f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -312,6 +312,7 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common 0.1.6", + "subtle", ] [[package]] @@ -581,6 +582,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "http" version = "1.1.0" @@ -1186,6 +1196,7 @@ dependencies = [ "crossbeam-channel", "dhat", "digest 0.11.0-pre.9", + "hmac", "image", "log", "mio", @@ -1206,6 +1217,7 @@ dependencies = [ "serde", "serde_json", "sha1", + "sha2", "simple_logger", "thiserror", "tokio", @@ -1635,6 +1647,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + [[package]] name = "signature" version = "2.2.0" diff --git a/README.md b/README.md index bc5237907..7dc693518 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi - [ ] Player Inventory - [x] Player Combat - Server + - [ ] Query - [x] RCON - [x] Inventories - [x] Chat diff --git a/pumpkin-protocol/Cargo.toml b/pumpkin-protocol/Cargo.toml index bb48e06da..27c0c6566 100644 --- a/pumpkin-protocol/Cargo.toml +++ b/pumpkin-protocol/Cargo.toml @@ -16,7 +16,7 @@ serde = { version = "1.0", features = ["derive"] } # to parse strings to json responses serde_json = "1.0" -flate2 = "1.0.30" +flate2 = "1.0.31" thiserror = "1.0.63" log = "0.4" diff --git a/pumpkin-protocol/src/bytebuf/mod.rs b/pumpkin-protocol/src/bytebuf/mod.rs index caca3480a..34f8980a1 100644 --- a/pumpkin-protocol/src/bytebuf/mod.rs +++ b/pumpkin-protocol/src/bytebuf/mod.rs @@ -295,6 +295,11 @@ impl ByteBuffer { pub fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { self.buffer.copy_to_bytes(len) } + + pub fn copy_to_slice(&mut self, dst: &mut [u8]) { + self.buffer.copy_to_slice(dst) + } + pub fn put_slice(&mut self, src: &[u8]) { self.buffer.put_slice(src) } diff --git a/pumpkin-protocol/src/client/login/c_plugin_request.rs b/pumpkin-protocol/src/client/login/c_plugin_request.rs new file mode 100644 index 000000000..a8651e7af --- /dev/null +++ b/pumpkin-protocol/src/client/login/c_plugin_request.rs @@ -0,0 +1,22 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize)] +#[packet(0x04)] +pub struct CLoginPluginRequest<'a> { + message_id: VarInt, + channel: &'a str, + data: &'a [u8], +} + +impl<'a> CLoginPluginRequest<'a> { + pub fn new(message_id: VarInt, channel: &'a str, data: &'a [u8]) -> Self { + Self { + message_id, + channel, + data, + } + } +} diff --git a/pumpkin-protocol/src/client/login/mod.rs b/pumpkin-protocol/src/client/login/mod.rs index 02a841acc..6824b2012 100644 --- a/pumpkin-protocol/src/client/login/mod.rs +++ b/pumpkin-protocol/src/client/login/mod.rs @@ -1,9 +1,11 @@ mod c_encryption_request; mod c_login_disconnect; mod c_login_success; +mod c_plugin_request; mod c_set_compression; pub use c_encryption_request::*; pub use c_login_disconnect::*; pub use c_login_success::*; +pub use c_plugin_request::*; pub use c_set_compression::*; diff --git a/pumpkin-protocol/src/server/login/s_plugin_response.rs b/pumpkin-protocol/src/server/login/s_plugin_response.rs index 3b8ccf3d1..15fca41f1 100644 --- a/pumpkin-protocol/src/server/login/s_plugin_response.rs +++ b/pumpkin-protocol/src/server/login/s_plugin_response.rs @@ -1,3 +1,4 @@ +use bytes::BytesMut; use pumpkin_macros::packet; use crate::{ @@ -6,18 +7,18 @@ use crate::{ }; #[packet(0x02)] -pub struct SLoginPluginResponse<'a> { +pub struct SLoginPluginResponse { pub message_id: VarInt, pub successful: bool, - pub data: Option<&'a [u8]>, + pub data: Option, } -impl<'a> ServerPacket for SLoginPluginResponse<'a> { +impl ServerPacket for SLoginPluginResponse { fn read(bytebuf: &mut ByteBuffer) -> Result { Ok(Self { message_id: bytebuf.get_var_int(), successful: bytebuf.get_bool(), - data: None, // TODO + data: bytebuf.get_option(|v| v.get_slice()), }) } } diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index b8f29a314..3085385ea 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -43,6 +43,10 @@ reqwest = { version = "0.12.5", features = ["json"]} sha1 = "0.10.6" digest = "=0.11.0-pre.9" +# velocity en +hmac = "0.12.1" +sha2 = "0.10.8" + thiserror = "1.0.63" # icon loading diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 267c4ad75..ded91bdd3 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -22,6 +22,7 @@ use sha1::{Digest, Sha1}; use crate::{ client::authentication::{self, GameProfile}, entity::player::{ChatMode, Hand}, + proxy::velocity::velocity_login, server::{Server, CURRENT_MC_VERSION}, }; @@ -34,6 +35,7 @@ use super::{ /// Implements the `Client` Packets impl Client { pub fn handle_handshake(&mut self, _server: &mut Server, handshake: SHandShake) { + dbg!("handshake"); self.protocol_version = handshake.protocol_version.0; self.connection_state = handshake.next_state; if self.connection_state == ConnectionState::Login { @@ -56,9 +58,17 @@ impl Client { self.close(); } + fn is_valid_player_name(name: &str) -> bool { + name.len() <= 16 && name.chars().all(|c| c > 32 as char && c < 127 as char) + } + pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) { - // TODO: do basic name validation dbg!("login start"); + + if !Self::is_valid_player_name(&login_start.name) { + self.kick("Invalid characters in username"); + return; + } // default game profile, when no online mode // TODO: make offline uuid self.gameprofile = Some(GameProfile { @@ -67,6 +77,13 @@ impl Client { properties: vec![], profile_actions: None, }); + let proxy = &server.advanced_config.proxy; + if proxy.enabled { + if proxy.velocity.enabled { + velocity_login(self) + } + return; + } // TODO: check config for encryption let verify_token: [u8; 4] = rand::random(); diff --git a/pumpkin/src/config/mod.rs b/pumpkin/src/config/mod.rs index 5e76a8e14..9d519dd66 100644 --- a/pumpkin/src/config/mod.rs +++ b/pumpkin/src/config/mod.rs @@ -1,12 +1,14 @@ use std::path::Path; use auth_config::AuthenticationConfig; +use proxy::ProxyConfig; use resource_pack::ResourcePackConfig; use serde::{Deserialize, Serialize}; use crate::{entity::player::GameMode, server::Difficulty}; pub mod auth_config; +pub mod proxy; pub mod resource_pack; /// Current Config version of the Base Config @@ -17,10 +19,11 @@ const CURRENT_BASE_VERSION: &str = "1.0.0"; /// This also allows you get some Performance or Resource boosts. /// Important: The Configuration should match Vanilla by default pub struct AdvancedConfiguration { - pub commands: CommandsConfig, + pub proxy: ProxyConfig, pub authentication: AuthenticationConfig, pub packet_compression: CompressionConfig, pub resource_pack: ResourcePackConfig, + pub commands: CommandsConfig, pub rcon: RCONConfig, pub pvp: PVPConfig, } @@ -107,6 +110,7 @@ impl Default for CompressionConfig { impl Default for AdvancedConfiguration { fn default() -> Self { Self { + proxy: ProxyConfig::default(), authentication: AuthenticationConfig::default(), commands: CommandsConfig::default(), packet_compression: CompressionConfig::default(), diff --git a/pumpkin/src/config/proxy.rs b/pumpkin/src/config/proxy.rs new file mode 100644 index 000000000..dba52f66e --- /dev/null +++ b/pumpkin/src/config/proxy.rs @@ -0,0 +1,22 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Default)] +pub struct ProxyConfig { + pub enabled: bool, + pub velocity: VelocityConfig, +} + +#[derive(Deserialize, Serialize)] +pub struct VelocityConfig { + pub enabled: bool, + pub secret: String, +} + +impl Default for VelocityConfig { + fn default() -> Self { + Self { + enabled: false, + secret: "".into(), + } + } +} diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 79a19884f..308140a01 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -18,6 +18,7 @@ pub mod client; pub mod commands; pub mod config; pub mod entity; +pub mod proxy; pub mod rcon; pub mod server; pub mod util; @@ -81,7 +82,7 @@ fn main() -> io::Result<()> { let mut server = Server::new((basic_config, advanced_configuration)); log::info!("Started Server took {}ms", time.elapsed().as_millis()); - log::info!("You now can connect to the server"); + log::info!("You now can connect to the server, Listening on {}", addr); if use_console { thread::spawn(move || { diff --git a/pumpkin/src/proxy/mod.rs b/pumpkin/src/proxy/mod.rs new file mode 100644 index 000000000..70249d623 --- /dev/null +++ b/pumpkin/src/proxy/mod.rs @@ -0,0 +1 @@ +pub mod velocity; diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs new file mode 100644 index 000000000..3b06ff97a --- /dev/null +++ b/pumpkin/src/proxy/velocity.rs @@ -0,0 +1,69 @@ +use std::net::SocketAddr; + +use bytes::{BufMut, BytesMut}; +use hmac::{Hmac, Mac}; +use pumpkin_protocol::{ + bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse, +}; +use sha2::Sha256; + +use crate::{client::Client, config::proxy::VelocityConfig}; + +type HmacSha256 = Hmac; + +const MAX_SUPPORTED_FORWARDING_VERSION: i32 = 4; +const PLAYER_INFO_CHANNEL: &str = "velocity:player_info"; + +pub fn velocity_login(client: &mut Client) { + let velocity_message_id: i32 = 0; + + let mut buf = BytesMut::new(); + buf.put_u8(MAX_SUPPORTED_FORWARDING_VERSION as u8); + client.send_packet(&CLoginPluginRequest::new( + velocity_message_id.into(), + PLAYER_INFO_CHANNEL, + &buf, + )); +} + +pub fn check_integrity(data: (&[u8], &[u8]), secret: String) -> bool { + let (signature, data_without_signature) = data; + let mut mac = + HmacSha256::new_from_slice(secret.as_bytes()).expect("HMAC can take key of any size"); + mac.update(data_without_signature); + mac.verify_slice(signature).is_ok() +} + +pub fn receive_plugin_response( + client: &mut Client, + config: VelocityConfig, + response: SLoginPluginResponse, +) { + dbg!("velocity response"); + if let Some(data) = response.data { + let (signature, data_without_signature) = data.split_at(32); + + if !check_integrity((signature, data_without_signature), config.secret) { + client.kick("Unable to verify player details"); + return; + } + let mut buf = ByteBuffer::new(BytesMut::new()); + buf.put_slice(data_without_signature); + + // check velocity version + let version = buf.get_var_int(); + let version = version.0; + if version > MAX_SUPPORTED_FORWARDING_VERSION { + client.kick(&format!( + "Unsupported forwarding version {version}, Max: {MAX_SUPPORTED_FORWARDING_VERSION}" + )); + return; + } + // TODO: no unwrap + let addr: SocketAddr = buf.get_string().unwrap().parse().unwrap(); + client.address = addr; + todo!() + } else { + client.kick("This server requires you to connect with Velocity.") + } +}