From b2214a56c4311b7ace927280a2c059816d5d63a8 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 6 Jul 2013 19:45:36 +0000 Subject: ProtoProxy: moved into the Tools folder git-svn-id: http://mc-server.googlecode.com/svn/trunk@1657 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- Tools/ProtoProxy/Connection.cpp | 2448 ++++++++++++++++++++++++++++++++++++ Tools/ProtoProxy/Connection.h | 200 +++ Tools/ProtoProxy/Globals.cpp | 10 + Tools/ProtoProxy/Globals.h | 221 ++++ Tools/ProtoProxy/ProtoProxy.cpp | 32 + Tools/ProtoProxy/ProtoProxy.sln | 29 + Tools/ProtoProxy/ProtoProxy.txt | 31 + Tools/ProtoProxy/ProtoProxy.vcproj | 267 ++++ Tools/ProtoProxy/Server.cpp | 82 ++ Tools/ProtoProxy/Server.h | 38 + 10 files changed, 3358 insertions(+) create mode 100644 Tools/ProtoProxy/Connection.cpp create mode 100644 Tools/ProtoProxy/Connection.h create mode 100644 Tools/ProtoProxy/Globals.cpp create mode 100644 Tools/ProtoProxy/Globals.h create mode 100644 Tools/ProtoProxy/ProtoProxy.cpp create mode 100644 Tools/ProtoProxy/ProtoProxy.sln create mode 100644 Tools/ProtoProxy/ProtoProxy.txt create mode 100644 Tools/ProtoProxy/ProtoProxy.vcproj create mode 100644 Tools/ProtoProxy/Server.cpp create mode 100644 Tools/ProtoProxy/Server.h (limited to 'Tools/ProtoProxy') diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp new file mode 100644 index 000000000..f23eecbe8 --- /dev/null +++ b/Tools/ProtoProxy/Connection.cpp @@ -0,0 +1,2448 @@ + +// Connection.cpp + +// Interfaces to the cConnection class representing a single pair of connected sockets + +#include "Globals.h" +#include "Connection.h" +#include "Server.h" +#include + + + + + +#define HANDLE_CLIENT_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ClientBuffer.Proc(Var)) \ + { \ + return false; \ + } \ + } + +#define HANDLE_SERVER_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ServerBuffer.Proc(Var)) \ + { \ + return false; \ + } \ + } + +#define CLIENTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client") +#define SERVERSEND(...) SendData(m_ServerSocket, __VA_ARGS__, "Server") +#define CLIENTENCRYPTSEND(...) SendEncryptedData(m_ClientSocket, m_ClientEncryptor, __VA_ARGS__, "Client") +#define SERVERENCRYPTSEND(...) SendEncryptedData(m_ServerSocket, m_ServerEncryptor, __VA_ARGS__, "Server") + +#define COPY_TO_SERVER() \ + { \ + AString ToServer; \ + m_ClientBuffer.ReadAgain(ToServer); \ + if (m_ServerState == csUnencrypted) \ + { \ + SERVERSEND(ToServer.data(), ToServer.size()); \ + } \ + else \ + { \ + SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ + } \ + } + +#define COPY_TO_CLIENT() \ + { \ + AString ToClient; \ + m_ServerBuffer.ReadAgain(ToClient); \ + if (m_ClientState == csUnencrypted) \ + { \ + CLIENTSEND(ToClient.data(), ToClient.size()); \ + } \ + else \ + { \ + CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ + } \ + } + +#define HANDLE_CLIENT_READ(Proc) \ + { \ + if (!Proc()) \ + { \ + AString Leftover; \ + m_ClientBuffer.ReadAgain(Leftover); \ + DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \ + m_ClientBuffer.ResetRead(); \ + return true; \ + } \ + } + +#define HANDLE_SERVER_READ(Proc) \ + { \ + if (!Proc()) \ + { \ + m_ServerBuffer.ResetRead(); \ + return true; \ + } \ + } + + +#define MAX_ENC_LEN 1024 + + + + +typedef unsigned char Byte; + + + + + +enum +{ + PACKET_KEEPALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT_MESSAGE = 0x03, + PACKET_TIME_UPDATE = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POSITION = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_POSITION_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECT = 0x10, + PACKET_PLAYER_ANIMATION = 0x12, + PACKET_ENTITY_ACTION = 0x13, + PACKET_SPAWN_NAMED_ENTITY = 0x14, + PACKET_SPAWN_PICKUP = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_OBJECT_VEHICLE = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_SPAWN_PAINTING = 0x19, + PACKET_SPAWN_EXPERIENCE_ORB = 0x1a, + PACKET_ENTITY_VELOCITY = 0x1c, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENTITY_RELATIVE_MOVE = 0x1f, + PACKET_ENTITY_LOOK = 0x20, + PACKET_ENTITY_RELATIVE_MOVE_LOOK = 0x21, + PACKET_ENTITY_TELEPORT = 0x22, + PACKET_ENTITY_HEAD_LOOK = 0x23, + PACKET_ENTITY_STATUS = 0x26, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_ENTITY_METADATA = 0x28, + PACKET_ENTITY_EFFECT = 0x29, + PACKET_ENTITY_EFFECT_REMOVE = 0x2a, + PACKET_SET_EXPERIENCE = 0x2b, + PACKET_ENTITY_PROPERTIES = 0x2c, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK_CHANGE = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_MAP_CHUNK_BULK = 0x38, + PACKET_SOUND_EFFECT = 0x3d, + PACKET_NAMED_SOUND_EFFECT = 0x3e, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_SET_SLOT = 0x67, + PACKET_WINDOW_CONTENTS = 0x68, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6b, + PACKET_UPDATE_SIGN = 0x82, + PACKET_UPDATE_TILE_ENTITY = 0x84, + PACKET_PLAYER_LIST_ITEM = 0xc9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_INCREMENT_STATISTIC = 0xc8, + PACKET_LOCALE_AND_VIEW = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_ENCRYPTION_KEY_RESPONSE = 0xfc, + PACKET_ENCRYPTION_KEY_REQUEST = 0xfd, + PACKET_PING = 0xfe, + PACKET_KICK = 0xff, +} ; + + + + +enum +{ + OBJECT_BOAT = 1, + OBJECT_MINECART = 10, + OBJECT_MINECART_STORAGE = 11, + OBJECT_MINECART_POWERED = 12, + OBJECT_TNT = 50, + OBJECT_ENDERCRYSTAL = 51, + OBJECT_ARROW = 60, + OBJECT_SNOWBALL = 61, + OBJECT_EGG = 62, + OBJECT_FALLING_BLOCK = 70, + OBJECT_EYE_OF_ENDER = 72, + OBJECT_DRAGON_EGG = 74, + OBJECT_FISHING_FLOAT = 90, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cConnection: + +cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : + m_ItemIdx(0), + m_LogFile(NULL), + m_Server(a_Server), + m_ClientSocket(a_ClientSocket), + m_ServerSocket(-1), + m_BeginTick(clock()), + m_ClientState(csUnencrypted), + m_ServerState(csUnencrypted), + m_Nonce(0), + m_ClientBuffer(1024 KiB), + m_ServerBuffer(1024 KiB), + m_HasClientPinged(false) +{ + Printf(m_LogNameBase, "Log_%d", (int)time(NULL)); + AString fnam(m_LogNameBase); + fnam.append(".log"); + m_LogFile = fopen(fnam.c_str(), "w"); + Log("Log file created"); +} + + + + + +cConnection::~cConnection() +{ + fclose(m_LogFile); +} + + + + + +void cConnection::Run(void) +{ + if (!ConnectToServer()) + { + Log("Cannot connect to server; aborting"); + return; + } + + while (true) + { + fd_set ReadFDs; + FD_ZERO(&ReadFDs); + FD_SET(m_ServerSocket, &ReadFDs); + FD_SET(m_ClientSocket, &ReadFDs); + int res = select(2, &ReadFDs, NULL, NULL, NULL); + if (res <= 0) + { + printf("select() failed: %d; aborting client", WSAGetLastError()); + break; + } + if (FD_ISSET(m_ServerSocket, &ReadFDs)) + { + if (!RelayFromServer()) + { + break; + } + } + if (FD_ISSET(m_ClientSocket, &ReadFDs)) + { + if (!RelayFromClient()) + { + break; + } + } + } + Log("Relaying ended, closing sockets"); + closesocket(m_ServerSocket); + closesocket(m_ClientSocket); +} + + + + + +void cConnection::Log(const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + Printf(FullMsg, "[%5.3f] %s\n", GetRelativeTime(), msg.c_str()); + + // Log to file: + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); + + // Log to screen: + // std::cout << FullMsg; +} + + + + + +void cConnection::DataLog(const void * a_Data, int a_Size, const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + AString Hex; + Printf(FullMsg, "[%5.3f] %s\n%s\n", GetRelativeTime(), msg.c_str(), CreateHexDump(Hex, a_Data, a_Size, 16).c_str()); + + // Log to file: + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); + + /* + // Log to screen: + std::cout << FullMsg; + //*/ +} + + + + + +void cConnection::LogFlush(void) +{ + fflush(m_LogFile); +} + + + + + +bool cConnection::ConnectToServer(void) +{ + m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_ServerSocket == INVALID_SOCKET) + { + return false; + } + sockaddr_in localhost; + localhost.sin_family = AF_INET; + localhost.sin_port = htons(m_Server.GetConnectPort()); + localhost.sin_addr.s_addr = htonl(0x7f000001); // localhost + if (connect(m_ServerSocket, (sockaddr *)&localhost, sizeof(localhost)) != 0) + { + printf("connection to server failed: %d\n", WSAGetLastError()); + return false; + } + Log("Connected to SERVER"); + return true; +} + + + + + +bool cConnection::RelayFromServer(void) +{ + char Buffer[64 KiB]; + int res = recv(m_ServerSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Server closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the SERVER", res); + + switch (m_ServerState) + { + case csUnencrypted: + { + return DecodeServersPackets(Buffer, res); + } + case csEncryptedUnderstood: + { + m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); + return DecodeServersPackets(Buffer, res); + } + case csEncryptedUnknown: + { + m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); + m_ClientEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + return CLIENTSEND(Buffer, res); + } + } + + return true; +} + + + + + +bool cConnection::RelayFromClient(void) +{ + char Buffer[64 KiB]; + int res = recv(m_ClientSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Client closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the CLIENT", res); + + switch (m_ClientState) + { + case csUnencrypted: + { + return DecodeClientsPackets(Buffer, res); + } + case csEncryptedUnderstood: + { + m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); + return DecodeClientsPackets(Buffer, res); + } + case csEncryptedUnknown: + { + m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); + m_ServerEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + return SERVERSEND(Buffer, res); + } + } + + return true; +} + + + + + +double cConnection::GetRelativeTime(void) +{ + return (double)(clock() - m_BeginTick) / CLOCKS_PER_SEC; + +} + + + + + +bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer) +{ + DataLog(a_Data, a_Size, "Sending data to %s", a_Peer); + + int res = send(a_Socket, a_Data, a_Size, 0); + if (res <= 0) + { + Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, WSAGetLastError()); + return false; + } + return true; +} + + + + + +bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer) +{ + AString All; + a_Data.ReadAll(All); + a_Data.CommitRead(); + return SendData(a_Socket, All.data(), All.size(), a_Peer); +} + + + + + +bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer) +{ + DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer); + const byte * Data = (const byte *)a_Data; + while (a_Size > 0) + { + byte Buffer[64 KiB]; + int NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size; + a_Encryptor.ProcessData(Buffer, Data, NumBytes); + bool res = SendData(a_Socket, (const char *)Buffer, NumBytes, a_Peer); + if (!res) + { + return false; + } + Data += NumBytes; + a_Size -= NumBytes; + } + return true; +} + + + + + +bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) +{ + AString All; + a_Data.ReadAll(All); + a_Data.CommitRead(); + return SendEncryptedData(a_Socket, a_Encryptor, All.data(), All.size(), a_Peer); +} + + + + + +bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size) +{ + if (!m_ClientBuffer.Write(a_Data, a_Size)) + { + Log("Too much queued data for the server, aborting connection"); + return false; + } + + while (m_ClientBuffer.CanReadBytes(1)) + { + Log("Decoding client's packets, there are now %d bytes in the queue", m_ClientBuffer.GetReadableSpace()); + unsigned char PacketType; + m_ClientBuffer.ReadByte(PacketType); + switch (PacketType) + { + case PACKET_BLOCK_DIG: HANDLE_CLIENT_READ(HandleClientBlockDig); break; + case PACKET_BLOCK_PLACE: HANDLE_CLIENT_READ(HandleClientBlockPlace); break; + case PACKET_CHAT_MESSAGE: HANDLE_CLIENT_READ(HandleClientChatMessage); break; + case PACKET_CLIENT_STATUSES: HANDLE_CLIENT_READ(HandleClientClientStatuses); break; + case PACKET_CREATIVE_INVENTORY_ACTION: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction); break; + case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_CLIENT_READ(HandleClientEncryptionKeyResponse); break; + case PACKET_ENTITY_ACTION: HANDLE_CLIENT_READ(HandleClientEntityAction); break; + case PACKET_HANDSHAKE: HANDLE_CLIENT_READ(HandleClientHandshake); break; + case PACKET_KEEPALIVE: HANDLE_CLIENT_READ(HandleClientKeepAlive); break; + case PACKET_LOCALE_AND_VIEW: HANDLE_CLIENT_READ(HandleClientLocaleAndView); break; + case PACKET_PING: HANDLE_CLIENT_READ(HandleClientPing); break; + case PACKET_PLAYER_ABILITIES: HANDLE_CLIENT_READ(HandleClientPlayerAbilities); break; + case PACKET_PLAYER_ANIMATION: HANDLE_CLIENT_READ(HandleClientAnimation); break; + case PACKET_PLAYER_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerLook); break; + case PACKET_PLAYER_ON_GROUND: HANDLE_CLIENT_READ(HandleClientPlayerOnGround); break; + case PACKET_PLAYER_POSITION: HANDLE_CLIENT_READ(HandleClientPlayerPosition); break; + case PACKET_PLAYER_POSITION_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook); break; + case PACKET_SLOT_SELECT: HANDLE_CLIENT_READ(HandleClientSlotSelect); break; + case PACKET_UPDATE_SIGN: HANDLE_CLIENT_READ(HandleClientUpdateSign); break; + case PACKET_USE_ENTITY: HANDLE_CLIENT_READ(HandleClientUseEntity); break; + case PACKET_WINDOW_CLICK: HANDLE_CLIENT_READ(HandleClientWindowClick); break; + case PACKET_WINDOW_CLOSE: HANDLE_CLIENT_READ(HandleClientWindowClose); break; + default: + { + if (m_ClientState == csEncryptedUnderstood) + { + Log("****************** Unknown packet 0x%02x from the client while encrypted; continuing to relay blind only", PacketType); + AString Data; + m_ClientBuffer.ResetRead(); + m_ClientBuffer.ReadAll(Data); + DataLog(Data.data(), Data.size(), "Current data in the client packet queue: %d bytes", Data.size()); + m_ClientState = csEncryptedUnknown; + m_ClientBuffer.ResetRead(); + if (m_ServerState == csUnencrypted) + { + SERVERSEND(m_ClientBuffer); + } + else + { + SERVERENCRYPTSEND(m_ClientBuffer); + } + return true; + } + else + { + Log("Unknown packet 0x%02x from the client while unencrypted; aborting connection", PacketType); + return false; + } + } + } // switch (PacketType) + m_ClientBuffer.CommitRead(); + } // while (CanReadBytes(1)) + return true; +} + + + + + +bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size) +{ + if (!m_ServerBuffer.Write(a_Data, a_Size)) + { + Log("Too much queued data for the client, aborting connection"); + return false; + } + + if ( + (m_ServerState == csEncryptedUnderstood) && + (m_ClientState == csUnencrypted) + ) + { + // Client hasn't finished encryption handshake yet, don't send them any data yet + } + + while (m_ServerBuffer.CanReadBytes(1)) + { + unsigned char PacketType; + m_ServerBuffer.ReadByte(PacketType); + Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%x", m_ServerBuffer.GetReadableSpace(), PacketType); + LogFlush(); + switch (PacketType) + { + case PACKET_ATTACH_ENTITY: HANDLE_SERVER_READ(HandleServerAttachEntity); break; + case PACKET_BLOCK_ACTION: HANDLE_SERVER_READ(HandleServerBlockAction); break; + case PACKET_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerBlockChange); break; + case PACKET_CHANGE_GAME_STATE: HANDLE_SERVER_READ(HandleServerChangeGameState); break; + case PACKET_CHAT_MESSAGE: HANDLE_SERVER_READ(HandleServerChatMessage); break; + case PACKET_COLLECT_PICKUP: HANDLE_SERVER_READ(HandleServerCollectPickup); break; + case PACKET_COMPASS: HANDLE_SERVER_READ(HandleServerCompass); break; + case PACKET_DESTROY_ENTITIES: HANDLE_SERVER_READ(HandleServerDestroyEntities); break; + case PACKET_ENCRYPTION_KEY_REQUEST: HANDLE_SERVER_READ(HandleServerEncryptionKeyRequest); break; + case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_SERVER_READ(HandleServerEncryptionKeyResponse); break; + case PACKET_ENTITY: HANDLE_SERVER_READ(HandleServerEntity); break; + case PACKET_ENTITY_EQUIPMENT: HANDLE_SERVER_READ(HandleServerEntityEquipment); break; + case PACKET_ENTITY_HEAD_LOOK: HANDLE_SERVER_READ(HandleServerEntityHeadLook); break; + case PACKET_ENTITY_LOOK: HANDLE_SERVER_READ(HandleServerEntityLook); break; + case PACKET_ENTITY_METADATA: HANDLE_SERVER_READ(HandleServerEntityMetadata); break; + case PACKET_ENTITY_PROPERTIES: HANDLE_SERVER_READ(HandleServerEntityProperties); break; + case PACKET_ENTITY_RELATIVE_MOVE: HANDLE_SERVER_READ(HandleServerEntityRelativeMove); break; + case PACKET_ENTITY_RELATIVE_MOVE_LOOK: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook); break; + case PACKET_ENTITY_STATUS: HANDLE_SERVER_READ(HandleServerEntityStatus); break; + case PACKET_ENTITY_TELEPORT: HANDLE_SERVER_READ(HandleServerEntityTeleport); break; + case PACKET_ENTITY_VELOCITY: HANDLE_SERVER_READ(HandleServerEntityVelocity); break; + case PACKET_INCREMENT_STATISTIC: HANDLE_SERVER_READ(HandleServerIncrementStatistic); break; + case PACKET_KEEPALIVE: HANDLE_SERVER_READ(HandleServerKeepAlive); break; + case PACKET_KICK: HANDLE_SERVER_READ(HandleServerKick); break; + case PACKET_LOGIN: HANDLE_SERVER_READ(HandleServerLogin); break; + case PACKET_MAP_CHUNK: HANDLE_SERVER_READ(HandleServerMapChunk); break; + case PACKET_MAP_CHUNK_BULK: HANDLE_SERVER_READ(HandleServerMapChunkBulk); break; + case PACKET_MULTI_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerMultiBlockChange); break; + case PACKET_NAMED_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerNamedSoundEffect); break; + case PACKET_PLAYER_ABILITIES: HANDLE_SERVER_READ(HandleServerPlayerAbilities); break; + case PACKET_PLAYER_ANIMATION: HANDLE_SERVER_READ(HandleServerPlayerAnimation); break; + case PACKET_PLAYER_LIST_ITEM: HANDLE_SERVER_READ(HandleServerPlayerListItem); break; + case PACKET_PLAYER_POSITION_LOOK: HANDLE_SERVER_READ(HandleServerPlayerPositionLook); break; + case PACKET_SET_EXPERIENCE: HANDLE_SERVER_READ(HandleServerSetExperience); break; + case PACKET_SET_SLOT: HANDLE_SERVER_READ(HandleServerSetSlot); break; + case PACKET_SLOT_SELECT: HANDLE_SERVER_READ(HandleServerSlotSelect); break; + case PACKET_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerSoundEffect); break; + case PACKET_SPAWN_MOB: HANDLE_SERVER_READ(HandleServerSpawnMob); break; + case PACKET_SPAWN_NAMED_ENTITY: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity); break; + case PACKET_SPAWN_OBJECT_VEHICLE: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle); break; + case PACKET_SPAWN_PAINTING: HANDLE_SERVER_READ(HandleServerSpawnPainting); break; + case PACKET_SPAWN_PICKUP: HANDLE_SERVER_READ(HandleServerSpawnPickup); break; + case PACKET_TIME_UPDATE: HANDLE_SERVER_READ(HandleServerTimeUpdate); break; + case PACKET_UPDATE_HEALTH: HANDLE_SERVER_READ(HandleServerUpdateHealth); break; + case PACKET_UPDATE_SIGN: HANDLE_SERVER_READ(HandleServerUpdateSign); break; + case PACKET_UPDATE_TILE_ENTITY: HANDLE_SERVER_READ(HandleServerUpdateTileEntity); break; + case PACKET_WINDOW_CLOSE: HANDLE_SERVER_READ(HandleServerWindowClose); break; + case PACKET_WINDOW_CONTENTS: HANDLE_SERVER_READ(HandleServerWindowContents); break; + case PACKET_WINDOW_OPEN: HANDLE_SERVER_READ(HandleServerWindowOpen); break; + default: + { + if (m_ServerState == csEncryptedUnderstood) + { + Log("********************** Unknown packet 0x%02x from the server while encrypted; continuing to relay blind only", PacketType); + AString Data; + m_ServerBuffer.ResetRead(); + m_ServerBuffer.ReadAll(Data); + DataLog(Data.data(), Data.size(), "Current data in the server packet queue: %d bytes", Data.size()); + m_ServerState = csEncryptedUnknown; + m_ServerBuffer.ResetRead(); + if (m_ClientState == csUnencrypted) + { + CLIENTSEND(m_ServerBuffer); + } + else + { + CLIENTENCRYPTSEND(m_ServerBuffer); + } + return true; + } + else + { + Log("Unknown packet 0x%02x from the server while unencrypted; aborting connection", PacketType); + return false; + } + } + } // switch (PacketType) + m_ServerBuffer.CommitRead(); + } // while (CanReadBytes(1)) + return true; +} + + + + + +bool cConnection::HandleClientAnimation(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Animation); + Log("Received a PACKET_ANIMATION from the client:"); + Log(" EntityID: %d", EntityID); + Log(" Animation: %d", Animation); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientBlockDig(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, Status); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockFace); + Log("Received a PACKET_BLOCK_DIG from the client:"); + Log(" Status = %d", Status); + Log(" Pos = <%d, %d, %d>", BlockX, BlockY, BlockZ); + Log(" BlockFace = %d", BlockFace); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientBlockPlace(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Face); + AString Desc; + if (!ParseSlot(m_ClientBuffer, Desc)) + { + return false; + } + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorX); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorY); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorZ); + Log("Received a PACKET_BLOCK_PLACE from the client:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Face = %d", Face); + Log(" Item = %s", Desc.c_str()); + Log(" Cursor = <%d, %d, %d>", CursorX, CursorY, CursorZ); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientChatMessage(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Message); + Log("Received a PACKET_CHAT_MESSAGE from the client:"); + Log(" Message = \"%s\"", Message.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientClientStatuses(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Statuses); + Log("Received a PACKET_CLIENT_STATUSES from the CLIENT:"); + Log(" Statuses = %d", Statuses); + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientCreativeInventoryAction(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ClientBuffer, Item)) + { + return false; + } + Log("Received a PACKET_CREATIVE_INVENTORY_ACTION from the client:"); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientEncryptionKeyResponse(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ClientBuffer.ReadString(EncKey, EncKeyLength)) + { + return true; + } + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ClientBuffer.ReadString(EncNonce, EncNonceLength)) + { + return true; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + Log("Client: Too long encryption params"); + return true; + } + StartClientEncryption(EncKey, EncNonce); + return true; +} + + + + + +bool cConnection::HandleClientEntityAction(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ActionType); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, UnknownHorseVal); + Log("Received a PACKET_ENTITY_ACTION from the client:"); + Log(" PlayerID = %d", PlayerID); + Log(" ActionType = %d", ActionType); + Log(" UnknownHorseVal = %d (0x%08x)", UnknownHorseVal, UnknownHorseVal); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientHandshake(void) +{ + // Read the packet from the client: + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ServerPort); + m_ClientBuffer.CommitRead(); + + Log("Received a PACKET_HANDSHAKE from the client:"); + Log(" ProtocolVersion = %d", ProtocolVersion); + Log(" Username = \"%s\"", Username.c_str()); + Log(" ServerHost = \"%s\"", ServerHost.c_str()); + Log(" ServerPort = %d", ServerPort); + + // Send the same packet to the server, but with our port: + cByteBuffer ToServer(512); + ToServer.WriteByte (PACKET_HANDSHAKE); + ToServer.WriteByte (ProtocolVersion); + ToServer.WriteBEUTF16String16(Username); + ToServer.WriteBEUTF16String16(ServerHost); + ToServer.WriteBEInt (m_Server.GetConnectPort()); + SERVERSEND(ToServer); + + return true; +} + + + + + +bool cConnection::HandleClientKeepAlive(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ID); + Log("Received a PACKET_KEEPALIVE from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientLocaleAndView(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ShowCape); + Log("Received a PACKET_LOCALE_AND_VIEW from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPing(void) +{ + m_HasClientPinged = true; + Log("Received a PACKET_PING from the client"); + m_ClientBuffer.ResetRead(); + SERVERSEND(m_ClientBuffer); + return true; +} + + + + + +bool cConnection::HandleClientPlayerAbilities(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Flags); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + Log("Receives a PACKET_PLAYER_ABILITIES from the client:"); + Log(" Flags = %d (0x%02x)", Flags, Flags); + Log(" FlyingSpeed = %f", FlyingSpeed); + Log(" WalkingSpeed = %f", WalkingSpeed); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerLook(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); + Log("Received a PACKET_PLAYER_LOOK from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerOnGround(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); + Log("Received a PACKET_PLAYER_ON_GROUND from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerPosition(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION from the client"); + + // TODO: list packet contents + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerPositionLook(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION_LOOK from the client"); + Log(" Pos = {%.03f, %.03f, %.03f}", PosX, PosY, PosZ); + Log(" Stance = %.03f", Stance); + Log(" Y, P = %.03f, %.03f", Yaw, Pitch); + Log(" IsOnGround = %s", IsOnGround ? "true" : "false"); + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientSlotSelect(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + Log("Received a PACKET_SLOT_SELECT from the client"); + Log(" SlotNum = %d", SlotNum); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientUpdateSign(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line4); + Log("Received a PACKET_UPDATE_SIGN from the client:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientUseEntity(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, MouseButton); + Log("Received a PACKET_USE_ENTITY from the client:"); + Log(" PlayerID = %d", PlayerID); + Log(" EntityID = %d", EntityID); + Log(" MouseButton = %d", MouseButton); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientWindowClick(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsRightClick); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsShiftClick); + AString Item; + if (!ParseSlot(m_ClientBuffer, Item)) + { + return false; + } + Log("Received a PACKET_WINDOW_CLICK from the client"); + Log(" WindowID = %d", WindowID); + Log(" SlotNum = %d", SlotNum); + Log(" IsRclk = %d, IsShift = %d", IsRightClick, IsShiftClick); + Log(" TransactionID = 0x%x", TransactionID); + Log(" ClickedItem = %s", Item.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientWindowClose(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); + Log("Received a PACKET_WINDOW_CLOSE from the client:"); + Log(" WindowID = %d", WindowID); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleServerAttachEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, VehicleID); + HANDLE_SERVER_PACKET_READ(ReadBool, bool, Leash); + Log("Received a PACKET_ATTACH_ENTITY from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" VehicleID = %d (0x%x)", VehicleID, VehicleID); + Log(" Leash = %s", Leash ? "true" : "false"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerBlockAction(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte1); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte2); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockID); + Log("Received a PACKET_BLOCK_ACTION from the server:"); + Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Bytes = (%d, %d) == (0x%x, 0x%x)", Byte1, Byte2, Byte1, Byte2); + Log(" BlockID = %d", BlockID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerBlockChange(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockType); + HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta); + Log("Received a PACKET_BLOCK_CHANGE from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerChangeGameState(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, Reason); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Data); + Log("Received a PACKET_CHANGE_GAME_STATE from the server:"); + Log(" Reason = %d", Reason); + Log(" Data = %d", Data); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerChatMessage(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Message); + Log("Received a PACKET_CHAT_MESSAGE from the server:"); + Log(" Message = \"%s\"", Message.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerCollectPickup(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectedID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectorID); + Log("Received a PACKET_COLLECT_PICKUP from the server:"); + Log(" CollectedID = %d", CollectedID); + Log(" CollectorID = %d", CollectorID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerCompass(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnZ); + Log("Received PACKET_COMPASS from the server:"); + Log(" Spawn = {%d, %d, %d}", SpawnX, SpawnY, SpawnZ); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerDestroyEntities(void) +{ + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumEntities); + if (!m_ServerBuffer.SkipRead((int)NumEntities * 4)) + { + return false; + } + Log("Received PACKET_DESTROY_ENTITIES from the server:"); + Log(" NumEntities = %d", NumEntities); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEncryptionKeyRequest(void) +{ + // Read the packet from the server: + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ServerID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength); + AString PublicKey; + if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength)) + { + return false; + } + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength); + AString Nonce; + if (!m_ServerBuffer.ReadString(Nonce, NonceLength)) + { + return false; + } + Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:"); + Log(" ServerID = %s", ServerID.c_str()); + + // Reply to the server: + SendEncryptionKeyResponse(PublicKey, Nonce); + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD to the client, using our own key: + Log("Sending PACKET_ENCRYPTION_KEY_REQUEST to the CLIENT"); + AString key; + StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs + m_Server.GetPublicKey().Save(sink); + cByteBuffer ToClient(512); + ToClient.WriteByte (PACKET_ENCRYPTION_KEY_REQUEST); + ToClient.WriteBEUTF16String16(ServerID); + ToClient.WriteBEShort ((short)key.size()); + ToClient.WriteBuf (key.data(), key.size()); + ToClient.WriteBEShort (4); + ToClient.WriteBEInt (m_Nonce); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + CLIENTSEND(ToClient); + return true; +} + + + + + +bool cConnection::HandleServerEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + Log("Received a PACKET_ENTITY from the server:"); + Log(" EntityID = %d", EntityID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityEquipment(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log("Received a PACKET_ENTITY_EQUIPMENT from the server:"); + Log(" EntityID = %d", EntityID); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityHeadLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); + Log("Received a PACKET_ENTITY_HEAD_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" HeadYaw = %d", HeadYaw); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityMetadata(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_ENTITY_METADATA from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityProperties(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Count); + Log("Received a PACKET_ENTITY_PROPERTIES from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Count = %d", Count); + for (int i = 0; i < Count; i++) + { + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Key); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value); + Log(" \"%s\" = %f", Key.c_str(), Value); + } // for i + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityRelativeMove(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); + Log("Received a PACKET_ENTITY_RELATIVE_MOVE from the server:"); + Log(" EntityID = %d", EntityID); + Log(" RelMove = <%d, %d, %d>", dx, dy, dz); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityRelativeMoveLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_RELATIVE_MOVE_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" RelMove = <%d, %d, %d>", dx, dy, dz); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityStatus(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Status); + Log("Received a PACKET_ENTITY_STATUS from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Status = %d", Status); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityTeleport(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_TELEPORT from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityVelocity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); + Log("Received a PACKET_ENTITY_VELOCITY from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerIncrementStatistic(void) +{ + // 0xc8 + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, StatisticID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Amount); + Log("Received a PACKET_INCREMENT_STATISTIC from the server:"); + Log(" StatisticID = %d (0x%x)", StatisticID, StatisticID); + Log(" Amount = %d", Amount); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerKeepAlive(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PingID); + Log("Received a PACKET_KEEP_ALIVE from the server:"); + Log(" ID = %d", PingID); + COPY_TO_CLIENT() + return true; +} + + + + + +bool cConnection::HandleServerEncryptionKeyResponse(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Lengths); + if (Lengths != 0) + { + Log("Lengths are not zero!"); + return true; + } + Log("Server communication is now encrypted"); + m_ServerState = csEncryptedUnderstood; + return true; +} + + + + + +bool cConnection::HandleServerKick(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Reason); + Log("Received PACKET_KICK from the SERVER:"); + if (m_HasClientPinged) + { + Log(" This was a std reply to client's PING"); + AStringVector Split; + + // Split by NULL chars (StringSplit() won't work here): + size_t Last = 0; + for (size_t i = 0; i < Reason.size(); i++) + { + if (Reason[i] == 0) + { + Split.push_back(Reason.substr(Last, i - Last)); + Last = i + 1; + } + } + + if (Split.size() == 5) + { + Log(" Protocol version: \"%s\"", Split[0].c_str()); + Log(" Server version: \"%s\"", Split[1].c_str()); + Log(" MOTD: \"%s\"", Split[2].c_str()); + Log(" Cur players: \"%s\"", Split[3].c_str()); + Log(" Max players: \"%s\"", Split[4].c_str()); + } + else + { + DataLog(Reason.data(), Reason.size(), " Unknown reply format, dumping hex:"); + } + } + else + { + Log(" Reason = \"%s\"", Reason.c_str()); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerLogin(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Dimension); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Unused); + HANDLE_SERVER_PACKET_READ(ReadChar, char, MaxPlayers); + Log("Received a PACKET_LOGIN from the server:"); + Log(" EntityID = %d", EntityID); + Log(" LevelType = \"%s\"", LevelType.c_str()); + Log(" GameMode = %d", GameMode); + Log(" Dimension = %d", Dimension); + Log(" Difficulty = %d", Difficulty); + Log(" Unused = %d", Unused); + Log(" MaxPlayers = %d", MaxPlayers); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerMapChunk(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsContiguous); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AdditionalBitmap); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); + AString CompressedData; + if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + { + return false; + } + Log("Received a PACKET_MAP_CHUNK from the server:"); + Log(" ChunkPos = [%d, %d]", ChunkX, ChunkZ); + Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); + + // TODO: Save the compressed data into a file for later analysis + + COPY_TO_CLIENT() + return true; +} + + + + + +bool cConnection::HandleServerMapChunkBulk(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ChunkCount); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); + HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent); + AString CompressedData; + if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + { + return false; + } + AString Meta; + if (!m_ServerBuffer.ReadString(Meta, ChunkCount * 12)) + { + return false; + } + Log("Received a PACKET_MAP_CHUNK_BULK from the server:"); + Log(" ChunkCount = %d", ChunkCount); + Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); + Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false"); + + // TODO: Save the compressed data into a file for later analysis + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerMultiBlockChange(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumBlocks); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataSize); + AString BlockChangeData; + if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize)) + { + return false; + } + Log("Received a PACKET_MULTI_BLOCK_CHANGE packet from the server:"); + Log(" Chunk = [%d, %d]", ChunkX, ChunkZ); + Log(" NumBlocks = %d", NumBlocks); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerNamedSoundEffect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, SoundName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_NAMED_SOUND_EFFECT from the server:"); + Log(" SoundName = \"%s\"", SoundName.c_str()); + Log(" Pos = (%d, %d, %d) ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 8, PosY / 8, PosZ / 8); + Log(" Volume = %f", Volume); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerAbilities(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, Flags); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + Log("Received a PACKET_PLAYER_ABILITIES from the server:"); + Log(" Flags = %d (0x%02x)", Flags, Flags); + Log(" FlyingSpeed = %f", FlyingSpeed); + Log(" WalkingSpeed = %f", WalkingSpeed); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerAnimation(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, AnimationID); + Log("Received a PACKET_PLAYER_ANIMATION from the server:"); + Log(" PlayerID: %d (0x%x)", PlayerID, PlayerID); + Log(" Animation: %d", AnimationID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerListItem(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, PlayerName); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnline); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Ping); + Log("Received a PACKET_PLAYERLIST_ITEM from the server:"); + Log(" PlayerName = \"%s\"", PlayerName.c_str()); + Log(" Ping = %d", Ping); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerPositionLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION_LOOK from the server"); + + // TODO: list packet contents + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSetExperience(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, ExperienceBar); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Level); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, TotalExperience); + Log("Received a PACKET_SET_EXPERIENCE from the server:"); + Log(" ExperienceBar = %.05f", ExperienceBar); + Log(" Level = %d", Level); + Log(" TotalExperience = %d", TotalExperience); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSetSlot(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log("Received a PACKET_SET_SLOT from the server:"); + Log(" WindowID = %d", WindowID); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSlotSelect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + Log("Received a PACKET_SLOT_SELECT from the server:"); + Log(" SlotNum = %d", SlotNum); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSoundEffect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EffectID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Data); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NoVolumeDecrease); + Log("Received a PACKET_SOUND_EFFECT from the server:"); + Log(" EffectID = %d", EffectID); + Log(" Pos = {%d, %d, %d}", PosX, PosY, PosZ); + Log(" Data = %d", Data); + Log(" NoVolumeDecrease = %d", NoVolumeDecrease); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnMob(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, MobType); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_SPAWN_MOB from the server:"); + Log(" EntityID = %d", EntityID); + Log(" MobType = %d", MobType); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Angles = [%d, %d, %d]", Yaw, Pitch, HeadYaw); + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnNamedEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, EntityName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, CurrentItem); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" Name = %s", EntityName.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Rotation = ", Yaw, Pitch); + Log(" CurrentItem = %d", CurrentItem); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnObjectVehicle(void) +{ + #ifdef _DEBUG + // DEBUG: + // This packet is still troublesome when DataIndicator != 0 + AString Buffer; + m_ServerBuffer.ResetRead(); + m_ServerBuffer.ReadAll(Buffer); + m_ServerBuffer.ResetRead(); + m_ServerBuffer.SkipRead(1); + if (Buffer.size() > 128) + { + // Only log up to 128 bytes + Buffer.erase(128, AString::npos); + } + DataLog(Buffer.data(), Buffer.size(), "Buffer while parsing the PACKET_SPAWN_OBJECT_VEHICLE packet (%d bytes):", Buffer.size()); + #endif // _DEBUG + + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, ObjType); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator); + AString ExtraData; + short VelocityX, VelocityY, VelocityZ; + if (DataIndicator != 0) + { + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedZ); + VelocityX = SpeedX; VelocityY = SpeedY; VelocityZ = SpeedZ; // Speed vars are local to this scope, but we need them available later + /* + // This doesn't seem to work - for a falling block I'm getting no extra data at all + int ExtraLen = 0; + switch (ObjType) + { + case OBJECT_FALLING_BLOCK: ExtraLen = 4; break; // int: BlockType | (BlockMeta << 12) + case OBJECT_ARROW: + case OBJECT_SNOWBALL: + case OBJECT_EGG: + case OBJECT_EYE_OF_ENDER: + case OBJECT_DRAGON_EGG: + case OBJECT_FISHING_FLOAT: + { + ExtraLen = 4; break; // int: EntityID of the thrower + } + // TODO: Splash potions + } + if ((ExtraLen > 0) && !m_ServerBuffer.ReadString(ExtraData, ExtraLen)) + { + return false; + } + */ + } + Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" ObjType = %d (0x%x)", ObjType, ObjType); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Rotation = ", Yaw, Pitch); + Log(" DataIndicator = %d (0x%x)", DataIndicator, DataIndicator); + if (DataIndicator != 0) + { + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + DataLog(ExtraData.data(), ExtraData.size(), " ExtraData size = %d:", ExtraData.size()); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnPainting(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ImageName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Direction); + Log("Received a PACKET_SPAWN_PAINTING from the server:"); + Log(" EntityID = %d", EntityID); + Log(" ImageName = \"%s\"", ImageName.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Direction = %d", Direction); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnPickup(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + AString ItemDesc; + if (!ParseSlot(m_ServerBuffer, ItemDesc)) + { + return false; + } + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Rotation); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Roll); + Log("Received a PACKET_SPAWN_PICKUP from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Item = %s", ItemDesc.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Angles = [%d, %d, %d]", Rotation, Pitch, Roll); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerTimeUpdate(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, WorldAge); + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, TimeOfDay); + Log("Received a PACKET_TIME_UPDATE from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateHealth(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Health); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Food); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Saturation); + Log("Received a PACKET_UPDATE_HEALTH from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateSign(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line4); + Log("Received a PACKET_UPDATE_SIGN from the server:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateTileEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Action); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, DataLength); + AString Data; + if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength)) + { + return false; + } + Log("Received a PACKET_UPDATE_TILE_ENTITY from the server:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Action = %d", Action); + DataLog(Data.data(), Data.size(), " Data (%d bytes)", Data.size()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowClose(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + Log("Received a PACKET_WINDOW_CLOSE from the server:"); + Log(" WindowID = %d", WindowID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowContents(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumSlots); + Log("Received a PACKET_WINDOW_CONTENTS from the server:"); + Log(" WindowID = %d", WindowID); + Log(" NumSlots = %d", NumSlots); + AStringVector Items; + for (short i = 0; i < NumSlots; i++) + { + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log(" %d: %s", i, Item.c_str()); + } + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowOpen(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowType); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Title); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumSlots); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, UseProvidedTitle); + int HorseInt = 0; + if (WindowType == 11) // Horse / Donkey / Mule + { + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, intHorseInt); + HorseInt = intHorseInt; + } + Log("Received a PACKET_WINDOW_OPEN from the server:"); + Log(" WindowID = %d", WindowID); + Log(" WindowType = %d", WindowType); + Log(" Title = \"%s\", Use = %d", Title.c_str(), UseProvidedTitle); + Log(" NumSlots = %d", NumSlots); + if (WindowType == 11) + { + Log(" HorseInt = %d (0x%08x)", HorseInt, HorseInt); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc) +{ + short ItemType; + if (!a_Buffer.ReadBEShort(ItemType)) + { + return false; + } + if (ItemType <= 0) + { + a_ItemDesc = ""; + return true; + } + if (!a_Buffer.CanReadBytes(5)) + { + return false; + } + char ItemCount; + short ItemDamage; + short MetadataLength; + a_Buffer.ReadChar(ItemCount); + a_Buffer.ReadBEShort(ItemDamage); + a_Buffer.ReadBEShort(MetadataLength); + Printf(a_ItemDesc, "%d:%d * %d", ItemType, ItemDamage, ItemCount); + if (MetadataLength <= 0) + { + return true; + } + AString Metadata; + Metadata.resize(MetadataLength); + if (!a_Buffer.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return false; + } + AString MetaHex; + CreateHexDump(MetaHex, Metadata.data(), Metadata.size(), 16); + AppendPrintf(a_ItemDesc, "; %d bytes of meta:\n%s", MetadataLength, MetaHex.c_str()); + + // Save metadata to a file: + AString fnam; + Printf(fnam, "%s_item_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++); + FILE * f = fopen(fnam.c_str(), "wb"); + if (f != NULL) + { + fwrite(Metadata.data(), 1, Metadata.size(), f); + fclose(f); + AppendPrintf(a_ItemDesc, "\n (saved to file \"%s\")", fnam.c_str()); + } + + return true; +} + + + + + +bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata) +{ + char x; + if (!a_Buffer.ReadChar(x)) + { + return false; + } + a_Metadata.push_back(x); + while (x != 0x7f) + { + int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index + int Type = ((unsigned)((unsigned char)x)) >> 5; // Upper 3 bits = type + int Length = 0; + switch (Type) + { + case 0: Length = 1; break; // byte + case 1: Length = 2; break; // short + case 2: Length = 4; break; // int + case 3: Length = 4; break; // float + case 4: // string16 + { + short Len = 0; + if (!a_Buffer.ReadBEShort(Len)) + { + return false; + } + short NetLen = htons(Len); + a_Metadata.append((char *)&NetLen, 2); + Length = Len; + break; + } + case 5: + { + int Before = a_Buffer.GetReadableSpace(); + AString ItemDesc; + if (!ParseSlot(a_Buffer, ItemDesc)) + { + return false; + } + int After = a_Buffer.GetReadableSpace(); + a_Buffer.ResetRead(); + a_Buffer.SkipRead(a_Buffer.GetReadableSpace() - Before); + Length = Before - After; + break; + } + case 6: Length = 12; break; // 3 * int + default: + { + ASSERT(!"Unknown metadata type"); + break; + } + } // switch (Type) + AString data; + if (!a_Buffer.ReadString(data, Length)) + { + return false; + } + a_Metadata.append(data); + if (!a_Buffer.ReadChar(x)) + { + return false; + } + a_Metadata.push_back(x); + } // while (x != 0x7f) + return true; +} + + + + + +void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount) +{ + AString Indent(a_IndentCount, ' '); + int pos = 0; + while (a_Metadata[pos] != 0x7f) + { + int Index = ((unsigned)((unsigned char)a_Metadata[pos])) & 0x1f; // Lower 5 bits = index + int Type = ((unsigned)((unsigned char)a_Metadata[pos])) >> 5; // Upper 3 bits = type + int Length = 0; + switch (Type) + { + case 0: + { + Log("%sbyte[%d] = %d", Indent.c_str(), Index, a_Metadata[pos + 1]); + pos += 1; + break; + } + case 1: + { + Log("%sshort[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]); + pos += 2; + break; + } + case 2: + { + Log("%sint[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); + pos += 4; + break; + } + case 3: + { + Log("%sfloat[%d] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); + pos += 4; + break; + } + case 4: // string16 + { + short Length = (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]; + Log("%sstring[%d] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + 3); + pos += Length + 2; + break; + } + case 5: + { + int BytesLeft = a_Metadata.size() - pos - 1; + cByteBuffer bb(BytesLeft); + bb.Write(a_Metadata.data() + pos + 1, BytesLeft); + AString ItemDesc; + if (!ParseSlot(bb, ItemDesc)) + { + ASSERT(!"Cannot parse item description from metadata"); + return; + } + int After = bb.GetReadableSpace(); + int BytesConsumed = BytesLeft - bb.GetReadableSpace(); + + Log("%sslot[%d] = %s (%d bytes)", Indent.c_str(), Index, ItemDesc.c_str(), BytesConsumed); + pos += BytesConsumed; + break; + } + case 6: + { + Log("%spos[%d] = <%d, %d, %d>", Indent.c_str(), Index, + (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4], + (a_Metadata[pos + 5] << 24) | (a_Metadata[pos + 6] << 16) | (a_Metadata[pos + 7] << 8) | a_Metadata[pos + 8], + (a_Metadata[pos + 9] << 24) | (a_Metadata[pos + 10] << 16) | (a_Metadata[pos + 11] << 8) | a_Metadata[pos + 12] + ); + pos += 12; + break; + } + default: + { + ASSERT(!"Unknown metadata type"); + break; + } + } // switch (Type) + pos += 1; + } // while (x != 0x7f) +} + + + + + +void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce) +{ + // Generate the shared secret and encrypt using the server's public key + byte SharedSecret[16]; + byte EncryptedSecret[128]; + memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret + RSA::PublicKey pk; + CryptoPP::StringSource src(a_ServerPublicKey, true); + ByteQueue bq; + src.TransferTo(bq); + bq.MessageEnd(); + pk.Load(bq); + RSAES::Encryptor rsaEncryptor(pk); + RandomPool rng; + time_t CurTime = time(NULL); + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + int EncryptedLength = rsaEncryptor.FixedCiphertextLength(); + ASSERT(EncryptedLength <= sizeof(EncryptedSecret)); + rsaEncryptor.Encrypt(rng, SharedSecret, sizeof(SharedSecret), EncryptedSecret); + m_ServerEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + m_ServerDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + + // Encrypt the nonce: + byte EncryptedNonce[128]; + rsaEncryptor.Encrypt(rng, (const byte *)(a_Nonce.data()), a_Nonce.size(), EncryptedNonce); + + // Send the packet to the server: + Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER"); + cByteBuffer ToServer(1024); + ToServer.WriteByte(PACKET_ENCRYPTION_KEY_RESPONSE); + ToServer.WriteBEShort(EncryptedLength); + ToServer.WriteBuf(EncryptedSecret, EncryptedLength); + ToServer.WriteBEShort(EncryptedLength); + ToServer.WriteBuf(EncryptedNonce, EncryptedLength); + SERVERSEND(ToServer); +} + + + + + +void cConnection::StartClientEncryption(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES::Decryptor rsaDecryptor(m_Server.GetPrivateKey()); + time_t CurTime = time(NULL); + RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + Log("Client: Bad nonce length"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != m_Nonce) + { + Log("Bad nonce value"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte SharedSecret[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), SharedSecret); + if (!res.isValidCoding || (res.messageLength != 16)) + { + Log("Bad key length"); + return; + } + + // Send encryption key response: + cByteBuffer ToClient(6); + ToClient.WriteByte((char)0xfc); + ToClient.WriteBEShort(0); + ToClient.WriteBEShort(0); + CLIENTSEND(ToClient); + + // Start the encryption: + m_ClientEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + m_ClientDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + Log("Client connection is now encrypted"); + m_ClientState = csEncryptedUnderstood; + + // Handle all postponed server data + DecodeServersPackets(NULL, 0); +} + + + + diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h new file mode 100644 index 000000000..013471302 --- /dev/null +++ b/Tools/ProtoProxy/Connection.h @@ -0,0 +1,200 @@ + +// Connection.h + +// Interfaces to the cConnection class representing a single pair of connected sockets + + + + + +#pragma once + +#include +#include "ByteBuffer.h" + + + + + +class cServer; + + + + + +class cConnection +{ + AString m_LogNameBase; ///< Base for the log filename and all files connected to this log + + int m_ItemIdx; ///< Index for the next file into which item metadata should be written (ParseSlot() function) + + cCriticalSection m_CSLog; + FILE * m_LogFile; + + cServer & m_Server; + SOCKET m_ClientSocket; + SOCKET m_ServerSocket; + + clock_t m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) + + enum eConnectionState + { + csUnencrypted, // The connection is not encrypted. Packets must be decoded in order to be able to start decryption. + csEncryptedUnderstood, // The communication is encrypted and so far all packets have been understood, so they can be still decoded + csEncryptedUnknown, // The communication is encrypted, but an unknown packet has been received, so packets cannot be decoded anymore + }; + + eConnectionState m_ClientState; + eConnectionState m_ServerState; + + int m_Nonce; + +public: + cConnection(SOCKET a_ClientSocket, cServer & a_Server); + ~cConnection(); + + void Run(void); + + void Log(const char * a_Format, ...); + void DataLog(const void * a_Data, int a_Size, const char * a_Format, ...); + void LogFlush(void); + +protected: + typedef CFB_Mode::Encryption Encryptor; + typedef CFB_Mode::Decryption Decryptor; + + cByteBuffer m_ClientBuffer; + cByteBuffer m_ServerBuffer; + + Decryptor m_ServerDecryptor; + Encryptor m_ServerEncryptor; + + Decryptor m_ClientDecryptor; + Encryptor m_ClientEncryptor; + + /// Set to true when PACKET_PING is received from the client; will cause special parsing for server kick + bool m_HasClientPinged; + + bool ConnectToServer(void); + + /// Relays data from server to client; returns false if connection aborted + bool RelayFromServer(void); + + /// Relays data from client to server; returns false if connection aborted + bool RelayFromClient(void); + + /// Returns the time relative to the first call of this function, in the fractional seconds elapsed + double GetRelativeTime(void); + + /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. + bool SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer); + + /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. + bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer); + + /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false + bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer); + + /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false + bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); + + /// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped + bool DecodeClientsPackets(const char * a_Data, int a_Size); + + /// Decodes packets coming from the server, sends appropriate counterparts to the client; returns false if the connection is to be dropped + bool DecodeServersPackets(const char * a_Data, int a_Size); + + // Packet handling, client-side: + bool HandleClientAnimation(void); + bool HandleClientBlockDig(void); + bool HandleClientBlockPlace(void); + bool HandleClientChatMessage(void); + bool HandleClientClientStatuses(void); + bool HandleClientCreativeInventoryAction(void); + bool HandleClientEncryptionKeyResponse(void); + bool HandleClientEntityAction(void); + bool HandleClientHandshake(void); + bool HandleClientKeepAlive(void); + bool HandleClientLocaleAndView(void); + bool HandleClientPing(void); + bool HandleClientPlayerAbilities(void); + bool HandleClientPlayerLook(void); + bool HandleClientPlayerOnGround(void); + bool HandleClientPlayerPosition(void); + bool HandleClientPlayerPositionLook(void); + bool HandleClientSlotSelect(void); + bool HandleClientUpdateSign(void); + bool HandleClientUseEntity(void); + bool HandleClientWindowClick(void); + bool HandleClientWindowClose(void); + + // Packet handling, server-side: + bool HandleServerAttachEntity(void); + bool HandleServerBlockAction(void); + bool HandleServerBlockChange(void); + bool HandleServerChangeGameState(void); + bool HandleServerChatMessage(void); + bool HandleServerCollectPickup(void); + bool HandleServerCompass(void); + bool HandleServerDestroyEntities(void); + bool HandleServerEncryptionKeyRequest(void); + bool HandleServerEncryptionKeyResponse(void); + bool HandleServerEntity(void); + bool HandleServerEntityEquipment(void); + bool HandleServerEntityHeadLook(void); + bool HandleServerEntityLook(void); + bool HandleServerEntityMetadata(void); + bool HandleServerEntityProperties(void); + bool HandleServerEntityRelativeMove(void); + bool HandleServerEntityRelativeMoveLook(void); + bool HandleServerEntityStatus(void); + bool HandleServerEntityTeleport(void); + bool HandleServerEntityVelocity(void); + bool HandleServerIncrementStatistic(void); + bool HandleServerKeepAlive(void); + bool HandleServerKick(void); + bool HandleServerLogin(void); + bool HandleServerMapChunk(void); + bool HandleServerMapChunkBulk(void); + bool HandleServerMultiBlockChange(void); + bool HandleServerNamedSoundEffect(void); + bool HandleServerPlayerAbilities(void); + bool HandleServerPlayerAnimation(void); + bool HandleServerPlayerListItem(void); + bool HandleServerPlayerPositionLook(void); + bool HandleServerSetExperience(void); + bool HandleServerSetSlot(void); + bool HandleServerSlotSelect(void); + bool HandleServerSoundEffect(void); + bool HandleServerSpawnMob(void); + bool HandleServerSpawnNamedEntity(void); + bool HandleServerSpawnObjectVehicle(void); + bool HandleServerSpawnPainting(void); + bool HandleServerSpawnPickup(void); + bool HandleServerTimeUpdate(void); + bool HandleServerUpdateHealth(void); + bool HandleServerUpdateSign(void); + bool HandleServerUpdateTileEntity(void); + bool HandleServerWindowClose(void); + bool HandleServerWindowContents(void); + bool HandleServerWindowOpen(void); + + /// Parses the slot data in a_Buffer into item description; returns true if successful, false if not enough data + bool ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc); + + /// Parses the metadata in a_Buffer into raw metadata in an AString; returns true if successful, false if not enough data + bool ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata); + + /// Logs the contents of the metadata in the AString, using Log(). Assumes a_Metadata is valid (parsed by ParseMetadata()). The log is indented by a_IndentCount spaces + void LogMetadata(const AString & a_Metadata, size_t a_IndentCount); + + /// Send EKResp to the server: + void SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce); + + /// Starts client encryption based on the parameters received + void StartClientEncryption(const AString & a_EncryptedSecret, const AString & a_EncryptedNonce); +} ; + + + + diff --git a/Tools/ProtoProxy/Globals.cpp b/Tools/ProtoProxy/Globals.cpp new file mode 100644 index 000000000..2c60fd698 --- /dev/null +++ b/Tools/ProtoProxy/Globals.cpp @@ -0,0 +1,10 @@ + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/Tools/ProtoProxy/Globals.h b/Tools/ProtoProxy/Globals.h new file mode 100644 index 000000000..404044a5f --- /dev/null +++ b/Tools/ProtoProxy/Globals.h @@ -0,0 +1,221 @@ + +// Globals.h + +// This file gets included from every module in the project, so that global symbols may be introduced easily +// Also used for precompiled header generation in MSVC environments + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + + + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + + + + + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator=(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace +#else + #include + #include // for mkdir + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#if !defined(ANDROID_NDK) + #include +#endif +#endif + +#if !defined(ANDROID_NDK) + #define USE_SQUIRREL +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include + + + + + +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "OSSupport/CriticalSection.h" + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) +#define KiB * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) ) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef NDEBUG + #define ASSERT(x) ((void)0) +#else + #define ASSERT assert +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) + + + + + +/// A generic interface used mainly in ForEach() functions +template class cItemCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; + + + + + +#include "CryptoPP/randpool.h" +#include "CryptoPP/aes.h" +#include "CryptoPP/rsa.h" +#include "CryptoPP/modes.h" + +using namespace CryptoPP; + + + + +#define LOGERROR printf +#define LOGINFO printf +#define LOGWARNING printf \ No newline at end of file diff --git a/Tools/ProtoProxy/ProtoProxy.cpp b/Tools/ProtoProxy/ProtoProxy.cpp new file mode 100644 index 000000000..0d61835e7 --- /dev/null +++ b/Tools/ProtoProxy/ProtoProxy.cpp @@ -0,0 +1,32 @@ + +// ProtoProxy.cpp + +// Implements the main app entrypoint + +#include "Globals.h" +#include "Server.h" + + + + + +int main(int argc, char ** argv) +{ + int ListenPort = (argc > 1) ? atoi(argv[1]) : 25564; + int ConnectPort = (argc > 2) ? atoi(argv[2]) : 25565; + cServer Server; + int res = Server.Init(ListenPort, ConnectPort); + if (res != 0) + { + printf("Server initialization failed: %d", res); + return res; + } + + Server.Run(); + + return 0; +} + + + + diff --git a/Tools/ProtoProxy/ProtoProxy.sln b/Tools/ProtoProxy/ProtoProxy.sln new file mode 100644 index 000000000..5488abd41 --- /dev/null +++ b/Tools/ProtoProxy/ProtoProxy.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtoProxy", "ProtoProxy.vcproj", "{EFEC8F76-1397-49A4-885B-314CB4244231}" + ProjectSection(ProjectDependencies) = postProject + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CryptoPP", "..\..\VC2008\CryptoPP.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.Build.0 = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.ActiveCfg = Release|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.Build.0 = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.Build.0 = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.ActiveCfg = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/ProtoProxy/ProtoProxy.txt b/Tools/ProtoProxy/ProtoProxy.txt new file mode 100644 index 000000000..d232d41fd --- /dev/null +++ b/Tools/ProtoProxy/ProtoProxy.txt @@ -0,0 +1,31 @@ + +// ProtoProxy.txt + +// A readme for the project + +/* +ProtoProxy +========== + +This is a project to create a proxy for the MineCraft protocol, allowing anyone to view the data sent over a network connection between a client and a server. This, in fact, performs a kind of Man-In-The-Middle (MITM) attack on the protocol by tapping in between the connection points and providing a decrypter and an encrypter for each. + +In order to catch the encryption parameters, the MC protocol needs to be understood at least a little bit at the beginning, when the cryptography parameters are exchanged. + +This project is currently Windows-only and I don't plan on making it multi-platform, although the effort needed for doing so should be minimal. + +The proxy only works on the localhost connection. It listens on port 25564 and expects the underlying MC server to run on port 25565. Ports can be changed by cmdline args: ProtoProxy . + +You need to set the server *not* to verify usernames ("online-mode=false" in server.properties) in order to be able to connect through ProtoProxy - since the full server name, including the port, is used for verification, the client uses different servername than the server and thus the verification fails. + + + +ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format. +The latest protocol which has been tested is 1.6.1 (#73). + + +*/ + + + + + diff --git a/Tools/ProtoProxy/ProtoProxy.vcproj b/Tools/ProtoProxy/ProtoProxy.vcproj new file mode 100644 index 000000000..b7384ee8a --- /dev/null +++ b/Tools/ProtoProxy/ProtoProxy.vcproj @@ -0,0 +1,267 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp new file mode 100644 index 000000000..3d4913355 --- /dev/null +++ b/Tools/ProtoProxy/Server.cpp @@ -0,0 +1,82 @@ + +// Server.cpp + +// Interfaces to the cServer class encapsulating the entire "server" + +#include "Globals.h" +#include "Server.h" +#include "Connection.h" + + + + + +cServer::cServer(void) +{ +} + + + + + +int cServer::Init(short a_ListenPort, short a_ConnectPort) +{ + m_ConnectPort = a_ConnectPort; + WSAData wsa; + int res = WSAStartup(0x0202, &wsa); + if (res != 0) + { + printf("Cannot initialize WinSock: %d\n", res); + return res; + } + + printf("Generating protocol encryption keypair...\n"); + time_t CurTime = time(NULL); + RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); + RSA::PublicKey pk(m_PrivateKey); + m_PublicKey = pk; + + m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + sockaddr_in local; + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = 0; // All interfaces + local.sin_port = htons(a_ListenPort); + bind(m_ListenSocket, (sockaddr *)&local, sizeof(local)); + listen(m_ListenSocket, 1); + + printf("Listening on port %d, connecting to localhost:%d\n", a_ListenPort, a_ConnectPort); + + return 0; +} + + + + + +void cServer::Run(void) +{ + printf("Server running.\n"); + while (true) + { + sockaddr_in Addr; + ZeroMemory(&Addr, sizeof(Addr)); + int AddrSize = sizeof(Addr); + SOCKET client = accept(m_ListenSocket, (sockaddr *)&Addr, &AddrSize); + if (client == INVALID_SOCKET) + { + printf("accept returned an error: %d; bailing out.\n", WSAGetLastError()); + return; + } + printf("Client connected, proxying...\n"); + cConnection Connection(client, *this); + Connection.Run(); + printf("Client disconnected. Ready for another connection.\n"); + } +} + + + + diff --git a/Tools/ProtoProxy/Server.h b/Tools/ProtoProxy/Server.h new file mode 100644 index 000000000..da64036b4 --- /dev/null +++ b/Tools/ProtoProxy/Server.h @@ -0,0 +1,38 @@ + +// Server.h + +// Interfaces to the cServer class encapsulating the entire "server" + + + + + +#pragma once + + + + + + +class cServer +{ + SOCKET m_ListenSocket; + RSA::PrivateKey m_PrivateKey; + RSA::PublicKey m_PublicKey; + short m_ConnectPort; + +public: + cServer(void); + + int Init(short a_ListenPort, short a_ConnectPort); + void Run(void); + + RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + + short GetConnectPort(void) const { return m_ConnectPort; } +} ; + + + + -- cgit v1.2.3