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",
"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"

View File

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

View File

@@ -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"

View File

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

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_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::*;

View File

@@ -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<BytesMut>,
}
impl<'a> ServerPacket for SLoginPluginResponse<'a> {
impl ServerPacket for SLoginPluginResponse {
fn read(bytebuf: &mut ByteBuffer) -> Result<Self, DeserializerError> {
Ok(Self {
message_id: bytebuf.get_var_int(),
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"
digest = "=0.11.0-pre.9"
# velocity en
hmac = "0.12.1"
sha2 = "0.10.8"
thiserror = "1.0.63"
# icon loading

View File

@@ -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();

View File

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

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 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 || {

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