Setup Base for Velocity (Proxy)

This commit is contained in:
Snowiiii 2024-08-15 15:00:41 +02:00
parent 333bf64235
commit ceb718d475
14 changed files with 180 additions and 8 deletions

23
Cargo.lock generated
View File

@ -312,6 +312,7 @@ dependencies = [
"block-buffer 0.10.4", "block-buffer 0.10.4",
"const-oid", "const-oid",
"crypto-common 0.1.6", "crypto-common 0.1.6",
"subtle",
] ]
[[package]] [[package]]
@ -581,6 +582,15 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 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]] [[package]]
name = "http" name = "http"
version = "1.1.0" version = "1.1.0"
@ -1186,6 +1196,7 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"dhat", "dhat",
"digest 0.11.0-pre.9", "digest 0.11.0-pre.9",
"hmac",
"image", "image",
"log", "log",
"mio", "mio",
@ -1206,6 +1217,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"sha1", "sha1",
"sha2",
"simple_logger", "simple_logger",
"thiserror", "thiserror",
"tokio", "tokio",
@ -1635,6 +1647,17 @@ dependencies = [
"digest 0.10.7", "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]] [[package]]
name = "signature" name = "signature"
version = "2.2.0" version = "2.2.0"

View File

@ -56,6 +56,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [ ] Player Inventory - [ ] Player Inventory
- [x] Player Combat - [x] Player Combat
- Server - Server
- [ ] Query
- [x] RCON - [x] RCON
- [x] Inventories - [x] Inventories
- [x] Chat - [x] Chat

View File

@ -16,7 +16,7 @@ serde = { version = "1.0", features = ["derive"] }
# to parse strings to json responses # to parse strings to json responses
serde_json = "1.0" serde_json = "1.0"
flate2 = "1.0.30" flate2 = "1.0.31"
thiserror = "1.0.63" thiserror = "1.0.63"
log = "0.4" log = "0.4"

View File

@ -295,6 +295,11 @@ impl ByteBuffer {
pub fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes { pub fn copy_to_bytes(&mut self, len: usize) -> bytes::Bytes {
self.buffer.copy_to_bytes(len) 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]) { pub fn put_slice(&mut self, src: &[u8]) {
self.buffer.put_slice(src) self.buffer.put_slice(src)
} }

View File

@ -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,
}
}
}

View File

@ -1,9 +1,11 @@
mod c_encryption_request; mod c_encryption_request;
mod c_login_disconnect; mod c_login_disconnect;
mod c_login_success; mod c_login_success;
mod c_plugin_request;
mod c_set_compression; mod c_set_compression;
pub use c_encryption_request::*; pub use c_encryption_request::*;
pub use c_login_disconnect::*; pub use c_login_disconnect::*;
pub use c_login_success::*; pub use c_login_success::*;
pub use c_plugin_request::*;
pub use c_set_compression::*; pub use c_set_compression::*;

View File

@ -1,3 +1,4 @@
use bytes::BytesMut;
use pumpkin_macros::packet; use pumpkin_macros::packet;
use crate::{ use crate::{
@ -6,18 +7,18 @@ use crate::{
}; };
#[packet(0x02)] #[packet(0x02)]
pub struct SLoginPluginResponse<'a> { pub struct SLoginPluginResponse {
pub message_id: VarInt, pub message_id: VarInt,
pub successful: bool, pub successful: bool,
pub data: Option<&'a [u8]>, pub data: Option<BytesMut>,
} }
impl<'a> ServerPacket for SLoginPluginResponse<'a> { impl ServerPacket for SLoginPluginResponse {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> { fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self { Ok(Self {
message_id: bytebuf.get_var_int(), message_id: bytebuf.get_var_int(),
successful: bytebuf.get_bool(), successful: bytebuf.get_bool(),
data: None, // TODO data: bytebuf.get_option(|v| v.get_slice()),
}) })
} }
} }

View File

@ -43,6 +43,10 @@ reqwest = { version = "0.12.5", features = ["json"]}
sha1 = "0.10.6" sha1 = "0.10.6"
digest = "=0.11.0-pre.9" digest = "=0.11.0-pre.9"
# velocity en
hmac = "0.12.1"
sha2 = "0.10.8"
thiserror = "1.0.63" thiserror = "1.0.63"
# icon loading # icon loading

View File

