Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion Core/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,51 @@ void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP )
}
}

static 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 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 Bool ContainsInvalidChars(const WideChar* playerName)
{
DEBUG_ASSERTCRASH(playerName != NULL, ("playerName is NULL"));
while (*playerName)
{
if (IsInvalidCharForPlayerName(*playerName++))
return true;
}

return false;
}

static 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
Expand Down Expand Up @@ -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 && player<MAX_SLOTS; ++player)
{
LANGameSlot *slot = m_currentGame->getLANSlot(player);
Expand Down
Loading