From 5028f9343ce62222fb42d4875fc4ff45db0b0a70 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 15 Dec 2024 16:35:47 +0000 Subject: [PATCH] Big-endian networking fixes --- Source/dvlnet/base.cpp | 8 ++--- Source/dvlnet/base_protocol.h | 8 +++-- Source/dvlnet/frame_queue.cpp | 9 ++++-- Source/dvlnet/packet.h | 22 +++++++++++-- Source/dvlnet/tcp_server.cpp | 2 +- Source/multi.cpp | 60 +++++++++++++++++++++++------------ Source/multi.h | 2 ++ Source/storm/storm_net.hpp | 8 ++--- 8 files changed, 81 insertions(+), 38 deletions(-) diff --git a/Source/dvlnet/base.cpp b/Source/dvlnet/base.cpp index 4e88c2dc26b..12a01eb7cb4 100644 --- a/Source/dvlnet/base.cpp +++ b/Source/dvlnet/base.cpp @@ -94,7 +94,7 @@ void base::HandleDisconnect(packet &pkt) plr_t newPlayer = pkt.NewPlayer(); if (newPlayer != plr_self) { if (IsConnected(newPlayer)) { - auto leaveinfo = pkt.LeaveInfo(); + auto leaveinfo = SDL_SwapLE32(pkt.LeaveInfo()); _SNETEVENT ev; ev.eventid = EVENT_TYPE_PLAYER_LEAVE_GAME; ev.playerid = pkt.NewPlayer(); @@ -401,7 +401,7 @@ bool base::SNetRegisterEventHandler(event_type evtype, SEVTHANDLER func) bool base::SNetLeaveGame(int type) { auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, - plr_self, type); + plr_self, static_cast(type)); send(*pkt); plr_self = PLR_BROADCAST; return true; @@ -411,8 +411,8 @@ bool base::SNetDropPlayer(int playerid, uint32_t flags) { auto pkt = pktfty->make_packet(plr_self, PLR_BROADCAST, - (plr_t)playerid, - (leaveinfo_t)flags); + static_cast(playerid), + static_cast(SDL_SwapLE32(flags))); send(*pkt); RecvLocal(*pkt); return true; diff --git a/Source/dvlnet/base_protocol.h b/Source/dvlnet/base_protocol.h index a3b91e62c51..cd9d94d242d 100644 --- a/Source/dvlnet/base_protocol.h +++ b/Source/dvlnet/base_protocol.h @@ -296,8 +296,10 @@ void base_protocol

::recv_decrypted(packet &pkt, endpoint_t sender) size_t neededSize = sizeof(GameData) + (PlayerNameLength * MAX_PLRS); if (pkt.Info().size() < neededSize) return; - const GameData *gameData = (const GameData *)pkt.Info().data(); - if (gameData->size != sizeof(GameData)) + GameData gameData; + std::memcpy(&gameData, pkt.Info().data(), sizeof(GameData)); + gameData.SwapLE(); + if (gameData.size != sizeof(GameData)) return; std::vector playerNames; for (size_t i = 0; i < Players.size(); i++) { @@ -316,7 +318,7 @@ void base_protocol

::recv_decrypted(packet &pkt, endpoint_t sender) size_t gameNameSize = pkt.Info().size() - neededSize; gameName.resize(gameNameSize); std::memcpy(&gameName[0], pkt.Info().data() + neededSize, gameNameSize); - game_list[gameName] = std::make_tuple(*gameData, playerNames, sender); + game_list[gameName] = std::make_tuple(gameData, playerNames, sender); return; } recv_ingame(pkt, sender); diff --git a/Source/dvlnet/frame_queue.cpp b/Source/dvlnet/frame_queue.cpp index 73e321c01e8..c7eb663a034 100644 --- a/Source/dvlnet/frame_queue.cpp +++ b/Source/dvlnet/frame_queue.cpp @@ -5,6 +5,7 @@ #include "appfat.h" #include "dvlnet/packet.h" #include "utils/attributes.h" +#include "utils/endian.hpp" namespace devilution { namespace net { @@ -56,7 +57,7 @@ bool frame_queue::PacketReady() if (Size() < sizeof(framesize_t)) return false; auto szbuf = Read(sizeof(framesize_t)); - std::memcpy(&nextsize, &szbuf[0], sizeof(framesize_t)); + nextsize = LoadLE32(szbuf.data()); if (nextsize == 0) FRAME_QUEUE_ERROR; } @@ -77,8 +78,10 @@ buffer_t frame_queue::MakeFrame(buffer_t packetbuf) buffer_t ret; if (packetbuf.size() > max_frame_size) ABORT(); - framesize_t size = packetbuf.size(); - ret.insert(ret.end(), packet_out::begin(size), packet_out::end(size)); + const framesize_t size = packetbuf.size(); + unsigned char sizeBuf[4]; + WriteLE32(sizeBuf, size); + ret.insert(ret.end(), sizeBuf, sizeBuf + 4); ret.insert(ret.end(), packetbuf.begin(), packetbuf.end()); return ret; } diff --git a/Source/dvlnet/packet.h b/Source/dvlnet/packet.h index a306a422fe5..89e195a6162 100644 --- a/Source/dvlnet/packet.h +++ b/Source/dvlnet/packet.h @@ -40,7 +40,7 @@ typedef uint8_t plr_t; typedef uint8_t seq_t; typedef uint32_t cookie_t; typedef uint32_t timestamp_t; -typedef int leaveinfo_t; // also change later +typedef uint32_t leaveinfo_t; #ifdef PACKET_ENCRYPTION typedef std::array key_t; #else @@ -208,7 +208,13 @@ void packet_in::process_element(T &x) #else app_fatal("invalid packet"); #endif - std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + if (sizeof(T) == 4) { + x = static_cast(LoadLE32(decrypted_buffer.data())); + } else if (sizeof(T) == 2) { + x = static_cast(LoadLE16(decrypted_buffer.data())); + } else { + std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + } decrypted_buffer.erase(decrypted_buffer.begin(), decrypted_buffer.begin() + sizeof(T)); } @@ -360,7 +366,17 @@ inline void packet_out::process_element(buffer_t &x) template void packet_out::process_element(T &x) { - decrypted_buffer.insert(decrypted_buffer.end(), begin(x), end(x)); + if (sizeof(T) == 4) { + unsigned char buf[4]; + WriteLE32(buf, x); + decrypted_buffer.insert(decrypted_buffer.end(), buf, buf + 4); + } else if (sizeof(T) == 2) { + unsigned char buf[2]; + WriteLE16(buf, x); + decrypted_buffer.insert(decrypted_buffer.end(), buf, buf + 2); + } else { + std::memcpy(&x, decrypted_buffer.data(), sizeof(T)); + } } template diff --git a/Source/dvlnet/tcp_server.cpp b/Source/dvlnet/tcp_server.cpp index 6cdb8b1cef8..65277c4a9eb 100644 --- a/Source/dvlnet/tcp_server.cpp +++ b/Source/dvlnet/tcp_server.cpp @@ -214,7 +214,7 @@ void tcp_server::DropConnection(const scc &con) { if (con->plr != PLR_BROADCAST) { auto pkt = pktfty.make_packet(PLR_MASTER, PLR_BROADCAST, - con->plr, LEAVE_DROP); + con->plr, static_cast(LEAVE_DROP)); connections[con->plr] = nullptr; SendPacket(*pkt); // TODO: investigate if it is really ok for the server to diff --git a/Source/multi.cpp b/Source/multi.cpp index 32452f5851b..104aef313e4 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -46,7 +47,7 @@ bool sgbSendDeltaTbl[MAX_PLRS]; GameData sgGameInitInfo; bool gbSelectProvider; int sglTimeoutStart; -int sgdwPlayerLeftReasonTbl[MAX_PLRS]; +int32_t sgdwPlayerLeftReasonTbl[MAX_PLRS]; TBuffer lowPriorityBuffer; uint32_t sgdwGameLoops; /** @@ -74,6 +75,13 @@ const event_type EventTypes[3] = { EVENT_TYPE_PLAYER_MESSAGE }; +void GameData::SwapLE() +{ + size = SDL_SwapLE32(size); + dwSeed = SDL_SwapLE32(dwSeed); + programid = SDL_SwapLE32(programid); +} + namespace { constexpr uint16_t HeaderCheckVal = @@ -376,34 +384,44 @@ void SetupLocalPositions() void HandleEvents(_SNETEVENT *pEvt) { + const uint32_t playerId = pEvt->playerid; switch (pEvt->eventid) { case EVENT_TYPE_PLAYER_CREATE_GAME: { - auto *gameData = (GameData *)pEvt->data; - if (gameData->size != sizeof(GameData)) - app_fatal(StrCat("Invalid size of game data: ", gameData->size)); - sgGameInitInfo = *gameData; - sgbPlayerTurnBitTbl[pEvt->playerid] = true; - break; - } + GameData gameData; + if (pEvt->databytes < sizeof(GameData)) + app_fatal(StrCat("Invalid packet size (databytes)); + std::memcpy(&gameData, pEvt->data, sizeof(gameData)); + gameData.SwapLE(); + if (gameData.size != sizeof(GameData)) + app_fatal(StrCat("Invalid size of game data: ", gameData.size)); + sgGameInitInfo = gameData; + sgbPlayerTurnBitTbl[playerId] = true; + } break; case EVENT_TYPE_PLAYER_LEAVE_GAME: { - sgbPlayerLeftGameTbl[pEvt->playerid] = true; - sgbPlayerTurnBitTbl[pEvt->playerid] = false; + sgbPlayerLeftGameTbl[playerId] = true; + sgbPlayerTurnBitTbl[playerId] = false; - int leftReason = 0; - if (pEvt->data != nullptr && pEvt->databytes >= sizeof(leftReason)) - leftReason = *(int *)pEvt->data; - sgdwPlayerLeftReasonTbl[pEvt->playerid] = leftReason; + int32_t leftReason = 0; + if (pEvt->data != nullptr && pEvt->databytes >= sizeof(leftReason)) { + std::memcpy(&leftReason, pEvt->data, sizeof(leftReason)); + leftReason = SDL_SwapLE32(leftReason); + } + sgdwPlayerLeftReasonTbl[playerId] = leftReason; if (leftReason == LEAVE_ENDING) gbSomebodyWonGameKludge = true; - sgbSendDeltaTbl[pEvt->playerid] = false; + sgbSendDeltaTbl[playerId] = false; - if (gbDeltaSender == pEvt->playerid) + if (gbDeltaSender == playerId) gbDeltaSender = MAX_PLRS; } break; - case EVENT_TYPE_PLAYER_MESSAGE: - EventPlrMsg((char *)pEvt->data); - break; + case EVENT_TYPE_PLAYER_MESSAGE: { + string_view data(static_cast(pEvt->data), pEvt->databytes); + if (const size_t nullPos = data.find('\0'); nullPos != string_view::npos) { + data.remove_suffix(data.size() - nullPos); + } + EventPlrMsg(data); + } break; } } @@ -432,7 +450,9 @@ bool InitSingle(GameData *gameData) } int unused = 0; - if (!SNetCreateGame("local", "local", (char *)&sgGameInitInfo, sizeof(sgGameInitInfo), &unused)) { + GameData gameInitInfo = sgGameInitInfo; + gameInitInfo.SwapLE(); + if (!SNetCreateGame("local", "local", reinterpret_cast(&gameInitInfo), sizeof(gameInitInfo), &unused)) { app_fatal(StrCat("SNetCreateGame1:\n", SDL_GetError())); } diff --git a/Source/multi.h b/Source/multi.h index 0772828ee03..8f64ddf44bf 100644 --- a/Source/multi.h +++ b/Source/multi.h @@ -32,6 +32,8 @@ struct GameData { uint8_t bCowQuest; uint8_t bFriendlyFire; uint8_t fullQuests; + + void SwapLE(); }; /* @brief Contains info of running public game (for game list browsing) */ diff --git a/Source/storm/storm_net.hpp b/Source/storm/storm_net.hpp index 22a6ed81a7b..9a9d95b1aea 100644 --- a/Source/storm/storm_net.hpp +++ b/Source/storm/storm_net.hpp @@ -36,10 +36,10 @@ struct _SNETCAPS { }; struct _SNETEVENT { - uint32_t eventid; - uint32_t playerid; - void *data; - uint32_t databytes; + uint32_t eventid; // native-endian + uint32_t playerid; // native-endian + void *data; // little-endian + uint32_t databytes; // native-endian }; #define PS_CONNECTED 0x10000