@ -22,6 +22,7 @@ use sha1::{Digest, Sha1};
use crate::{ use crate::{
client::authentication::{self, GameProfile}, client::authentication::{self, GameProfile},
entity::player::{ChatMode, Hand}, entity::player::{ChatMode, Hand},
proxy::velocity::velocity_login,
server::{Server, CURRENT_MC_VERSION}, server::{Server, CURRENT_MC_VERSION},
}; };
@ -34,6 +35,7 @@ use super::{
/// Implements the `Client` Packets /// Implements the `Client` Packets
impl Client { impl Client {
pub fn handle_handshake(&mut self, _server: &mut Server, handshake: SHandShake) { pub fn handle_handshake(&mut self, _server: &mut Server, handshake: SHandShake) {
dbg!("handshake");
self.protocol_version = handshake.protocol_version.0; self.protocol_version = handshake.protocol_version.0;
self.connection_state = handshake.next_state; self.connection_state = handshake.next_state;
if self.connection_state == ConnectionState::Login { if self.connection_state == ConnectionState::Login {
@ -56,9 +58,17 @@ impl Client {
self.close(); 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) { pub fn handle_login_start(&mut self, server: &mut Server, login_start: SLoginStart) {
// TODO: do basic name validation
dbg!("login start"); 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 // default game profile, when no online mode
// TODO: make offline uuid // TODO: make offline uuid
self.gameprofile = Some(GameProfile { self.gameprofile = Some(GameProfile {
@ -67,6 +77,13 @@ impl Client {
properties: vec![], properties: vec![],
profile_actions: None, 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 // TODO: check config for encryption
let verify_token: [u8; 4] = rand::random(); let verify_token: [u8; 4] = rand::random();

View File

@ -1,12 +1,14 @@
use std::path::Path; use std::path::Path;
use auth_config::AuthenticationConfig; use auth_config::AuthenticationConfig;
use proxy::ProxyConfig;
use resource_pack::ResourcePackConfig; use resource_pack::ResourcePackConfig;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{entity::player::GameMode, server::Difficulty}; use crate::{entity::player::GameMode, server::Difficulty};
pub mod auth_config; pub mod auth_config;
pub mod proxy;
pub mod resource_pack; pub mod resource_pack;
/// Current Config version of the Base Config /// 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. /// This also allows you get some Performance or Resource boosts.
/// Important: The Configuration should match Vanilla by default /// Important: The Configuration should match Vanilla by default
pub struct AdvancedConfiguration { pub struct AdvancedConfiguration {
pub commands: CommandsConfig, pub proxy: ProxyConfig,
pub authentication: AuthenticationConfig, pub authentication: AuthenticationConfig,
pub packet_compression: CompressionConfig, pub packet_compression: CompressionConfig,
pub resource_pack: ResourcePackConfig, pub resource_pack: ResourcePackConfig,
pub commands: CommandsConfig,
pub rcon: RCONConfig, pub rcon: RCONConfig,
pub pvp: PVPConfig, pub pvp: PVPConfig,
} }
@ -107,6 +110,7 @@ impl Default for CompressionConfig {
impl Default for AdvancedConfiguration { impl Default for AdvancedConfiguration {
fn default() -> Self { fn default() -> Self {
Self { Self {
proxy: ProxyConfig::default(),
authentication: AuthenticationConfig::default(), authentication: AuthenticationConfig::default(),
commands: CommandsConfig::default(), commands: CommandsConfig::default(),
packet_compression: CompressionConfig::default(), packet_compression: CompressionConfig::default(),

View File

@ -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(),
}
}
}

View File

@ -18,6 +18,7 @@ pub mod client;
pub mod commands; pub mod commands;
pub mod config; pub mod config;
pub mod entity; pub mod entity;
pub mod proxy;
pub mod rcon; pub mod rcon;
pub mod server; pub mod server;
pub mod util; pub mod util;
@ -81,7 +82,7 @@ fn main() -> io::Result<()> {
let mut server = Server::new((basic_config, advanced_configuration)); let mut server = Server::new((basic_config, advanced_configuration));
log::info!("Started Server took {}ms", time.elapsed().as_millis()); 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 { if use_console {
thread::spawn(move || { thread::spawn(move || {

1
pumpkin/src/proxy/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod velocity;

View File

@ -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<Sha256>;
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.")
}
}