From eeb63b8901a9c049f1bb594abb9ce9b4a9c47620 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 11 Jan 2021 16:39:43 +0000 Subject: zlib -> libdeflate (#5085) + Use libdeflate + Use std::byte * Fix passing temporary to string_view + Emulate make_unique_for_overwrite --- src/Protocol/ChunkDataSerializer.cpp | 9 +- src/Protocol/ChunkDataSerializer.h | 14 +- src/Protocol/ForgeHandshake.cpp | 77 ++++---- src/Protocol/ForgeHandshake.h | 10 +- src/Protocol/Packetizer.h | 4 +- src/Protocol/Protocol.h | 6 +- src/Protocol/ProtocolRecognizer.cpp | 13 +- src/Protocol/Protocol_1_10.cpp | 2 +- src/Protocol/Protocol_1_11.cpp | 2 +- src/Protocol/Protocol_1_12.cpp | 4 +- src/Protocol/Protocol_1_13.cpp | 15 +- src/Protocol/Protocol_1_14.cpp | 3 +- src/Protocol/Protocol_1_8.cpp | 361 +++++++++++++++++------------------ src/Protocol/Protocol_1_8.h | 22 ++- src/Protocol/Protocol_1_9.cpp | 14 +- src/Protocol/Protocol_1_9.h | 2 +- 16 files changed, 268 insertions(+), 290 deletions(-) (limited to 'src/Protocol') diff --git a/src/Protocol/ChunkDataSerializer.cpp b/src/Protocol/ChunkDataSerializer.cpp index ac740a145..a16cce02b 100644 --- a/src/Protocol/ChunkDataSerializer.cpp +++ b/src/Protocol/ChunkDataSerializer.cpp @@ -1,6 +1,5 @@ #include "Globals.h" #include "ChunkDataSerializer.h" -#include "zlib/zlib.h" #include "Protocol_1_8.h" #include "Protocol_1_9.h" #include "../ClientHandle.h" @@ -543,14 +542,10 @@ inline void cChunkDataSerializer::WriteSectionDataSeamless(const cChunkData::sCh inline void cChunkDataSerializer::CompressPacketInto(ChunkDataCache & a_Cache) { - m_Packet.ReadAll(a_Cache.PacketData); + m_Compressor.ReadFrom(m_Packet); m_Packet.CommitRead(); - if (!cProtocol_1_8_0::CompressPacket(a_Cache.PacketData, a_Cache.ToSend)) - { - ASSERT(!"Packet compression failed."); - return; - } + cProtocol_1_8_0::CompressPacket(m_Compressor, a_Cache.ToSend); a_Cache.Engaged = true; } diff --git a/src/Protocol/ChunkDataSerializer.h b/src/Protocol/ChunkDataSerializer.h index aeff7c356..ea0b0be11 100644 --- a/src/Protocol/ChunkDataSerializer.h +++ b/src/Protocol/ChunkDataSerializer.h @@ -3,6 +3,8 @@ #include "../ByteBuffer.h" #include "../ChunkData.h" #include "../Defines.h" +#include "CircularBufferCompressor.h" +#include "StringCompression.h" @@ -37,8 +39,7 @@ class cChunkDataSerializer /** A single cache entry containing the raw data, compressed data, and a validity flag. */ struct ChunkDataCache { - std::string PacketData; - std::string ToSend; + ContiguousByteBuffer ToSend; bool Engaged = false; }; @@ -50,7 +51,7 @@ public: Parameters are the coordinates of the chunk to serialise, and the data and biome data read from the chunk. */ void SendToClients(int a_ChunkX, int a_ChunkZ, const cChunkData & a_Data, const unsigned char * a_BiomeData, const ClientHandles & a_SendTo); -protected: +private: /** Serialises the given chunk, storing the result into the given cache entry, and sends the data. If the cache entry is already present, simply re-uses it. */ @@ -74,6 +75,9 @@ protected: /** A staging area used to construct the chunk packet, persistent to avoid reallocating. */ cByteBuffer m_Packet; + /** A compressor used to compress the chunk data. */ + CircularBufferCompressor m_Compressor; + /** The dimension for the World this Serializer is tied to. */ const eDimension m_Dimension; @@ -81,7 +85,3 @@ protected: It is used during a single invocation of SendToClients with more than one client. */ std::array(CacheVersion::Last) + 1> m_Cache; } ; - - - - diff --git a/src/Protocol/ForgeHandshake.cpp b/src/Protocol/ForgeHandshake.cpp index 8c16d4f0c..99b585894 100644 --- a/src/Protocol/ForgeHandshake.cpp +++ b/src/Protocol/ForgeHandshake.cpp @@ -36,8 +36,8 @@ namespace ClientPhase /** Server handshake state phases. */ namespace ServerPhase { - static const Int8 WAITINGCACK = 2; - static const Int8 COMPLETE = 3; + static const auto WAITINGCACK = std::byte(2); + static const auto COMPLETE = std::byte(3); } @@ -105,12 +105,12 @@ void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID & m_UUID = a_UUID; m_Properties = a_Properties; - static const std::array Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; - AString ChannelsString; + static const std::array Channels{{ "FML|HS", "FML", "FML|MP", "FML", "FORGE" }}; + ContiguousByteBuffer ChannelsString; for (auto & Channel: Channels) { - ChannelsString.append(Channel); - ChannelsString.push_back('\0'); + ChannelsString.append({ reinterpret_cast(Channel.data()), Channel.size() }); + ChannelsString.push_back(std::byte(0)); } m_Client->SendPluginMessage("REGISTER", ChannelsString); @@ -123,7 +123,6 @@ void cForgeHandshake::BeginForgeHandshake(const AString & a_Name, const cUUID & void cForgeHandshake::SendServerHello() { - AString Message; cByteBuffer Buf(6); // Discriminator | Byte | Always 0 for ServerHello Buf.WriteBEInt8(Discriminator::ServerHello); @@ -131,6 +130,8 @@ void cForgeHandshake::SendServerHello() Buf.WriteBEInt8(2); // Dimension TODO Buf.WriteBEInt32(0); + + ContiguousByteBuffer Message; Buf.ReadAll(Message); m_Client->SendPluginMessage("FML|HS", Message); @@ -140,18 +141,18 @@ void cForgeHandshake::SendServerHello() -AStringMap cForgeHandshake::ParseModList(const char * a_Data, size_t a_Size) +AStringMap cForgeHandshake::ParseModList(const ContiguousByteBufferView a_Data) { AStringMap Mods; - if (a_Size < 4) + if (a_Data.size() < 4) { - SetError(Printf("ParseModList invalid packet, missing length (size = %zu)", a_Size)); + SetError(Printf("ParseModList invalid packet, missing length (size = %zu)", a_Data.size())); return Mods; } - cByteBuffer Buf(a_Size); - Buf.Write(a_Data, a_Size); + cByteBuffer Buf(a_Data.size()); + Buf.Write(a_Data.data(), a_Data.size()); UInt32 NumMods; if (!Buf.ReadVarInt32(NumMods)) { @@ -182,11 +183,11 @@ AStringMap cForgeHandshake::ParseModList(const char * a_Data, size_t a_Size) -void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { - if (a_Size == 2) + if (a_Data.size() == 2) { - int FmlProtocolVersion = a_Data[1]; + const auto FmlProtocolVersion = static_cast(a_Data[1]); LOGD("Received ClientHello with FML protocol version %d", FmlProtocolVersion); if (FmlProtocolVersion != 2) { @@ -195,7 +196,7 @@ void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a } else { - SetError(Printf("Received unexpected length of ClientHello: %zu", a_Size)); + SetError(Printf("Received unexpected length of ClientHello: %zu", a_Data.size())); } } @@ -203,11 +204,11 @@ void cForgeHandshake::HandleClientHello(cClientHandle * a_Client, const char * a -void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleModList(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { LOGD("Received ModList"); - auto ClientMods = ParseModList(a_Data + 1, a_Size - 1); + auto ClientMods = ParseModList(a_Data.substr(1)); AString ClientModsString; for (auto & item: ClientMods) { @@ -241,7 +242,8 @@ void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Dat Buf.WriteVarUTF8String(item.first); // name Buf.WriteVarUTF8String(item.second); // version } - AString ServerModList; + + ContiguousByteBuffer ServerModList; Buf.ReadAll(ServerModList); m_Client->SendPluginMessage("FML|HS", ServerModList); @@ -251,15 +253,15 @@ void cForgeHandshake::HandleModList(cClientHandle * a_Client, const char * a_Dat -void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { - if (a_Size != 2) + if (a_Data.size() != 2) { - SetError(Printf("Unexpected HandshakeAck packet length: %zu", a_Size)); + SetError(Printf("Unexpected HandshakeAck packet length: %zu", a_Data.size())); return; } - auto Phase = a_Data[1]; + const auto Phase = static_cast(a_Data[1]); LOGD("Received client HandshakeAck with phase = %d", Phase); switch (Phase) @@ -282,7 +284,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * Buf.WriteVarInt32(NumSubstitutions); Buf.WriteVarInt32(NumDummies); - AString RegistryData; + ContiguousByteBuffer RegistryData; Buf.ReadAll(RegistryData); m_Client->SendPluginMessage("FML|HS", RegistryData); break; @@ -292,8 +294,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * { LOGD("Client finished receiving registry data; acknowledging"); - AString Ack; - Ack.push_back(Discriminator::HandshakeAck); + ContiguousByteBuffer Ack; + Ack.push_back(std::byte(Discriminator::HandshakeAck)); Ack.push_back(ServerPhase::WAITINGCACK); m_Client->SendPluginMessage("FML|HS", Ack); break; @@ -303,8 +305,8 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * { LOGD("Client is pending completion; sending complete ack"); - AString Ack; - Ack.push_back(Discriminator::HandshakeAck); + ContiguousByteBuffer Ack; + Ack.push_back(std::byte(Discriminator::HandshakeAck)); Ack.push_back(ServerPhase::COMPLETE); m_Client->SendPluginMessage("FML|HS", Ack); @@ -320,7 +322,7 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * default: { - SetError(Printf("Received unknown phase in Forge handshake acknowledgement: %d", Phase)); + SetError(fmt::format("Received unknown phase in Forge handshake acknowledgement: {}", Phase)); break; } } @@ -330,11 +332,11 @@ void cForgeHandshake::HandleHandshakeAck(cClientHandle * a_Client, const char * -void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size) +void cForgeHandshake::DataReceived(cClientHandle * a_Client, const ContiguousByteBufferView a_Data) { if (!m_IsForgeClient) { - SetError(Printf("Received unexpected Forge data from non-Forge client (%zu bytes)", a_Size)); + SetError(Printf("Received unexpected Forge data from non-Forge client (%zu bytes)", a_Data.size())); return; } if (m_Errored) @@ -343,19 +345,18 @@ void cForgeHandshake::DataReceived(cClientHandle * a_Client, const char * a_Data return; } - if (a_Size <= 1) + if (a_Data.size() <= 1) { - SetError(Printf("Received unexpectedly short Forge data (%zu bytes)", a_Size)); + SetError(Printf("Received unexpectedly short Forge data (%zu bytes)", a_Data.size())); return; } - auto Discriminator = a_Data[0]; - + const auto Discriminator = static_cast(a_Data[0]); switch (Discriminator) { - case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data, a_Size); break; - case Discriminator::ModList: HandleModList(a_Client, a_Data, a_Size); break; - case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data, a_Size); break; + case Discriminator::ClientHello: HandleClientHello(a_Client, a_Data); break; + case Discriminator::ModList: HandleModList(a_Client, a_Data); break; + case Discriminator::HandshakeAck: HandleHandshakeAck(a_Client, a_Data); break; default: { diff --git a/src/Protocol/ForgeHandshake.h b/src/Protocol/ForgeHandshake.h index 46e0efaa6..061369c15 100644 --- a/src/Protocol/ForgeHandshake.h +++ b/src/Protocol/ForgeHandshake.h @@ -34,7 +34,7 @@ public: void SendServerHello(); /** Process received data from the client advancing the Forge handshake. */ - void DataReceived(cClientHandle * a_Client, const char * a_Data, size_t a_Size); + void DataReceived(cClientHandle * a_Client, ContiguousByteBufferView a_Data); private: /** True if the Forge handshake is in an errored state. */ @@ -48,13 +48,13 @@ private: cUUID m_UUID; Json::Value m_Properties; - void HandleClientHello(cClientHandle * a_Client, const char * a_Data, size_t a_Size); - void HandleModList(cClientHandle * a_Client, const char * a_Data, size_t a_Size); - void HandleHandshakeAck(cClientHandle * a_Client, const char * a_Data, size_t a_Size); + void HandleClientHello(cClientHandle * a_Client, ContiguousByteBufferView a_Data); + void HandleModList(cClientHandle * a_Client, ContiguousByteBufferView a_Data); + void HandleHandshakeAck(cClientHandle * a_Client, ContiguousByteBufferView a_Data); /** Set errored state to prevent further handshake message processing. */ void SetError(const AString & message); /** Parse the client ModList packet of installed Forge mods and versions. */ - AStringMap ParseModList(const char * a_Data, size_t a_Size); + AStringMap ParseModList(ContiguousByteBufferView a_Data); }; diff --git a/src/Protocol/Packetizer.h b/src/Protocol/Packetizer.h index 4ece6e4fa..f4d632a27 100644 --- a/src/Protocol/Packetizer.h +++ b/src/Protocol/Packetizer.h @@ -119,9 +119,9 @@ public: } - inline void WriteBuf(const char * a_Data, size_t a_Size) + inline void WriteBuf(const ContiguousByteBufferView a_Data) { - VERIFY(m_Out.Write(a_Data, a_Size)); + VERIFY(m_Out.Write(a_Data.data(), a_Data.size())); } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 5c9d5105f..341e0b0f4 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -365,7 +365,7 @@ public: virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0; virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 0; virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) = 0; - virtual void SendChunkData (const std::string_view a_ChunkData) = 0; + virtual void SendChunkData (ContiguousByteBufferView a_ChunkData) = 0; virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) = 0; @@ -405,7 +405,7 @@ public: virtual void SendPlayerMoveLook (void) = 0; virtual void SendPlayerPosition (void) = 0; virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; - virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; + virtual void SendPluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; virtual void SendResetTitle (void) = 0; virtual void SendResourcePack (const AString & a_ResourcePackUrl) = 0; @@ -468,7 +468,7 @@ protected: virtual Version GetProtocolVersion() = 0; /** A generic data-sending routine, all outgoing packet data needs to be routed through this so that descendants may override it. */ - virtual void SendData(const char * a_Data, size_t a_Size) = 0; + virtual void SendData(ContiguousByteBufferView a_Data) = 0; /** Sends a single packet contained within the cPacketizer class. The cPacketizer's destructor calls this to send the contained packet; protocol may transform the data (compression in 1.8 etc). */ diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 5d146c46a..dc6b93b01 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -123,11 +123,6 @@ void cMultiVersionProtocol::HandleIncomingDataInRecognitionStage(cClientHandle & HandleIncomingDataInOldPingResponseStage(a_Clyent, a_In); }; } - catch (const std::exception & Oops) - { - a_Client.Kick(Oops.what()); - return; - } // Explicitly process any remaining data with the new handler: HandleIncomingData(a_Client, a_Data); @@ -341,15 +336,15 @@ void cMultiVersionProtocol::SendPacket(cClientHandle & a_Client, cByteBuffer & a // Compression doesn't apply to this state, send raw data: VERIFY(OutPacketLenBuffer.WriteVarInt32(PacketLen)); - AString LengthData; + ContiguousByteBuffer LengthData; OutPacketLenBuffer.ReadAll(LengthData); - a_Client.SendData(LengthData.data(), LengthData.size()); + a_Client.SendData(LengthData); // Send the packet's payload: - AString PacketData; + ContiguousByteBuffer PacketData; a_OutPacketBuffer.ReadAll(PacketData); a_OutPacketBuffer.CommitRead(); - a_Client.SendData(PacketData.data(), PacketData.size()); + a_Client.SendData(PacketData); } diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index 0efef360c..b3a3205d5 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -679,7 +679,7 @@ void cProtocol_1_10_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 05bf35562..ff0c34223 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -520,7 +520,7 @@ void cProtocol_1_11_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 93a1160ee..30a975d18 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -1045,7 +1045,7 @@ void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) { // TODO not yet used, not sure if it is needed // https://wiki.vg/index.php?title=Protocol&oldid=14204#Crafting_Book_Data - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); } @@ -1054,7 +1054,7 @@ void cProtocol_1_12::HandlePacketCraftingBookData(cByteBuffer & a_ByteBuffer) void cProtocol_1_12::HandlePacketAdvancementTab(cByteBuffer & a_ByteBuffer) { - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); m_Client->GetPlayer()->SendMessageInfo("The new advancements are not implemented."); } diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index 251f8cf55..4da065af7 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -298,13 +298,14 @@ void cProtocol_1_13::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) m_Client->SetClientBrand(Brand); // Send back our brand, including the length: - SendPluginMessage("minecraft:brand", "\x08""Cuberite"); + m_Client->SendPluginMessage("minecraft:brand", "\x08""Cuberite"); return; } + ContiguousByteBuffer Data; + // Read the plugin message and relay to clienthandle: - AString Data; - VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds + VERIFY(a_ByteBuffer.ReadSome(Data, a_ByteBuffer.GetReadableSpace())); // Always succeeds m_Client->HandlePluginMessage(Channel, Data); } @@ -694,8 +695,8 @@ bool cProtocol_1_13::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; @@ -1423,8 +1424,8 @@ bool cProtocol_1_13_2::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; diff --git a/src/Protocol/Protocol_1_14.cpp b/src/Protocol/Protocol_1_14.cpp index e015c6cd1..bc0e68d94 100644 --- a/src/Protocol/Protocol_1_14.cpp +++ b/src/Protocol/Protocol_1_14.cpp @@ -231,8 +231,7 @@ bool cProtocol_1_14::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketTyp // Game switch (a_PacketType) { - default: AString dum; a_ByteBuffer.ReadAll(dum); a_ByteBuffer.CommitRead(); a_ByteBuffer.Write(" ", 1); - return true; + default: a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); a_ByteBuffer.CommitRead(); return true; } } diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp index d9d18b41a..12e2d441c 100644 --- a/src/Protocol/Protocol_1_8.cpp +++ b/src/Protocol/Protocol_1_8.cpp @@ -85,7 +85,6 @@ Implements the 1.8 protocol classes: const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows... -const uLongf MAX_COMPRESSED_PACKET_LEN = 200 KiB; // Maximum size of compressed packets. static const UInt32 CompressionThreshold = 256; // After how large a packet should we compress it. @@ -178,7 +177,7 @@ void cProtocol_1_8_0::DataReceived(cByteBuffer & a_Buffer, const char * a_Data, { if (m_IsEncrypted) { - Byte Decrypted[512]; + std::byte Decrypted[512]; while (a_Size > 0) { size_t NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size; @@ -320,12 +319,12 @@ void cProtocol_1_8_0::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type -void cProtocol_1_8_0::SendChunkData(const std::string_view a_ChunkData) +void cProtocol_1_8_0::SendChunkData(const ContiguousByteBufferView a_ChunkData) { ASSERT(m_State == 3); // In game mode? cCSLock Lock(m_CSPacket); - SendData(a_ChunkData.data(), a_ChunkData.size()); + SendData(a_ChunkData); } @@ -1143,13 +1142,13 @@ void cProtocol_1_8_0::SendPlayerSpawn(const cPlayer & a_Player) -void cProtocol_1_8_0::SendPluginMessage(const AString & a_Channel, const AString & a_Message) +void cProtocol_1_8_0::SendPluginMessage(const AString & a_Channel, const ContiguousByteBufferView a_Message) { ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, pktPluginMessage); Pkt.WriteString(a_Channel); - Pkt.WriteBuf(a_Message.data(), a_Message.size()); + Pkt.WriteBuf(a_Message); } @@ -1717,11 +1716,11 @@ void cProtocol_1_8_0::SendWindowProperty(const cWindow & a_Window, size_t a_Prop -bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_CompressedData) +void cProtocol_1_8_0::CompressPacket(CircularBufferCompressor & a_Packet, ContiguousByteBuffer & a_CompressedData) { - const auto UncompressedSize = a_Packet.size(); + const auto Uncompressed = a_Packet.GetView(); - if (UncompressedSize < CompressionThreshold) + if (Uncompressed.size() < CompressionThreshold) { /* Size doesn't reach threshold, not worth compressing. @@ -1734,7 +1733,7 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr ---------------------------------------------- */ const UInt32 DataSize = 0; - const auto PacketSize = static_cast(cByteBuffer::GetVarIntSize(DataSize) + UncompressedSize); + const auto PacketSize = static_cast(cByteBuffer::GetVarIntSize(DataSize) + Uncompressed.size()); cByteBuffer LengthHeaderBuffer( cByteBuffer::GetVarIntSize(PacketSize) + @@ -1744,14 +1743,14 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr LengthHeaderBuffer.WriteVarInt32(PacketSize); LengthHeaderBuffer.WriteVarInt32(DataSize); - AString LengthData; + ContiguousByteBuffer LengthData; LengthHeaderBuffer.ReadAll(LengthData); - a_CompressedData.reserve(LengthData.size() + UncompressedSize); - a_CompressedData.assign(LengthData.data(), LengthData.size()); - a_CompressedData.append(a_Packet); + a_CompressedData.reserve(LengthData.size() + Uncompressed.size()); + a_CompressedData = LengthData; + a_CompressedData += Uncompressed; - return true; + return; } /* Definitely worth compressing. @@ -1765,28 +1764,11 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr ---------------------------------------------- */ - // Compress the data: - char CompressedData[MAX_COMPRESSED_PACKET_LEN]; - - uLongf CompressedSize = compressBound(static_cast(a_Packet.size())); - if (CompressedSize >= MAX_COMPRESSED_PACKET_LEN) - { - ASSERT(!"Too high packet size."); - return false; - } - - if ( - compress2( - reinterpret_cast(CompressedData), &CompressedSize, - reinterpret_cast(a_Packet.data()), static_cast(a_Packet.size()), Z_DEFAULT_COMPRESSION - ) != Z_OK - ) - { - return false; - } + const auto CompressedData = a_Packet.Compress(); + const auto Compressed = CompressedData.GetView(); - const UInt32 DataSize = static_cast(UncompressedSize); - const auto PacketSize = static_cast(cByteBuffer::GetVarIntSize(DataSize) + CompressedSize); + const UInt32 DataSize = static_cast(Uncompressed.size()); + const auto PacketSize = static_cast(cByteBuffer::GetVarIntSize(DataSize) + Compressed.size()); cByteBuffer LengthHeaderBuffer( cByteBuffer::GetVarIntSize(PacketSize) + @@ -1796,14 +1778,12 @@ bool cProtocol_1_8_0::CompressPacket(const AString & a_Packet, AString & a_Compr LengthHeaderBuffer.WriteVarInt32(PacketSize); LengthHeaderBuffer.WriteVarInt32(DataSize); - AString LengthData; + ContiguousByteBuffer LengthData; LengthHeaderBuffer.ReadAll(LengthData); - a_CompressedData.reserve(LengthData.size() + CompressedSize); - a_CompressedData.assign(LengthData.data(), LengthData.size()); - a_CompressedData.append(CompressedData, CompressedSize); - - return true; + a_CompressedData.reserve(LengthData.size() + Compressed.size()); + a_CompressedData = LengthData; + a_CompressedData += Compressed; } @@ -1947,7 +1927,7 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat { if (a_Buffer.GetReadableSpace() > 0) { - AString AllData; + ContiguousByteBuffer AllData; size_t OldReadableSpace = a_Buffer.GetReadableSpace(); a_Buffer.ReadAll(AllData); a_Buffer.ResetRead(); @@ -1992,12 +1972,11 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat } // Check packet for compression: - UInt32 UncompressedSize = 0; - AString UncompressedData; if (m_State == 3) { UInt32 NumBytesRead = static_cast(a_Buffer.GetReadableSpace()); + UInt32 UncompressedSize; if (!a_Buffer.ReadVarInt(UncompressedSize)) { m_Client->Kick("Compression packet incomplete"); @@ -2011,113 +1990,35 @@ void cProtocol_1_8_0::AddReceivedData(cByteBuffer & a_Buffer, const char * a_Dat if (UncompressedSize > 0) { // Decompress the data: - AString CompressedData; - VERIFY(a_Buffer.ReadString(CompressedData, PacketLen)); - if (InflateString(CompressedData.data(), PacketLen, UncompressedData) != Z_OK) - { - m_Client->Kick("Compression failure"); - return; - } - PacketLen = static_cast(UncompressedData.size()); - if (PacketLen != UncompressedSize) - { - m_Client->Kick("Wrong uncompressed packet size given"); - return; - } - } - } - - // Move the packet payload to a separate cByteBuffer, bb: - cByteBuffer bb(PacketLen + 1); - if (UncompressedSize == 0) - { - // No compression was used, move directly - VERIFY(a_Buffer.ReadToByteBuffer(bb, static_cast(PacketLen))); - } - else - { - // Compression was used, move the uncompressed data: - VERIFY(bb.Write(UncompressedData.data(), UncompressedData.size())); - } - a_Buffer.CommitRead(); + m_Extractor.ReadFrom(a_Buffer, PacketLen); + a_Buffer.CommitRead(); - UInt32 PacketType; - if (!bb.ReadVarInt(PacketType)) - { - // Not enough data - break; - } - - // Write one NUL extra, so that we can detect over-reads - bb.Write("\0", 1); + const auto UncompressedData = m_Extractor.Extract(UncompressedSize); + const auto Uncompressed = UncompressedData.GetView(); + cByteBuffer bb(Uncompressed.size()); - // Log the packet info into the comm log file: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - AString PacketData; - bb.ReadAll(PacketData); - bb.ResetRead(); - bb.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again - ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read - PacketData.resize(PacketData.size() - 1); - AString PacketDataHex; - CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16); - m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n", - PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str() - ); - } + // Compression was used, move the uncompressed data: + VERIFY(bb.Write(Uncompressed.data(), Uncompressed.size())); - if (!HandlePacket(bb, PacketType)) - { - // Unknown packet, already been reported, but without the length. Log the length here: - LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen); - - #ifdef _DEBUG - // Dump the packet contents into the log: - bb.ResetRead(); - AString Packet; - bb.ReadAll(Packet); - Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection - AString Out; - CreateHexDump(Out, Packet.data(), Packet.size(), 24); - LOGD("Packet contents:\n%s", Out.c_str()); - #endif // _DEBUG - - // Put a message in the comm log: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n"); + HandlePacket(bb); + continue; } - - return; } - // The packet should have 1 byte left in the buffer - the NUL we had added - if (bb.GetReadableSpace() != 1) - { - // Read more or less than packet length, report as error - LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read %zu bytes, packet contained %u bytes", - PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen - ); + // Move the packet payload to a separate cByteBuffer, bb: + cByteBuffer bb(PacketLen); - // Put a message in the comm log: - if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) - { - m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %zu left) ^^^^^^\n\n\n", - 1, bb.GetReadableSpace() - ); - m_CommLogFile.Flush(); - } + // No compression was used, move directly: + VERIFY(a_Buffer.ReadToByteBuffer(bb, static_cast(PacketLen))); + a_Buffer.CommitRead(); - ASSERT(!"Read wrong number of bytes!"); - m_Client->PacketError(PacketType); - } + HandlePacket(bb); } // for (ever) // Log any leftover bytes into the logfile: if (g_ShouldLogCommIn && (a_Buffer.GetReadableSpace() > 0) && m_CommLogFile.IsOpen()) { - AString AllData; + ContiguousByteBuffer AllData; size_t OldReadableSpace = a_Buffer.GetReadableSpace(); a_Buffer.ReadAll(AllData); a_Buffer.ResetRead(); @@ -2367,8 +2268,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu { return; } - AString EncKey; - if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength)) + ContiguousByteBuffer EncKey; + if (!a_ByteBuffer.ReadSome(EncKey, EncKeyLength)) { return; } @@ -2376,8 +2277,8 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu { return; } - AString EncNonce; - if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength)) + ContiguousByteBuffer EncNonce; + if (!a_ByteBuffer.ReadSome(EncNonce, EncNonceLength)) { return; } @@ -2391,7 +2292,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu // Decrypt EncNonce using privkey cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); UInt32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; - int res = rsaDecryptor.Decrypt(reinterpret_cast(EncNonce.data()), EncNonce.size(), reinterpret_cast(DecryptedNonce), sizeof(DecryptedNonce)); + int res = rsaDecryptor.Decrypt(EncNonce, reinterpret_cast(DecryptedNonce), sizeof(DecryptedNonce)); if (res != 4) { LOGD("Bad nonce length: got %d, exp %d", res, 4); @@ -2407,7 +2308,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu // Decrypt the symmetric encryption key using privkey: Byte DecryptedKey[MAX_ENC_LEN]; - res = rsaDecryptor.Decrypt(reinterpret_cast(EncKey.data()), EncKey.size(), DecryptedKey, sizeof(DecryptedKey)); + res = rsaDecryptor.Decrypt(EncKey, DecryptedKey, sizeof(DecryptedKey)); if (res != 16) { LOGD("Bad key length"); @@ -2416,7 +2317,7 @@ void cProtocol_1_8_0::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBu } StartEncryption(DecryptedKey); - m_Client->HandleLogin(m_Client->GetUsername()); + m_Client->HandleLogin(); } @@ -2438,22 +2339,22 @@ void cProtocol_1_8_0::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer) return; } - cServer * Server = cRoot::Get()->GetServer(); + m_Client->SetUsername(std::move(Username)); + // If auth is required, then send the encryption request: - if (Server->ShouldAuthenticate()) + if (const auto Server = cRoot::Get()->GetServer(); Server->ShouldAuthenticate()) { cPacketizer Pkt(*this, pktEncryptionRequest); Pkt.WriteString(Server->GetServerID()); - const AString & PubKeyDer = Server->GetPublicKeyDER(); + const auto PubKeyDer = Server->GetPublicKeyDER(); Pkt.WriteVarInt32(static_cast(PubKeyDer.size())); - Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size()); + Pkt.WriteBuf(PubKeyDer); Pkt.WriteVarInt32(4); Pkt.WriteBEInt32(static_cast(reinterpret_cast(this))); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) - m_Client->SetUsername(Username); return; } - m_Client->HandleLogin(Username); + m_Client->HandleLogin(); } @@ -2704,20 +2605,20 @@ void cProtocol_1_8_0::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer) HandleVanillaPluginMessage(a_ByteBuffer, Channel); // Skip any unread data (vanilla sometimes sends garbage at the end of a packet; #1692): - if (a_ByteBuffer.GetReadableSpace() > 1) + if (a_ByteBuffer.GetReadableSpace() > 0) { LOGD("Protocol 1.8: Skipping garbage data at the end of a vanilla PluginMessage packet, %u bytes", - static_cast(a_ByteBuffer.GetReadableSpace() - 1) + static_cast(a_ByteBuffer.GetReadableSpace()) ); - a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace() - 1); + a_ByteBuffer.SkipRead(a_ByteBuffer.GetReadableSpace()); } return; } // Read the plugin message and relay to clienthandle: - AString Data; - VERIFY(a_ByteBuffer.ReadString(Data, a_ByteBuffer.GetReadableSpace() - 1)); // Always succeeds + ContiguousByteBuffer Data; + VERIFY(a_ByteBuffer.ReadSome(Data, a_ByteBuffer.GetReadableSpace())); // Always succeeds m_Client->HandlePluginMessage(Channel, Data); } @@ -2976,7 +2877,7 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con HANDLE_READ(a_ByteBuffer, ReadVarUTF8String, AString, Brand); m_Client->SetClientBrand(Brand); // Send back our brand, including the length: - SendPluginMessage("MC|Brand", "\x08""Cuberite"); + m_Client->SendPluginMessage("MC|Brand", "\x08""Cuberite"); return; } else if (a_Channel == "MC|Beacon") @@ -3001,8 +2902,8 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con LOG("Unhandled vanilla plugin channel: \"%s\".", a_Channel.c_str()); // Read the payload and send it through to the clienthandle: - AString Message; - VERIFY(a_ByteBuffer.ReadString(Message, a_ByteBuffer.GetReadableSpace() - 1)); + ContiguousByteBuffer Message; + VERIFY(a_ByteBuffer.ReadSome(Message, a_ByteBuffer.GetReadableSpace() - 1)); m_Client->HandlePluginMessage(a_Channel, Message); } @@ -3010,23 +2911,24 @@ void cProtocol_1_8_0::HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, con -void cProtocol_1_8_0::SendData(const char * a_Data, size_t a_Size) +void cProtocol_1_8_0::SendData(ContiguousByteBufferView a_Data) { if (m_IsEncrypted) { - Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks) - while (a_Size > 0) + std::byte Encrypted[8 KiB]; // Larger buffer, we may be sending lots of data (chunks) + + while (a_Data.size() > 0) { - size_t NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size; - m_Encryptor.ProcessData(Encrypted, reinterpret_cast(a_Data), NumBytes); - m_Client->SendData(reinterpret_cast(Encrypted), NumBytes); - a_Size -= NumBytes; - a_Data += NumBytes; + const auto NumBytes = (a_Data.size() > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Data.size(); + m_Encryptor.ProcessData(Encrypted, a_Data.data(), NumBytes); + m_Client->SendData({ Encrypted, NumBytes }); + + a_Data = a_Data.substr(NumBytes); } } else { - m_Client->SendData(a_Data, a_Size); + m_Client->SendData(a_Data); } } @@ -3054,8 +2956,8 @@ bool cProtocol_1_8_0::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_ a_Item.Empty(); } - AString Metadata; - if (!a_ByteBuffer.ReadString(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes - 1) || (Metadata.size() == 0) || (Metadata[0] == 0)) + ContiguousByteBuffer Metadata; + if (!a_ByteBuffer.ReadSome(Metadata, a_ByteBuffer.GetReadableSpace() - a_KeepRemainingBytes) || Metadata.empty() || (Metadata[0] == std::byte(0))) { // No metadata return true; @@ -3069,10 +2971,10 @@ bool cProtocol_1_8_0::ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_ -void cProtocol_1_8_0::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +void cProtocol_1_8_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) { // Parse into NBT: - cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); + cParsedNBT NBT(a_Metadata); if (!NBT.IsValid()) { AString HexDump; @@ -3189,33 +3091,34 @@ eBlockFace cProtocol_1_8_0::FaceIntToBlockFace(const Int32 a_BlockFace) void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt) { - UInt32 PacketLen = static_cast(m_OutPacketBuffer.GetUsedSpace()); - AString PacketData, CompressedPacket; - m_OutPacketBuffer.ReadAll(PacketData); + ASSERT(m_OutPacketBuffer.GetReadableSpace() == m_OutPacketBuffer.GetUsedSpace()); + + m_Compressor.ReadFrom(m_OutPacketBuffer); m_OutPacketBuffer.CommitRead(); + const auto PacketData = m_Compressor.GetView(); + if (m_State == 3) { + ContiguousByteBuffer CompressedPacket; + // Compress the packet payload: - if (!cProtocol_1_8_0::CompressPacket(PacketData, CompressedPacket)) - { - return; - } + cProtocol_1_8_0::CompressPacket(m_Compressor, CompressedPacket); // Send the packet's payload compressed: - SendData(CompressedPacket.data(), CompressedPacket.size()); + SendData(CompressedPacket); } else { // Compression doesn't apply to this state, send raw data: - m_OutPacketLenBuffer.WriteVarInt32(PacketLen); - AString LengthData; + m_OutPacketLenBuffer.WriteVarInt32(static_cast(PacketData.size())); + ContiguousByteBuffer LengthData; m_OutPacketLenBuffer.ReadAll(LengthData); - SendData(LengthData.data(), LengthData.size()); + m_OutPacketLenBuffer.CommitRead(); + SendData(LengthData); // Send the packet's payload directly: - m_OutPacketLenBuffer.CommitRead(); - SendData(PacketData.data(), PacketData.size()); + SendData(PacketData); } // Log the comm into logfile: @@ -3226,7 +3129,7 @@ void cProtocol_1_8_0::SendPacket(cPacketizer & a_Pkt) CreateHexDump(Hex, PacketData.data(), PacketData.size(), 16); m_CommLogFile.Printf("Outgoing packet: type %s (translated to 0x%02x), length %u (0x%04x), state %d. Payload (incl. type):\n%s\n", cPacketizer::PacketTypeToStr(a_Pkt.GetPacketType()), GetPacketID(a_Pkt.GetPacketType()), - PacketLen, PacketLen, m_State, Hex + PacketData.size(), PacketData.size(), m_State, Hex ); /* // Useful for debugging a new protocol: @@ -3346,13 +3249,13 @@ void cProtocol_1_8_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) } Writer.Finish(); - AString Result = Writer.GetResult(); - if (Result.size() == 0) + const auto Result = Writer.GetResult(); + if (Result.empty()) { a_Pkt.WriteBEInt8(0); return; } - a_Pkt.WriteBuf(Result.data(), Result.size()); + a_Pkt.WriteBuf(Result); } @@ -3479,7 +3382,7 @@ void cProtocol_1_8_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } @@ -3978,6 +3881,82 @@ void cProtocol_1_8_0::WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & +void cProtocol_1_8_0::HandlePacket(cByteBuffer & a_Buffer) +{ + UInt32 PacketType; + if (!a_Buffer.ReadVarInt(PacketType)) + { + // Not enough data + return; + } + + // Log the packet info into the comm log file: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + ContiguousByteBuffer PacketData; + a_Buffer.ReadAll(PacketData); + a_Buffer.ResetRead(); + a_Buffer.ReadVarInt(PacketType); // We have already read the packet type once, it will be there again + ASSERT(PacketData.size() > 0); // We have written an extra NUL, so there had to be at least one byte read + PacketData.resize(PacketData.size() - 1); + AString PacketDataHex; + CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16); + m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n", + PacketType, PacketType, a_Buffer.GetUsedSpace(), a_Buffer.GetUsedSpace(), m_State, PacketDataHex.c_str() + ); + } + + if (!HandlePacket(a_Buffer, PacketType)) + { + // Unknown packet, already been reported, but without the length. Log the length here: + LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, a_Buffer.GetUsedSpace()); + +#ifdef _DEBUG + // Dump the packet contents into the log: + a_Buffer.ResetRead(); + ContiguousByteBuffer Packet; + a_Buffer.ReadAll(Packet); + Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection + AString Out; + CreateHexDump(Out, Packet.data(), Packet.size(), 24); + LOGD("Packet contents:\n%s", Out.c_str()); +#endif // _DEBUG + + // Put a message in the comm log: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n"); + } + + return; + } + + // The packet should have nothing left in the buffer: + if (a_Buffer.GetReadableSpace() != 0) + { + // Read more or less than packet length, report as error + LOGWARNING("Protocol 1.8: Wrong number of bytes read for packet 0x%x, state %d. Read %zu bytes, packet contained %u bytes", + PacketType, m_State, a_Buffer.GetUsedSpace() - a_Buffer.GetReadableSpace(), a_Buffer.GetUsedSpace() + ); + + // Put a message in the comm log: + if (g_ShouldLogCommIn && m_CommLogFile.IsOpen()) + { + m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %zu left) ^^^^^^\n\n\n", + 1, a_Buffer.GetReadableSpace() + ); + m_CommLogFile.Flush(); + } + + ASSERT(!"Read wrong number of bytes!"); + m_Client->PacketError(PacketType); + } +} + + + + + void cProtocol_1_8_0::SendEntityTeleport(const cEntity & a_Entity) { cPacketizer Pkt(*this, pktTeleportEntity); diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h index 44415758f..f5800cb21 100644 --- a/src/Protocol/Protocol_1_8.h +++ b/src/Protocol/Protocol_1_8.h @@ -20,6 +20,9 @@ Declares the 1.8 protocol classes: #include "../mbedTLS++/AesCfb128Decryptor.h" #include "../mbedTLS++/AesCfb128Encryptor.h" +#include "CircularBufferCompressor.h" +#include "StringCompression.h" + @@ -46,7 +49,7 @@ public: virtual void SendChat (const AString & a_Message, eChatType a_Type) override; virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override; virtual void SendChatRaw (const AString & a_MessageRaw, eChatType a_Type) override; - virtual void SendChunkData (const std::string_view a_ChunkData) override; + virtual void SendChunkData (ContiguousByteBufferView a_ChunkData) override; virtual void SendCollectEntity (const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle) override; @@ -88,7 +91,7 @@ public: virtual void SendPlayerMoveLook (void) override; virtual void SendPlayerPosition (void) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; + virtual void SendPluginMessage (const AString & a_Channel, ContiguousByteBufferView a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; virtual void SendResetTitle (void) override; virtual void SendResourcePack (const AString & a_ResourcePackUrl) override; @@ -125,9 +128,8 @@ public: virtual AString GetAuthServerID(void) override { return m_AuthServerID; } /** Compress the packet. a_Packet must be without packet length. - a_Compressed will be set to the compressed packet includes packet length and data length. - If compression fails, the function returns false. */ - static bool CompressPacket(const AString & a_Packet, AString & a_Compressed); + a_Compressed will be set to the compressed packet includes packet length and data length. */ + static void CompressPacket(CircularBufferCompressor & a_Packet, ContiguousByteBuffer & a_Compressed); /** The 1.8 protocol use a particle id instead of a string. This function converts the name to the id. If the name is incorrect, it returns 0. */ static int GetParticleID(const AString & a_ParticleName); @@ -194,7 +196,7 @@ protected: virtual void HandleVanillaPluginMessage(cByteBuffer & a_ByteBuffer, const AString & a_Channel); /** Sends the data to the client, encrypting them if needed. */ - virtual void SendData(const char * a_Data, size_t a_Size) override; + virtual void SendData(ContiguousByteBufferView a_Size) override; /** Sends the packet to the client. Called by the cPacketizer's destructor. */ virtual void SendPacket(cPacketizer & a_Packet) override; @@ -205,7 +207,7 @@ protected: virtual bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item, size_t a_KeepRemainingBytes = 0); /** Parses item metadata as read by ReadItem(), into the item enchantments. */ - virtual void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); + virtual void ParseItemMetadata(cItem & a_Item, ContiguousByteBufferView a_Metadata); virtual void StartEncryption(const Byte * a_Key); @@ -242,9 +244,15 @@ private: cAesCfb128Decryptor m_Decryptor; cAesCfb128Encryptor m_Encryptor; + CircularBufferCompressor m_Compressor; + CircularBufferExtractor m_Extractor; + /** The logfile where the comm is logged, when g_ShouldLogComm is true */ cFile m_CommLogFile; + /** Handle a complete packet stored in the given buffer. */ + void HandlePacket(cByteBuffer & a_Buffer); + /** Sends an entity teleport packet. Mitigates a 1.8 bug where the position in the entity spawn packet is ignored, and so entities don't show up until a teleport is sent. */ diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index 72d3fd4b1..67e920925 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -1031,10 +1031,10 @@ void cProtocol_1_9_0::HandlePacketWindowClick(cByteBuffer & a_ByteBuffer) -void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) +void cProtocol_1_9_0::ParseItemMetadata(cItem & a_Item, const ContiguousByteBufferView a_Metadata) { // Parse into NBT: - cParsedNBT NBT(a_Metadata.data(), a_Metadata.size()); + cParsedNBT NBT(a_Metadata); if (!NBT.IsValid()) { AString HexDump; @@ -1438,13 +1438,13 @@ void cProtocol_1_9_0::WriteItem(cPacketizer & a_Pkt, const cItem & a_Item) Writer.Finish(); - AString Result = Writer.GetResult(); - if (Result.size() == 0) + const auto Result = Writer.GetResult(); + if (Result.empty()) { a_Pkt.WriteBEInt8(0); return; } - a_Pkt.WriteBuf(Result.data(), Result.size()); + a_Pkt.WriteBuf(Result); } @@ -1550,7 +1550,7 @@ void cProtocol_1_9_0::WriteBlockEntity(cPacketizer & a_Pkt, const cBlockEntity & } Writer.Finish(); - a_Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + a_Pkt.WriteBuf(Writer.GetResult()); } @@ -2289,7 +2289,7 @@ void cProtocol_1_9_4::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, c Writer.AddString("Text4", JsonUtils::WriteFastString(Line4)); Writer.Finish(); - Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size()); + Pkt.WriteBuf(Writer.GetResult()); } diff --git a/src/Protocol/Protocol_1_9.h b/src/Protocol/Protocol_1_9.h index 0339ddeb3..27b005bd4 100644 --- a/src/Protocol/Protocol_1_9.h +++ b/src/Protocol/Protocol_1_9.h @@ -98,7 +98,7 @@ protected: virtual void HandlePacketWindowClick (cByteBuffer & a_ByteBuffer) override; /** Parses item metadata as read by ReadItem(), into the item enchantments. */ - virtual void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) override; + virtual void ParseItemMetadata(cItem & a_Item, ContiguousByteBufferView a_Metadata) override; /** Converts the hand parameter received by the protocol into eHand constants. If the received value doesn't match any of the know value, raise an assertion fail or return hMain. */ -- cgit v1.2.3