// Connection.cpp // Interfaces to the cConnection class representing a single pair of connected sockets #include "Globals.h" #include "Connection.h" #include "Server.h" #include #ifdef _WIN32 #include // For _mkdir() #endif /// When defined, the following macro causes a sleep after each parsed packet (DEBUG-mode only) // #define SLEEP_AFTER_PACKET #if defined(_DEBUG) && defined(SLEEP_AFTER_PACKET) #define DebugSleep Sleep #else #define DebugSleep(X) #endif // else _DEBUG #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); \ switch (m_ServerState) \ { \ case csUnencrypted: \ { \ SERVERSEND(ToServer.data(), ToServer.size()); \ break; \ } \ case csEncryptedUnderstood: \ case csEncryptedUnknown: \ { \ SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ break; \ } \ case csWaitingForEncryption: \ { \ Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \ m_ServerEncryptionBuffer.append(ToServer.data(), ToServer.size()); \ break; \ } \ } \ DebugSleep(50); \ } #define COPY_TO_CLIENT() \ { \ AString ToClient; \ m_ServerBuffer.ReadAgain(ToClient); \ switch (m_ClientState) \ { \ case csUnencrypted: \ { \ CLIENTSEND(ToClient.data(), ToClient.size()); \ break; \ } \ case csEncryptedUnderstood: \ case csEncryptedUnknown: \ { \ CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ break; \ } \ case csWaitingForEncryption: \ { \ Log("Waiting for client encryption, queued %u bytes", ToClient.size()); \ m_ClientEncryptionBuffer.append(ToClient.data(), ToClient.size()); \ break; \ } \ } \ DebugSleep(50); \ } #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_EXPLOSION = 0x3c, 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_TAB_COMPLETION = 0xcb, PACKET_LOCALE_AND_VIEW = 0xcc, PACKET_CLIENT_STATUSES = 0xcd, PACKET_PLUGIN_MESSAGE = 0xfa, PACKET_ENCRYPTION_KEY_RESPONSE = 0xfc, PACKET_ENCRYPTION_KEY_REQUEST = 0xfd, PACKET_PING = 0xfe, PACKET_KICK = 0xff, // Synonyms: PACKET_DISCONNECT = PACKET_KICK, } ; enum { OBJECT_BOAT = 1, OBJECT_MINECART = 10, OBJECT_MINECART_STORAGE = 11, OBJECT_MINECART_POWERED = 12, OBJECT_MINECART_TNT = 13, OBJECT_MINECART_HOPPER = 14, 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, } ; AString PrintableAbsIntTriplet(int a_X, int a_Y, int a_Z, double a_Divisor = 32) { return Printf("<%d, %d, %d> ~ {%.02f, %.02f, %.02f}", a_X, a_Y, a_Z, (double)a_X / a_Divisor, (double)a_Y / a_Divisor, (double)a_Z / a_Divisor ); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // 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(m_Timer.GetNowTime()), m_ClientState(csUnencrypted), m_ServerState(csUnencrypted), m_Nonce(0), m_ClientBuffer(1024 KiB), m_ServerBuffer(1024 KiB), m_HasClientPinged(false) { // Create the Logs subfolder, if not already created: #if defined(_WIN32) _mkdir("Logs"); #else mkdir("Logs", 0777); #endif Printf(m_LogNameBase, "Logs/Log_%d", (int)time(NULL)); AString fnam(m_LogNameBase); fnam.append(".log"); m_LogFile = fopen(fnam.c_str(), "w"); Log("Log file created"); printf("Connection is logged to file \"%s\"\n", fnam.c_str()); } 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: case csWaitingForEncryption: { 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: case csWaitingForEncryption: { 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)(m_Timer.GetNowTime() - m_BeginTick) / 1000; } 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)) { unsigned char PacketType; m_ClientBuffer.ReadByte(PacketType); Log("Decoding client's packets, there are now %d bytes in the queue; next packet is 0x%02x", m_ClientBuffer.GetReadableSpace(), 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_DISCONNECT: HANDLE_CLIENT_READ(HandleClientDisconnect); 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_PLUGIN_MESSAGE: HANDLE_CLIENT_READ(HandleClientPluginMessage); break; case PACKET_SLOT_SELECT: HANDLE_CLIENT_READ(HandleClientSlotSelect); break; case PACKET_TAB_COMPLETION: HANDLE_CLIENT_READ(HandleClientTabCompletion); 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%02x", 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_EXPLOSION: HANDLE_SERVER_READ(HandleServerExplosion); 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_PLUGIN_MESSAGE: HANDLE_SERVER_READ(HandleServerPluginMessage); 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_TAB_COMPLETION: HANDLE_SERVER_READ(HandleServerTabCompletion); 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::HandleClientDisconnect(void) { HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Reason); Log("Received a PACKET_DISCONNECT from the client:"); Log(" Reason = \"%s\"", Reason.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::HandleClientPluginMessage(void) { HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, Length); AString Data; if (!m_ClientBuffer.ReadString(Data, Length)) { return false; } Log("Received a PACKET_PLUGIN_MESSAGE from the client"); Log(" ChannelName = \"%s\"", ChannelName.c_str()); DataLog(Data.data(), Length, " Data: %d bytes", Length); 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::HandleClientTabCompletion(void) { HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Query); Log("Received a PACKET_TAB_COMPLETION query from the client"); Log(" Query = \"%s\"", Query.c_str()); 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); HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ListLength); Log(" ListLength = %d", ListLength); for (int j = 0; j < ListLength; j++) { HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDHi); HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDLo); HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal); HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ByteVal); Log(" [%d] = {0x%08llx%08llx, %f, %i}", j, UUIDHi, UUIDLo, DblVal, ByteVal); } // for j } // 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 = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str()); 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 = %s", PrintableAbsIntTriplet(dx, dy, dz).c_str()); 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, AbsX); HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsY); HANDLE_SERVER_PACKET_READ(ReadBEInt, int, AbsZ); 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) ~ {%.02f, %.02f, %.02f}", AbsX, AbsY, AbsZ, (double)AbsX / 32, (double)AbsY / 32, (double)AbsZ / 32); 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 = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str()); COPY_TO_CLIENT(); return true; } bool cConnection::HandleServerExplosion(void) { HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX); HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY); HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ); HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Force); HANDLE_SERVER_PACKET_READ(ReadBEInt, int, NumRecords); struct sCoords { int x, y, z; sCoords(int a_X, int a_Y, int a_Z) : x(a_X), y(a_Y), z(a_Z) {} } ; std::vector Records; Records.reserve(NumRecords); int PosXI = (int)PosX, PosYI = (int)PosY, PosZI = (int)PosZ; for (int i = 0; i < NumRecords; i++) { HANDLE_SERVER_PACKET_READ(ReadChar, char, rx); HANDLE_SERVER_PACKET_READ(ReadChar, char, ry); HANDLE_SERVER_PACKET_READ(ReadChar, char, rz); Records.push_back(sCoords(PosXI + rx, PosYI + ry, PosZI + rz)); } HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionX); HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionY); HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, PlayerMotionZ); Log("Received a PACKET_EXPLOSION from the server:"); Log(" Pos = {%.02f, %.02f, %.02f}", PosX, PosY, PosZ); Log(" Force = %.02f", Force); Log(" NumRecords = %d", NumRecords); for (int i = 0; i < NumRecords; i++) { Log(" Records[%d] = {%d, %d, %d}", i, Records[i].x, Records[i].y, Records[i].z); } Log(" Player motion = <%.02f, %.02f, %.02f>", PlayerMotionX, PlayerMotionY, PlayerMotionZ); 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; DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size()); SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size()); m_ServerEncryptionBuffer.clear(); 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; size_t Len = Reason.size(); for (size_t i = 0; i < Len; i++) { if (Reason[i] == 0) { Split.push_back(Reason.substr(Last, i - Last)); Last = i + 1; } } if (Last < Len) { Split.push_back(Reason.substr(Last)); } if (Split.size() == 6) { Log(" Preamble: \"%s\"", Split[0].c_str()); Log(" Protocol version: \"%s\"", Split[1].c_str()); Log(" Server version: \"%s\"", Split[2].c_str()); Log(" MOTD: \"%s\"", Split[3].c_str()); Log(" Cur players: \"%s\"", Split[4].c_str()); Log(" Max players: \"%s\"", Split[5].c_str()); // Modify the MOTD to show that it's being ProtoProxied: Reason.assign(Split[0]); Reason.push_back(0); Reason.append(Split[1]); Reason.push_back(0); Reason.append(Split[2]); Reason.push_back(0); Reason.append(Printf("ProtoProxy: %s", Split[3].c_str())); Reason.push_back(0); Reason.append(Split[4]); Reason.push_back(0); Reason.append(Split[5]); AString ReasonBE16; UTF8ToRawBEUTF16(Reason.data(), Reason.size(), ReasonBE16); AString PacketStart("\xff"); PacketStart.push_back((ReasonBE16.size() / 2) / 256); PacketStart.push_back((ReasonBE16.size() / 2) % 256); CLIENTSEND(PacketStart.data(), PacketStart.size()); CLIENTSEND(ReasonBE16.data(), ReasonBE16.size()); return true; } 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; } // Read individual chunk metas. // Need to read them first and only then start logging (in case we don't have the full packet yet) struct sChunkMeta { int m_ChunkX, m_ChunkZ; short m_PrimaryBitmap; short m_AddBitmap; sChunkMeta(int a_ChunkX, int a_ChunkZ, short a_PrimaryBitmap, short a_AddBitmap) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_PrimaryBitmap(a_PrimaryBitmap), m_AddBitmap(a_AddBitmap) { } } ; typedef std::vector sChunkMetas; sChunkMetas ChunkMetas; ChunkMetas.reserve(ChunkCount); for (short i = 0; i < ChunkCount; i++) { HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap); HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AddBitmap); ChunkMetas.push_back(sChunkMeta(ChunkX, ChunkZ, PrimaryBitmap, AddBitmap)); } 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"); // Log individual chunk coords: int idx = 0; for (sChunkMetas::iterator itr = ChunkMetas.begin(), end = ChunkMetas.end(); itr != end; ++itr, ++idx) { Log(" [%d]: [%d, %d], primary bitmap 0x%02x, add bitmap 0x%02x", idx, itr->m_ChunkX, itr->m_ChunkZ, itr->m_PrimaryBitmap, itr->m_AddBitmap ); } // for itr - ChunkMetas[] // 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::HandleServerPluginMessage(void) { HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Length); AString Data; if (!m_ServerBuffer.ReadString(Data, Length)) { return false; } Log("Received a PACKET_PLUGIN_MESSAGE from the server"); Log(" ChannelName = \"%s\"", ChannelName.c_str()); DataLog(Data.data(), Length, " Data: %d bytes", Length); COPY_TO_SERVER(); 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 = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str()); Log(" Angles = [%d, %d, %d]", Yaw, Pitch, HeadYaw); Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str()); 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 = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str()); 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 = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str()); Log(" Rotation = ", Yaw, Pitch); Log(" DataIndicator = %d (0x%x)", DataIndicator, DataIndicator); if (DataIndicator != 0) { Log(" Velocity = %s", PrintableAbsIntTriplet(VelocityX, VelocityY, VelocityZ, 8000).c_str()); 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 = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str()); 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 = %s", PrintableAbsIntTriplet(PosX, PosY, PosZ).c_str()); Log(" Angles = [%d, %d, %d]", Rotation, Pitch, Roll); COPY_TO_CLIENT(); return true; } bool cConnection::HandleServerTabCompletion(void) { HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Results); Log("Received a PACKET_TAB_COMPLETION from the server, results given:"); // Parse the zero-terminated list of results: size_t len = Results.size(); size_t last = 0; for (size_t i = 0; i < len; i++) { if (Results[i] == 0) { Log(" \"%s\"", Results.substr(last, i - last).c_str()); last = i + 1; } } if (last < len) { Log(" \"%s\"", Results.substr(last, len - last).c_str()); } 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); m_ServerState = csWaitingForEncryption; } 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; // Send the queued data: DataLog(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size(), "Sending the queued data to client (%u bytes):", m_ClientEncryptionBuffer.size()); CLIENTENCRYPTSEND(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size()); m_ClientEncryptionBuffer.clear(); // Handle all postponed server data DecodeServersPackets(NULL, 0); }