Skip to content
Open
Show file tree
Hide file tree
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
23 changes: 22 additions & 1 deletion Generals/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,28 @@ 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)
{
constexpr WideChar IllegalNameChars[] = L",:;|\f\n\r\t\v";
Copy link

@xezon xezon Sep 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the pipe character disallowed? I created a network match and was able to match 2 players with pipes (before this change). The pipe character is popular to separate clan names.

shot_20250920_112024_1

shot_20250920_112127_2

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one was included via a suggestion, any thoughts on this @roossienb?.
I think it would be OK to allow pipe for the parsing functionality at least - as far as I know it's ,:; that would mess up the string splitting.

Copy link

@xezon xezon Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

roossienb asked if pipe was a problem. It is not after testing it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of banning \f\n\r\t\v, how about banning all characters less than 32 ? That includes all control characters.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that makes sense. As these are wide chars, are there other values or sequences beyond the traditional ASCII control characters that we should consider as well?

Copy link

@xezon xezon Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs testing. I just tested Network lobby with nickname öäü and it triggered Asserts in Debug, but otherwise worked.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, testing is definitely needed here.
I was thinking of things like Surrogate pairs and Unicode special characters that will require a lot more validation/handling that what is proposed in this PR right now.
Should we limit names to only characters in the BMP for example? Require the string to be normalized to a specific normalization form?

const Bool containsIllegalChars = wcscspn(msg->name, IllegalNameChars);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can simplify further to wcscspn(msg->name, L",:;|\f\n\r\t\v");

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also be... != wcslen(msg->name)

const Bool isEffectivelyEmpty = wcsspn(msg->name, L" ") == wcslen(msg->name);
if (containsIllegalChars || isEffectivelyEmpty)
{
// Just deny with a duplicate name reason, for backwards compatibility with retail
reply.LANMessageType = 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."));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code only affects LAN lobby, correct? If so, does this need to be replicated anywhere in Online Lobby code?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen the online lobby code use the string representation of the GameInfo, which is the root of this problem. On the other hand I haven't looked really closely at the online lobby code either.
There might be 2 cases here:

  • Excluding ;:, because it causes trouble for the string parsing. If the online lobby does not use string parsing it strictly does not need to be replicated, other than maybe for consistency across the two lobbies.
  • Control characters and maybe other troublesome characters that could cause issues in the UI or other representations. These ones I think would be beneficial to replicate.

}
}

// 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
23 changes: 22 additions & 1 deletion GeneralsMD/Code/GameEngine/Source/GameNetwork/LANAPIhandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,28 @@ 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)
{
constexpr WideChar IllegalNameChars[] = L",:;|\f\n\r\t\v";
const Bool containsIllegalChars = wcscspn(msg->name, IllegalNameChars);
const Bool isEffectivelyEmpty = wcsspn(msg->name, L" ") == wcslen(msg->name);
if (containsIllegalChars || isEffectivelyEmpty)
{
// Just deny with a duplicate name reason, for backwards compatibility with retail
reply.LANMessageType = 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