From afd699fb6a61124ed028e2660678976895660f19 Mon Sep 17 00:00:00 2001 From: Lixfel Date: Wed, 9 Mar 2022 11:28:11 +0100 Subject: [PATCH] Compression and encryption --- Cargo.toml | 3 ++ src/frames.rs | 131 +++++++++++++++++++++++++++++++++++++++--------- src/main.rs | 28 ++--------- src/protocol.rs | 74 +++++++++++++++++++++++++++ src/serde.rs | 12 ++++- 5 files changed, 198 insertions(+), 50 deletions(-) create mode 100644 src/protocol.rs diff --git a/Cargo.toml b/Cargo.toml index bdae01c..66007dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,7 @@ serde = { version = "1.0", features = ["derive", "std"] } serde_json = "1.0" bytes = "1.1" uuid = "0.8" +cfb8 = "0.7" +aes = "0.7" +miniz_oxide = "0.5" lixcraft-derive = { path = "lixcraft-derive" } \ No newline at end of file diff --git a/src/frames.rs b/src/frames.rs index b0e0e10..e7622e7 100644 --- a/src/frames.rs +++ b/src/frames.rs @@ -1,10 +1,55 @@ -use std::io::ErrorKind; +use std::pin::Pin; +use std::task::{Context, Poll}; +use aes::Aes128; use bytes::{Buf, BufMut, Bytes, BytesMut}; -use tokio::io::Error; +use cfb8::cipher::AsyncStreamCipher; +use cfb8::Cfb8; +use miniz_oxide::deflate::{compress_to_vec_zlib, CompressionLevel}; +use miniz_oxide::inflate::decompress_to_vec_zlib_with_limit; +use tokio::io::{AsyncRead, AsyncWrite, Error, ReadBuf}; use tokio_util::codec::{Decoder, Encoder}; -use crate::VarInt; +use crate::{JavaSerializable, VarInt}; +use crate::serde::JavaDeserError; -pub struct LengthPrefixedFrame { + +pub struct JavaEncryptor { + inner: W, + cipher: Cfb8 +} +impl AsyncWrite for JavaEncryptor { + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + let mut out = buf.to_vec(); + self.cipher.encrypt(&mut out[..]); + Pin::new(&mut (self.inner)).poll_write(cx, &out[..]) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_shutdown(cx) + } +} + +pub struct JavaDecryptor { + inner: R, + cipher: Cfb8 +} +impl AsyncRead for JavaDecryptor { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll> { + let cur = buf.filled().len(); + match Pin::new(&mut self.inner).poll_read(cx, buf) { + Poll::Ready(Ok(())) => { + self.cipher.decrypt(&mut buf.filled_mut()[cur..]); + Poll::Ready(Ok(())) + }, + v => v + } + } +} + +pub struct LengthPrefixedCodec { pub max_length: usize } @@ -13,7 +58,7 @@ enum VarIntReadError { InvalidData } -impl LengthPrefixedFrame { +impl LengthPrefixedCodec { fn try_read_var_int(&self, src: &mut BytesMut) -> Result<(VarInt, usize), VarIntReadError> { let src = src.chunk(); let mut num_read = 0; @@ -42,9 +87,9 @@ impl LengthPrefixedFrame { } } -impl Decoder for LengthPrefixedFrame { - type Item = BytesMut; - type Error = tokio::io::Error; +impl Decoder for LengthPrefixedCodec { + type Item = Bytes; + type Error = JavaDeserError; fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { println!("!"); @@ -54,34 +99,72 @@ impl Decoder for LengthPrefixedFrame { if src.len() >= total_length { src.advance(prefix_length); - Ok(Some(src.split_to(usize::from(length)))) + Ok(Some(src.split_to(usize::from(length)).freeze())) } else { Ok(None) } }, Err(VarIntReadError::BytesMissing) => Ok(None), - Err(VarIntReadError::InvalidData) => Err(Error::from(ErrorKind::InvalidData)) + Err(VarIntReadError::InvalidData) => Err(JavaDeserError::InvalidValue) } } } -impl Encoder for LengthPrefixedFrame { - type Error = tokio::io::Error; +impl Encoder for LengthPrefixedCodec { + type Error = JavaDeserError; fn encode(&mut self, data: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> { - dst.reserve(5 + data.len()); - - let mut v = data.len(); - loop { - if v & 0x80 == 0 { - dst.put_u8(v as u8); - break; - } - - dst.put_u8((v as u8 & 0x7f) | 0x80); - v >>= 7; - } + let len = VarInt::from(data.len()); + dst.reserve(len.size_in_bytes() + data.len()); + len.serialize(dst); dst.put_slice(&data); Ok(()) } +} + +pub struct CompressedCodec { + pub length_prefixed: LengthPrefixedCodec, + pub threshold: usize +} + +impl Encoder for CompressedCodec { + type Error = JavaDeserError; + + fn encode(&mut self, data: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> { + let (inner_len, data) = if data.len() < self.threshold { + (VarInt(0), data) + } else { + (VarInt::from(data.len()), Bytes::from(compress_to_vec_zlib(&data[..], CompressionLevel::BestSpeed as u8))) + }; + + let total_len = VarInt::from(inner_len.size_in_bytes() + data.len()); + dst.reserve(total_len.size_in_bytes() + inner_len.size_in_bytes() + data.len()); + total_len.serialize(dst); + inner_len.serialize(dst); + dst.put_slice(&data[..]); + Ok(()) + } +} +impl Decoder for CompressedCodec { + type Item = Bytes; + type Error = JavaDeserError; + + fn decode(&mut self, src: &mut BytesMut) -> Result, Self::Error> { + match self.length_prefixed.decode(src) { + Ok(Some(mut buf)) => { + let inner_len = VarInt::deserialize(&mut buf)?; + if *inner_len == 0 { + Ok(Some(buf)) + } else { + let decompressed = decompress_to_vec_zlib_with_limit(&buf[..], *inner_len as usize).map_err(|_| JavaDeserError::InvalidValue)?; + if *inner_len as usize != decompressed.len() { + Err(JavaDeserError::InvalidValue) + } else { + Ok(Some(Bytes::from(decompressed))) + } + } + }, + v => v + } + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b8ddc28..92caa07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,16 +3,15 @@ extern crate core; pub mod serde; pub mod packets; pub mod frames; +pub mod protocol; -use std::collections::HashMap; use std::error::Error; use std::net::SocketAddr; use std::sync::Arc; -use bytes::BytesMut; use tokio::net::{TcpListener, TcpStream}; use tokio_stream::StreamExt; use tokio_util::codec::Framed; -use crate::frames::LengthPrefixedFrame; +use crate::frames::LengthPrefixedCodec; use crate::packets::handshake::HandshakePacket; use crate::serde::{JavaSerializable, VarInt}; @@ -59,29 +58,10 @@ async fn main() -> Result<(), Box> { Ok(()) } -enum Reason { - Disconnected, - ConnectionDrop, - ProtocolError -} - -struct Protocol { - packet_handlers: HashMap Reason>> -} - -fn handle_handshake(buf: BytesMut) -> Result, Reason> { - Err(Reason::Disconnected) -} - async fn process_initial(stream: TcpStream, _addr: SocketAddr) { - /*let handshake_protocol = Protocol { - packet_handlers: HashMap::from([(VarInt(0), Box::new(handle_handshake) as Box Reason>)]), - };*/ - - let mut framed = Framed::new(stream, LengthPrefixedFrame {max_length: 263}); + let mut framed = Framed::new(stream, LengthPrefixedCodec {max_length: 263}); match framed.next().await { - Some(Ok(buf)) => { - let mut buf = buf.freeze(); + Some(Ok(mut buf)) => { match VarInt::deserialize(&mut buf) { Ok(VarInt(0)) => { match HandshakePacket::deserialize(&mut buf) { diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..8c75ec0 --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,74 @@ +use std::collections::HashMap; +use bytes::Bytes; +use crate::{HandshakePacket, VarInt}; + +pub const CURRENT_VERSION: VarInt = VarInt(758); + +pub fn version_to_string(version: VarInt) -> Option<&'static str> { + Some(match *version { + 758 => "1.18.2", + 757 => "1.18.1", + 756 => "1.17.1", + 755 => "1.17", + 754 => "1.16.5", + 753 => "1.16.3", + 751 => "1.16.2", + 736 => "1.16.1", + 735 => "1.16", + 578 => "1.15.2", + 575 => "1.15.1", + 573 => "1.15", + 498 => "1.14.4", + 490 => "1.14.3", + 485 => "1.14.2", + 480 => "1.14.1", + 477 => "1.14", + 404 => "1.13.2", + 401 => "1.13.1", + 393 => "1.13", + 340 => "1.12.2", + 338 => "1.12.1", + 335 => "1.12", + 316 => "1.11.2", + 315 => "1.11", + 210 => "1.10.2", + 110 => "1.9.4", + 109 => "1.9.2", + 108 => "1.9.1", + 107 => "1.9", + 47 => "1.8.9", + 5 => "1.7.10", + 4 => "1.7.5", + _ => return None + }) +} + +pub enum Todo { + Continue, + Disconnect +} + +pub trait Protocol { + fn handle_buf(&self, buf: Bytes) -> Option>; +} +pub struct HandshakeProtocol {} +impl Protocol for HandshakeProtocol { + fn handle_buf(&self, buf: Bytes) -> Option> { + todo!() + } +} + +pub struct GenericProtocol { + packet_handlers: HashMap Todo>> +} + +fn handle_handshake(handshake: HandshakePacket) -> Todo { + todo!() +} +/* +pub fn handshake_protocol() -> Protocol { + Protocol { + packet_handlers: HashMap::from([(VarInt(0), Box::new(handle_handshake) as Box Todo>)]), + } +} +*/ \ No newline at end of file diff --git a/src/serde.rs b/src/serde.rs index aa04ced..937a9ea 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,6 +1,7 @@ +use std::io::Error; use std::ops::{DerefMut, Deref}; use bytes::{Buf, BufMut, Bytes, BytesMut}; -use crate::serde::JavaDeserError::{InvalidValue, MissingBytes}; +use crate::serde::JavaDeserError::{InvalidValue, MissingBytes, OtherError}; pub use lixcraft_derive::JavaSerializable; use uuid::Uuid; @@ -8,7 +9,14 @@ use uuid::Uuid; #[derive(Debug)] pub enum JavaDeserError { InvalidValue, - MissingBytes + MissingBytes, + OtherError +} + +impl From for JavaDeserError { + fn from(_: Error) -> Self { + OtherError + } } pub trait JavaSerializable: Sized {