From 3c3c5f245226fedcb6ff125293a3d46552e761f4 Mon Sep 17 00:00:00 2001 From: Slurmlord Date: Thu, 18 Sep 2025 13:15:31 +0200 Subject: [PATCH 1/2] bugfix(network): Deny players with invalid names to join game --- .../Source/GameNetwork/LANAPIhandlers.cpp | 65 ++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 0e5675b5c95..2a98fc188dc 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -202,6 +202,51 @@ void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP ) } } +static const Bool IsInvalidCharForPlayerName(const WideChar c) +{ + return c < L' ' // C0 control chars + || c == L',' || c == L':' || c == L';' // chars used for strtok in ParseAsciiStringToGameInfo + || (c >= L'\x007f' && c <= L'\x009f') // DEL + C1 control chars + || c == L'\x2028' || c == L'\x2029' // line and paragraph separators + || (c >= L'\xdc00' && c <= L'\xdfff') // low surrogate, for chars beyond the Unicode Basic Multilingual Plane + || (c >= L'\xd800' && c <= L'\xdbff'); // high surrogate, for chars beyond the BMP +} + +static const Bool IsSpaceCharacter(const WideChar c) +{ + return c == L' ' // space + || c == L'\xA0' // no-break space + || c == L'\x1680' // ogham space mark + || (c >= L'\x2000' && c <= L'\x200A') // en/em spaces, figure, punctuation, thin, hair + || c == L'\x202F' // narrow no-break space + || c == L'\x205F' // medium mathematical space + || c == L'\x3000'; // ideographic space +} + +static const Bool ContainsInvalidChars(const WideChar* playerName) +{ + DEBUG_ASSERTCRASH(playerName != NULL, ("playerName is NULL")); + while (*playerName) + { + if (IsInvalidCharForPlayerName(*playerName++)) + return true; + } + + return false; +} + +static const Bool ContainsAnyReadableChars(const WideChar* playerName) +{ + DEBUG_ASSERTCRASH(playerName != NULL, ("playerName is NULL")); + while (*playerName) + { + if (!IsSpaceCharacter(*playerName++)) + return true; + } + + return false; +} + void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) { UnsignedInt responseIP = senderIP; // need this cause the player may or may not be @@ -290,7 +335,25 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP ) } #endif - // We're the host, so see if he has a duplicate name + // TheSuperHackers @bugfix slurmlord 18/09/2025 need to validate the name of the connecting player before + // allowing them to join to prevent messing up the format of game state string. Commas, colons, semicolons etc. + // should not be in a player name. It should also not consist of only space characters. + if (canJoin) + { + if (ContainsInvalidChars(msg->name) || !ContainsAnyReadableChars(msg->name)) + { + // Just deny with a duplicate name reason, for backwards compatibility with retail + reply.messageType = LANMessage::MSG_JOIN_DENY; + reply.GameNotJoined.reason = LANAPIInterface::RET_DUPLICATE_NAME; + reply.GameNotJoined.gameIP = m_localIP; + reply.GameNotJoined.playerIP = senderIP; + canJoin = false; + + DEBUG_LOG(("LANAPI::handleRequestJoin - join denied because of illegal characters in the player name.")); + } + } + + // Then see if the player has a duplicate name for (player = 0; canJoin && playergetLANSlot(player); From 1513ed8cb55b0cd7dbe50b3fc407f5eab08ec1b6 Mon Sep 17 00:00:00 2001 From: Slurmlord Date: Thu, 13 Nov 2025 23:18:27 +0100 Subject: [PATCH 2/2] Remove over-eager consts --- Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp index 2a98fc188dc..3c79e60cac5 100644 --- a/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp +++ b/Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp @@ -202,7 +202,7 @@ void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP ) } } -static const Bool IsInvalidCharForPlayerName(const WideChar c) +static Bool IsInvalidCharForPlayerName(const WideChar c) { return c < L' ' // C0 control chars || c == L',' || c == L':' || c == L';' // chars used for strtok in ParseAsciiStringToGameInfo @@ -212,7 +212,7 @@ static const Bool IsInvalidCharForPlayerName(const WideChar c) || (c >= L'\xd800' && c <= L'\xdbff'); // high surrogate, for chars beyond the BMP } -static const Bool IsSpaceCharacter(const WideChar c) +static Bool IsSpaceCharacter(const WideChar c) { return c == L' ' // space || c == L'\xA0' // no-break space @@ -223,7 +223,7 @@ static const Bool IsSpaceCharacter(const WideChar c) || c == L'\x3000'; // ideographic space } -static const Bool ContainsInvalidChars(const WideChar* playerName) +static Bool ContainsInvalidChars(const WideChar* playerName) { DEBUG_ASSERTCRASH(playerName != NULL, ("playerName is NULL")); while (*playerName) @@ -235,7 +235,7 @@ static const Bool ContainsInvalidChars(const WideChar* playerName) return false; } -static const Bool ContainsAnyReadableChars(const WideChar* playerName) +static Bool ContainsAnyReadableChars(const WideChar* playerName) { DEBUG_ASSERTCRASH(playerName != NULL, ("playerName is NULL")); while (*playerName)