diff --git a/Cargo.lock b/Cargo.lock index 8781fda..7825a02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -78,6 +87,17 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "hashbrown" version = "0.15.1" @@ -141,9 +161,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "meexprox" version = "0.1.0" dependencies = [ + "bytebuffer", "ignore-result", "log", "random-string", + "ring", "rust_mc_proto", "serde_yml", "simplelog", @@ -213,6 +235,21 @@ dependencies = [ "fastrand", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rust_mc_proto" version = "0.1.16" @@ -264,6 +301,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simplelog" version = "0.12.2" @@ -275,6 +318,12 @@ dependencies = [ "time", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "syn" version = "2.0.87" @@ -334,6 +383,12 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "uuid" version = "1.11.0" @@ -346,13 +401,28 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 25b0f03..ea9d50d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,6 @@ uuid = "1.11.0" log = "0.4.22" simplelog = "0.12.2" ignore-result = "0.2.0" -random-string = "1.1.0" \ No newline at end of file +random-string = "1.1.0" +ring = "0.17.8" +bytebuffer = "2.3.0" \ No newline at end of file diff --git a/config.yml b/config.yml index 040c293..d429a56 100644 --- a/config.yml +++ b/config.yml @@ -30,5 +30,5 @@ incoming_forwarding: # player forwarding for incoming connections # player forwarding types: # - velocity (or "modern" in Velocity config) (secret is required) # - bungeecord (or "legacy" in Velocity config) (secret is optional) -# - meexprox (best) (secret is required) +# - meexprox (open-source protocol) (secret is required) # - none (enabled: false) \ No newline at end of file diff --git a/src/meexprox/connection.rs b/src/meexprox/connection.rs index 39618f9..5800913 100644 --- a/src/meexprox/connection.rs +++ b/src/meexprox/connection.rs @@ -1,11 +1,13 @@ use std::{net::{SocketAddr, TcpStream}, sync::{Arc, Mutex}, thread}; +use bytebuffer::ByteBuffer; use ignore_result::Ignore; -use log::debug; +use log::info; +use ring::hmac; use rust_mc_proto::{DataBufferReader, DataBufferWriter, MCConnTcp, Packet, ProtocolError}; use uuid::Uuid; -use super::{config::{ProxyConfig, ServerInfo}, error::{AsProxyResult, ProxyError}}; +use super::{config::{PlayerForwarding, ProxyConfig, ServerInfo}, error::{AsProxyResult, ProxyError}}; #[derive(Clone, Debug)] pub struct LoginInfo { @@ -74,6 +76,7 @@ pub struct Player { impl Player { pub fn read( + _config: &ProxyConfig, protocol_version: u16, server_address: String, server_port: u16, @@ -98,7 +101,7 @@ impl Player { login_info: None, name: name.clone(), uuid, - server: Some(server), + server: Some(server.clone()), protocol_version }; @@ -128,6 +131,47 @@ impl Player { player.set_server_compression(compression); player.set_client_compression(compression); } + 0x04 => { // login plugin request + let message_id = packet.read_isize_varint().as_proxy()?; + let channel = packet.read_string().as_proxy()?; + + if channel == "velocity:player_info" { + if let PlayerForwarding::Velocity(secret) = &server.player_forwarding { + let version: u8 = if packet.buffer().len() - packet.buffer().get_rpos() == 1 { + packet.read_byte().as_proxy()? + } else { + 1 + }; + + let response = Packet::build(0x02, |p| { + p.write_isize_varint(message_id)?; + p.write_boolean(true)?; + + let mut buf = ByteBuffer::new(); + DataBufferWriter::write_u8_varint(&mut buf, version)?; + DataBufferWriter::write_string(&mut buf, &addr.to_string())?; + DataBufferWriter::write_uuid(&mut buf, &uuid)?; + DataBufferWriter::write_string(&mut buf, &name)?; + DataBufferWriter::write_u8_varint(&mut buf, 0)?; // properties // maybe fix later + let buf = buf.as_bytes(); + + let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes()); + let sig = hmac::sign(&key, &buf); + + p.write_bytes(sig.as_ref())?; + p.write_bytes(buf.as_ref())?; + + Ok(()) + }).as_proxy()?; + + player.write_server_packet(&response)?; + continue; + } + } + + player.write_client_packet(&packet)?; + player.write_server_packet(&player.read_client_packet()?)?; + } _ => { return Err(ProxyError::LoginPacket); }, @@ -144,8 +188,6 @@ impl Player { verify_token }); - debug!("player connected"); - player.client_recv_loop(); player.server_recv_loop(); @@ -155,40 +197,58 @@ impl Player { pub fn client_recv_loop(&self) { let mut client: rust_mc_proto::MinecraftConnection = self.client_conn.clone().lock().unwrap().try_clone().unwrap(); let server = self.server_conn.clone(); + let name = self.name.clone(); thread::spawn(move || { + info!("Player {} connected", name); while let Ok(packet) = client.read_packet() { while !server.lock().unwrap().is_alive() {} server.lock().unwrap().write_packet(&packet).ignore(); } + info!("Player {} disconnected", name); + server.lock().unwrap().close(); }); } + pub fn disconnect(&self) { + self.client_conn.lock().unwrap().close(); + self.server_conn.lock().unwrap().close(); + } + + pub fn kick(&self, text: String) -> Result<(), ProxyError> { + self.write_client_packet(&Packet::build( + 0x1D, |p| p.write_string(&text) + ).as_proxy()?)?; + self.disconnect(); + Ok(()) + } + pub fn server_recv_loop(&self) { let mut server = self.server_conn.clone().lock().unwrap().try_clone().unwrap(); let client = self.client_conn.clone(); + let server_name = self.server.as_ref().unwrap().name.clone(); + let name = self.name.clone(); thread::spawn(move || { + info!("Server {} connected player {}", server_name, name); while let Ok(packet) = server.read_packet() { client.lock().unwrap().write_packet(&packet).ignore(); } + info!("Server {} disconnected player {}", server_name, name); }); } - pub fn connect(&self, config: &ProxyConfig, server_conn: TcpStream) -> Result<(), ProxyError> { + pub fn connect_server(&self, config: &ProxyConfig, server: ServerInfo) -> Result<(), ProxyError> { self.server_conn.lock().unwrap().close(); - let mut server_conn = MCConnTcp::new(server_conn); + let mut server_conn = MCConnTcp::connect(&server.host).as_proxy()?; if let Some(login_info) = &self.login_info { login_info.write(config, &mut server_conn).as_proxy()?; } + *self.server_conn.lock().unwrap() = server_conn; self.server_recv_loop(); Ok(()) } - pub fn connect_server(&self, config: &ProxyConfig, server: ServerInfo) -> Result<(), ProxyError> { - self.connect(config, TcpStream::connect(&server.host).map_err(|_| ProxyError::ServerConnect)?) - } - pub fn write_client_packet(&self, packet: &Packet) -> Result<(), ProxyError> { self.client_conn.lock().unwrap().write_packet(packet).as_proxy() } diff --git a/src/meexprox/meexprox.rs b/src/meexprox/meexprox.rs index 002c1a5..f4b4270 100644 --- a/src/meexprox/meexprox.rs +++ b/src/meexprox/meexprox.rs @@ -1,4 +1,4 @@ -use log::{debug, error, info}; +use log::{error, info}; use rust_mc_proto::{ read_packet, write_packet, DataBufferReader, DataBufferWriter, MCConnTcp, Packet }; @@ -89,6 +89,7 @@ impl MeexProx { } } else if next_state == 2 { self.players.write().unwrap().push(Player::read( + &self.config, protocol_version, server_address, server_port,