@@ -202,6 +202,51 @@ void LANAPI::handleRequestGameInfo( LANMessage *msg, UnsignedInt senderIP )
202202 }
203203}
204204
205+ static Bool IsInvalidCharForPlayerName (const WideChar c)
206+ {
207+ return c < L' ' // C0 control chars
208+ || c == L' ,' || c == L' :' || c == L' ;' // chars used for strtok in ParseAsciiStringToGameInfo
209+ || (c >= L' \x00 7f' && c <= L' \x00 9f' ) // DEL + C1 control chars
210+ || c == L' \x20 28' || c == L' \x20 29' // line and paragraph separators
211+ || (c >= L' \xdc 00' && c <= L' \xdf ff' ) // low surrogate, for chars beyond the Unicode Basic Multilingual Plane
212+ || (c >= L' \xd8 00' && c <= L' \xdb ff' ); // high surrogate, for chars beyond the BMP
213+ }
214+
215+ static Bool IsSpaceCharacter (const WideChar c)
216+ {
217+ return c == L' ' // space
218+ || c == L' \xA0 ' // no-break space
219+ || c == L' \x16 80' // ogham space mark
220+ || (c >= L' \x20 00' && c <= L' \x20 0A' ) // en/em spaces, figure, punctuation, thin, hair
221+ || c == L' \x20 2F' // narrow no-break space
222+ || c == L' \x20 5F' // medium mathematical space
223+ || c == L' \x30 00' ; // ideographic space
224+ }
225+
226+ static Bool ContainsInvalidChars (const WideChar* playerName)
227+ {
228+ DEBUG_ASSERTCRASH (playerName != NULL , (" playerName is NULL" ));
229+ while (*playerName)
230+ {
231+ if (IsInvalidCharForPlayerName (*playerName++))
232+ return true ;
233+ }
234+
235+ return false ;
236+ }
237+
238+ static Bool ContainsAnyReadableChars (const WideChar* playerName)
239+ {
240+ DEBUG_ASSERTCRASH (playerName != NULL , (" playerName is NULL" ));
241+ while (*playerName)
242+ {
243+ if (!IsSpaceCharacter (*playerName++))
244+ return true ;
245+ }
246+
247+ return false ;
248+ }
249+
205250void LANAPI::handleRequestJoin ( LANMessage *msg, UnsignedInt senderIP )
206251{
207252 UnsignedInt responseIP = senderIP; // need this cause the player may or may not be
@@ -290,7 +335,25 @@ void LANAPI::handleRequestJoin( LANMessage *msg, UnsignedInt senderIP )
290335 }
291336#endif
292337
293- // We're the host, so see if he has a duplicate name
338+ // TheSuperHackers @bugfix slurmlord 18/09/2025 need to validate the name of the connecting player before
339+ // allowing them to join to prevent messing up the format of game state string. Commas, colons, semicolons etc.
340+ // should not be in a player name. It should also not consist of only space characters.
341+ if (canJoin)
342+ {
343+ if (ContainsInvalidChars (msg->name ) || !ContainsAnyReadableChars (msg->name ))
344+ {
345+ // Just deny with a duplicate name reason, for backwards compatibility with retail
346+ reply.messageType = LANMessage::MSG_JOIN_DENY;
347+ reply.GameNotJoined .reason = LANAPIInterface::RET_DUPLICATE_NAME;
348+ reply.GameNotJoined .gameIP = m_localIP;
349+ reply.GameNotJoined .playerIP = senderIP;
350+ canJoin = false ;
351+
352+ DEBUG_LOG ((" LANAPI::handleRequestJoin - join denied because of illegal characters in the player name." ));
353+ }
354+ }
355+
356+ // Then see if the player has a duplicate name
294357 for (player = 0 ; canJoin && player<MAX_SLOTS; ++player)
295358 {
296359 LANGameSlot *slot = m_currentGame->getLANSlot (player);
0 commit comments