From 3d6dd8ed8736fcac894d484e5e3782ff4b045c49 Mon Sep 17 00:00:00 2001 From: Vladislav Golub Date: Tue, 24 Dec 2019 19:44:56 +0300 Subject: [PATCH 1/2] Add login server bound packets --- src/lib.rs | 84 ++++++++++++++++++++++---- src/login.rs | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/status.rs | 4 +- 3 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 src/login.rs diff --git a/src/lib.rs b/src/lib.rs index b9f2810..6cb06a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! This crate implements Minecraft protocol. //! //! Information about protocol can be found at https://wiki.vg/Protocol. +use byteorder::{ReadBytesExt, WriteBytesExt}; use io::Error as IoError; use mc_varint::{VarIntRead, VarIntWrite}; use serde_json::error::Error as JsonError; @@ -9,17 +10,23 @@ use std::io::{Read, Write}; use std::string::FromUtf8Error; pub mod chat; +pub mod login; pub mod status; /// Current supported protocol version. pub const PROTOCOL_VERSION: usize = 498; /// String maximum length. -const MAX_STRING_LENGTH: usize = 32_768; +const STRING_MAX_LENGTH: u32 = 32_768; /// Possible errors while encoding packet. pub enum EncodeError { - /// String length can't be more than `MAX_STRING_LENGTH` value. - StringTooLong, + /// String length can't be more than provided value. + StringTooLong { + /// String length. + length: usize, + /// Max string length. + max_length: u32, + }, IOError { io_error: IoError, }, @@ -46,8 +53,13 @@ pub enum DecodeError { UnknownPacketType { type_id: u8, }, - /// String length can't be more than `MAX_STRING_LENGTH` value. - StringTooLong, + /// String length can't be more than provided value. + StringTooLong { + /// String length. + length: u32, + /// Max string length. + max_length: u32, + }, IOError { io_error: IoError, }, @@ -58,6 +70,8 @@ pub enum DecodeError { Utf8Error { utf8_error: FromUtf8Error, }, + /// Boolean are parsed from byte. Valid byte value are 0 or 1. + NonBoolValue, } impl From for DecodeError { @@ -88,20 +102,36 @@ trait Packet { /// Trait adds additional helper methods for `Read` to read protocol data. trait PacketRead { - fn read_string(&mut self) -> Result; + fn read_bool(&mut self) -> Result; + + fn read_string(&mut self, max_length: u32) -> Result; + + fn read_byte_array(&mut self) -> Result, DecodeError>; } /// Trait adds additional helper methods for `Write` to write protocol data. trait PacketWrite { - fn write_string(&mut self, value: &str) -> Result<(), EncodeError>; + fn write_bool(&mut self, value: bool) -> Result<(), EncodeError>; + + fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError>; + + fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError>; } impl PacketRead for R { - fn read_string(&mut self) -> Result { + fn read_bool(&mut self) -> Result { + match self.read_u8()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(DecodeError::NonBoolValue), + } + } + + fn read_string(&mut self, max_length: u32) -> Result { let length = self.read_var_u32()?; - if length > MAX_STRING_LENGTH as u32 { - return Err(DecodeError::StringTooLong); + if length > max_length as u32 { + return Err(DecodeError::StringTooLong { length, max_length }); } let mut buf = vec![0; length as usize]; @@ -109,12 +139,33 @@ impl PacketRead for R { Ok(String::from_utf8(buf)?) } + + fn read_byte_array(&mut self) -> Result, DecodeError> { + let length = self.read_var_u32()?; + + let mut buf = vec![0; length as usize]; + self.read_exact(&mut buf)?; + + Ok(buf) + } } impl PacketWrite for W { - fn write_string(&mut self, value: &str) -> Result<(), EncodeError> { - if value.len() > MAX_STRING_LENGTH { - return Err(EncodeError::StringTooLong); + fn write_bool(&mut self, value: bool) -> Result<(), EncodeError> { + if value { + self.write_u8(1)?; + } else { + self.write_u8(0)?; + } + + Ok(()) + } + + fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError> { + let length = value.len(); + + if length > max_length as usize { + return Err(EncodeError::StringTooLong { length, max_length }); } self.write_var_u32(value.len() as u32)?; @@ -122,4 +173,11 @@ impl PacketWrite for W { Ok(()) } + + fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError> { + self.write_var_u32(value.len() as u32)?; + self.write_all(value)?; + + Ok(()) + } } diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 0000000..e13e554 --- /dev/null +++ b/src/login.rs @@ -0,0 +1,162 @@ +use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite}; +use mc_varint::{VarIntRead, VarIntWrite}; +use std::io::{Read, Write}; + +/// Login maximum length. +const LOGIN_MAX_LENGTH: u32 = 16; + +pub enum LoginServerBoundPacket { + LoginStart(LoginStart), + EncryptionResponse(EncryptionResponse), + LoginPluginResponse(LoginPluginResponse), +} + +pub enum LoginClientBoundPacket { + Disconnect, + EncryptionRequest, + LoginSuccess, + SetCompression, + LoginPluginRequest, +} + +impl LoginServerBoundPacket { + pub fn get_type_id(&self) -> u8 { + match self { + LoginServerBoundPacket::LoginStart(_) => 0x00, + LoginServerBoundPacket::EncryptionResponse(_) => 0x01, + LoginServerBoundPacket::LoginPluginResponse(_) => 0x02, + } + } + + pub fn decode(type_id: u8, reader: &mut R) -> Result { + match type_id { + 0x00 => { + let login_start = LoginStart::decode(reader)?; + + Ok(LoginServerBoundPacket::LoginStart(login_start)) + } + 0x01 => { + let encryption_response = EncryptionResponse::decode(reader)?; + + Ok(LoginServerBoundPacket::EncryptionResponse( + encryption_response, + )) + } + 0x02 => { + let login_plugin_response = LoginPluginResponse::decode(reader)?; + + Ok(LoginServerBoundPacket::LoginPluginResponse( + login_plugin_response, + )) + } + _ => Err(DecodeError::UnknownPacketType { type_id }), + } + } +} + +pub struct LoginStart { + pub name: String, +} + +impl LoginStart { + pub fn new(name: String) -> LoginServerBoundPacket { + let login_start = LoginStart { name }; + + LoginServerBoundPacket::LoginStart(login_start) + } +} + +impl Packet for LoginStart { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_string(&self.name, LOGIN_MAX_LENGTH) + } + + fn decode(reader: &mut R) -> Result { + let name = reader.read_string(LOGIN_MAX_LENGTH)?; + + Ok(LoginStart { name }) + } +} + +pub struct EncryptionResponse { + pub shared_secret: Vec, + pub verify_token: Vec, +} + +impl EncryptionResponse { + pub fn new(shared_secret: Vec, verify_token: Vec) -> LoginServerBoundPacket { + let encryption_response = EncryptionResponse { + shared_secret, + verify_token, + }; + + LoginServerBoundPacket::EncryptionResponse(encryption_response) + } +} + +impl Packet for EncryptionResponse { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_byte_array(&self.shared_secret)?; + writer.write_byte_array(&self.verify_token)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let shared_secret = reader.read_byte_array()?; + let verify_token = reader.read_byte_array()?; + + Ok(EncryptionResponse { + shared_secret, + verify_token, + }) + } +} + +pub struct LoginPluginResponse { + pub message_id: i32, + pub successful: bool, + pub data: Vec, +} + +impl LoginPluginResponse { + pub fn new(message_id: i32, successful: bool, data: Vec) -> LoginServerBoundPacket { + let login_plugin_response = LoginPluginResponse { + message_id, + successful, + data, + }; + + LoginServerBoundPacket::LoginPluginResponse(login_plugin_response) + } +} + +impl Packet for LoginPluginResponse { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.message_id)?; + writer.write_bool(self.successful)?; + writer.write_all(&self.data)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let message_id = reader.read_var_i32()?; + let successful = reader.read_bool()?; + + let mut data = Vec::new(); + reader.read_to_end(data.as_mut())?; + + Ok(LoginPluginResponse { + message_id, + successful, + data, + }) + } +} diff --git a/src/status.rs b/src/status.rs index 21595e1..9bc5cb5 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,4 +1,4 @@ -use crate::{DecodeError, EncodeError, Packet, PacketWrite}; +use crate::{DecodeError, EncodeError, Packet, PacketWrite, STRING_MAX_LENGTH}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::{Deserialize, Serialize}; use std::io::{Read, Write}; @@ -143,7 +143,7 @@ impl Packet for StatusResponse { fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { let json = serde_json::to_string(&self.server_status)?; - writer.write_string(&json)?; + writer.write_string(&json, STRING_MAX_LENGTH)?; Ok(()) } From 764dfda8ed7cdc5c42f2c0b6a8152dbaf9c95bcd Mon Sep 17 00:00:00 2001 From: Vladislav Golub Date: Tue, 24 Dec 2019 21:30:07 +0300 Subject: [PATCH 2/2] Add login client bound packets --- src/lib.rs | 35 ++++++++- src/login.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 225 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6cb06a0..fcb7e2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,19 @@ //! This crate implements Minecraft protocol. //! //! Information about protocol can be found at https://wiki.vg/Protocol. -use byteorder::{ReadBytesExt, WriteBytesExt}; use io::Error as IoError; -use mc_varint::{VarIntRead, VarIntWrite}; -use serde_json::error::Error as JsonError; use std::io; use std::io::{Read, Write}; use std::string::FromUtf8Error; +use byteorder::ReadBytesExt; +use byteorder::WriteBytesExt; +use mc_varint::{VarIntRead, VarIntWrite}; +use serde_json::error::Error as JsonError; +use uuid::parser::ParseError as UuidParseError; + +use crate::chat::Message; + pub mod chat; pub mod login; pub mod status; @@ -72,6 +77,9 @@ pub enum DecodeError { }, /// Boolean are parsed from byte. Valid byte value are 0 or 1. NonBoolValue, + UuidParseError { + uuid_parse_error: UuidParseError, + }, } impl From for DecodeError { @@ -92,6 +100,12 @@ impl From for DecodeError { } } +impl From for DecodeError { + fn from(uuid_parse_error: UuidParseError) -> Self { + DecodeError::UuidParseError { uuid_parse_error } + } +} + trait Packet { type Output; @@ -107,6 +121,8 @@ trait PacketRead { fn read_string(&mut self, max_length: u32) -> Result; fn read_byte_array(&mut self) -> Result, DecodeError>; + + fn read_chat_message(&mut self) -> Result; } /// Trait adds additional helper methods for `Write` to write protocol data. @@ -116,6 +132,8 @@ trait PacketWrite { fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError>; fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError>; + + fn write_chat_message(&mut self, value: &Message) -> Result<(), EncodeError>; } impl PacketRead for R { @@ -148,6 +166,13 @@ impl PacketRead for R { Ok(buf) } + + fn read_chat_message(&mut self) -> Result { + let json = self.read_string(STRING_MAX_LENGTH)?; + let message = Message::from_json(&json)?; + + Ok(message) + } } impl PacketWrite for W { @@ -180,4 +205,8 @@ impl PacketWrite for W { Ok(()) } + + fn write_chat_message(&mut self, value: &Message) -> Result<(), EncodeError> { + self.write_string(&value.to_json()?, STRING_MAX_LENGTH) + } } diff --git a/src/login.rs b/src/login.rs index e13e554..d7b6717 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,9 +1,14 @@ -use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite}; -use mc_varint::{VarIntRead, VarIntWrite}; use std::io::{Read, Write}; -/// Login maximum length. +use mc_varint::{VarIntRead, VarIntWrite}; +use uuid::Uuid; + +use crate::chat::Message; +use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite, STRING_MAX_LENGTH}; + const LOGIN_MAX_LENGTH: u32 = 16; +const SERVER_ID_MAX_LENGTH: u32 = 20; +const HYPHENATED_UUID_LENGTH: u32 = 36; pub enum LoginServerBoundPacket { LoginStart(LoginStart), @@ -12,11 +17,11 @@ pub enum LoginServerBoundPacket { } pub enum LoginClientBoundPacket { - Disconnect, - EncryptionRequest, - LoginSuccess, - SetCompression, - LoginPluginRequest, + LoginDisconnect(LoginDisconnect), + EncryptionRequest(EncryptionRequest), + LoginSuccess(LoginSuccess), + SetCompression(SetCompression), + LoginPluginRequest(LoginPluginRequest), } impl LoginServerBoundPacket { @@ -160,3 +165,183 @@ impl Packet for LoginPluginResponse { }) } } + +pub struct LoginDisconnect { + pub reason: Message, +} + +impl LoginDisconnect { + pub fn new(reason: Message) -> LoginClientBoundPacket { + let login_disconnect = LoginDisconnect { reason }; + + LoginClientBoundPacket::LoginDisconnect(login_disconnect) + } +} + +impl Packet for LoginDisconnect { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_chat_message(&self.reason)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let reason = reader.read_chat_message()?; + + Ok(LoginDisconnect { reason }) + } +} + +pub struct EncryptionRequest { + pub server_id: String, + pub public_key: Vec, + pub verify_token: Vec, +} + +impl EncryptionRequest { + pub fn new( + server_id: String, + public_key: Vec, + verify_token: Vec, + ) -> LoginClientBoundPacket { + let encryption_request = EncryptionRequest { + server_id, + public_key, + verify_token, + }; + + LoginClientBoundPacket::EncryptionRequest(encryption_request) + } +} + +impl Packet for EncryptionRequest { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_string(&self.server_id, SERVER_ID_MAX_LENGTH)?; + writer.write_byte_array(&self.public_key)?; + writer.write_byte_array(&self.verify_token)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let server_id = reader.read_string(SERVER_ID_MAX_LENGTH)?; + let public_key = reader.read_byte_array()?; + let verify_token = reader.read_byte_array()?; + + Ok(EncryptionRequest { + server_id, + public_key, + verify_token, + }) + } +} + +pub struct LoginSuccess { + pub uuid: Uuid, + pub username: String, +} + +impl LoginSuccess { + pub fn new(uuid: Uuid, username: String) -> LoginClientBoundPacket { + let login_success = LoginSuccess { uuid, username }; + + LoginClientBoundPacket::LoginSuccess(login_success) + } +} + +impl Packet for LoginSuccess { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + let uuid_hyphenated_string = self.uuid.to_hyphenated().to_string(); + + writer.write_string(&uuid_hyphenated_string, HYPHENATED_UUID_LENGTH)?; + writer.write_string(&self.username, LOGIN_MAX_LENGTH)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let uuid_hyphenated_string = reader.read_string(HYPHENATED_UUID_LENGTH)?; + + let uuid = Uuid::parse_str(&uuid_hyphenated_string)?; + let username = reader.read_string(LOGIN_MAX_LENGTH)?; + + Ok(LoginSuccess { uuid, username }) + } +} + +pub struct SetCompression { + pub threshold: i32, +} + +impl SetCompression { + pub fn new(threshold: i32) -> LoginClientBoundPacket { + let set_compression = SetCompression { threshold }; + + LoginClientBoundPacket::SetCompression(set_compression) + } +} + +impl Packet for SetCompression { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.threshold)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let threshold = reader.read_var_i32()?; + + Ok(SetCompression { threshold }) + } +} + +pub struct LoginPluginRequest { + pub message_id: i32, + pub channel: String, + pub data: Vec, +} + +impl LoginPluginRequest { + pub fn new(message_id: i32, channel: String, data: Vec) -> LoginClientBoundPacket { + let login_plugin_request = LoginPluginRequest { + message_id, + channel, + data, + }; + + LoginClientBoundPacket::LoginPluginRequest(login_plugin_request) + } +} + +impl Packet for LoginPluginRequest { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.message_id)?; + writer.write_string(&self.channel, STRING_MAX_LENGTH)?; + writer.write_all(&self.data)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let message_id = reader.read_var_i32()?; + let channel = reader.read_string(STRING_MAX_LENGTH)?; + let mut data = Vec::new(); + reader.read_to_end(data.as_mut())?; + + Ok(LoginPluginRequest { + message_id, + channel, + data, + }) + } +}