From 0e437cf13b2f8316e6b7b47ff8f16ce212172e11 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sat, 14 Mar 2015 19:44:32 +0100 Subject: [PATCH 1/5] Anti flood for game commands --- common/server.h | 5 ++++- common/server_protocolhandler.cpp | 25 +++++++++++++++++++++++-- common/server_protocolhandler.h | 2 +- servatrice/servatrice.ini.example | 8 +++++++- servatrice/src/servatrice.cpp | 2 ++ servatrice/src/servatrice.h | 6 ++++-- 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/common/server.h b/common/server.h index 81088622..896f6ef8 100644 --- a/common/server.h +++ b/common/server.h @@ -60,8 +60,11 @@ public: virtual int getMaxMessageCountPerInterval() const { return 0; } virtual int getMaxMessageSizePerInterval() const { return 0; } virtual int getMaxGamesPerUser() const { return 0; } + virtual int getCommandCountingInterval() const { return 0; } + virtual int getMaxCommandCountPerInterval() const { return 0; } + virtual bool getThreaded() const { return false; } - + Server_DatabaseInterface *getDatabaseInterface() const; int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; } diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 07f3fd0f..a7ec3ade 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -217,12 +217,26 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const if (!player) return Response::RespNotInRoom; + int commandCountingInterval = server->getCommandCountingInterval(); + int maxMessageCountPerInterval = server->getMaxMessageCountPerInterval(); GameEventStorage ges; Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.game_command_size() - 1; i >= 0; --i) { const GameCommand &sc = cont.game_command(i); logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) + QString::fromStdString(sc.ShortDebugString())); - + + if (commandCountingInterval > 0) { + int totalCount = 0; + if (commandCountOverTime.isEmpty()) + commandCountOverTime.prepend(0); + ++commandCountOverTime[0]; + for (int i = 0; i < commandCountOverTime.size(); ++i) + totalCount += commandCountOverTime[i]; + + if (totalCount > maxMessageCountPerInterval) + return Response::RespChatFlood; + } + Response::ResponseCode resp = player->processGameCommand(sc, rc, ges); if (resp != Response::RespOk) @@ -314,7 +328,14 @@ void Server_ProtocolHandler::pingClockTimeout() if (messageCountOverTime.size() > server->getMessageCountingInterval()) messageCountOverTime.removeLast(); } - + + interval = server->getCommandCountingInterval(); + if (interval > 0) { + commandCountOverTime.prepend(0); + if (commandCountOverTime.size() > server->getCommandCountingInterval()) + commandCountOverTime.removeLast(); + } + if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime()) prepareDestroy(); ++timeRunning; diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 98e0f6cc..5389b724 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -51,7 +51,7 @@ protected: bool acceptsRoomListChanges; virtual void logDebugMessage(const QString & /* message */) { } private: - QList messageSizeOverTime, messageCountOverTime; + QList messageSizeOverTime, messageCountOverTime, commandCountOverTime; int timeRunning, lastDataReceived; QTimer *pingClock; diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index c4454745..52c1f61b 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -130,7 +130,7 @@ max_users_per_address=4 ; IP addresses listed (ignoring the max_users_per_address). Default is "127.0.0.1,::1"; example: "192.73.233.244,81.4.100.74" trusted_sources="127.0.0.1,::1" -; Servatrice can avoid users from flooding rooms with large number messages in an interval of time. +; Servatrice can avoid users from flooding rooms with large number of messages in an interval of time. ; This setting defines the length in seconds of the considered interval; default is 10 message_counting_interval=10 @@ -143,6 +143,12 @@ max_message_count_per_interval=10 ; Maximum number of games a single user can create; default is 5 max_games_per_user=5 +; Servatrice can avoid users from flooding games with large number of game commands in an interval of time. +; This setting defines the length in seconds of the considered interval; default is 10 +command_counting_interval=10 + +; Maximum number of game commands in an interval before new commands gets dropped; default is 10 +max_command_count_per_interval=10 [logging] diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index b2c957b3..2e730a22 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -259,6 +259,8 @@ bool Servatrice::initServer() maxMessageCountPerInterval = settingsCache->value("security/max_message_count_per_interval", 10).toInt(); maxMessageSizePerInterval = settingsCache->value("security/max_message_size_per_interval", 1000).toInt(); maxGamesPerUser = settingsCache->value("security/max_games_per_user", 5).toInt(); + commandCountingInterval = settingsCache->value("game/command_counting_interval", 10).toInt(); + maxCommandCountPerInterval = settingsCache->value("game/max_command_count_per_interval", 10).toInt(); try { if (settingsCache->value("servernetwork/active", 0).toInt()) { qDebug() << "Connecting to ISL network."; diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index baa2e6e8..e7e548d1 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -115,8 +115,8 @@ private: QMutex txBytesMutex, rxBytesMutex; quint64 txBytes, rxBytes; int maxGameInactivityTime, maxPlayerInactivityTime; - int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser; - + int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval; + QString shutdownReason; int shutdownMinutes; QTimer *shutdownTimer; @@ -143,6 +143,8 @@ public: int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; } int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; } int getMaxGamesPerUser() const { return maxGamesPerUser; } + int getCommandCountingInterval() const { return commandCountingInterval; } + int getMaxCommandCountPerInterval() const { return maxCommandCountPerInterval; } AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; } QString getDbPrefix() const { return dbPrefix; } int getServerId() const { return serverId; } From 8be55635632220318c83ed27b596af53db7c6e07 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 19 Mar 2015 10:10:19 +0100 Subject: [PATCH 2/5] Added user notification for RespChatFlood --- cockatrice/src/tab_game.cpp | 13 ++++++++++++- cockatrice/src/tab_game.h | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 92c50c1b..85fa44a3 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -837,6 +837,8 @@ void TabGame::sendGameCommand(PendingCommand *pend, int playerId) AbstractClient *client = getClientForPlayer(playerId); if (!client) return; + + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(commandFinished(const Response &))); client->sendCommand(pend); } @@ -845,7 +847,16 @@ void TabGame::sendGameCommand(const google::protobuf::Message &command, int play AbstractClient *client = getClientForPlayer(playerId); if (!client) return; - client->sendCommand(prepareGameCommand(command)); + + PendingCommand *pend = prepareGameCommand(command); + connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(commandFinished(const Response &))); + client->sendCommand(pend); +} + +void TabGame::commandFinished(const Response &response) +{ + if (response.response_code() == Response::RespChatFlood) + messageLog->appendMessage(tr("You are flooding the game. Please wait a couple of seconds.")); } PendingCommand *TabGame::prepareGameCommand(const ::google::protobuf::Message &cmd) diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index 1b9bff5f..cd2fcb0f 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -198,6 +198,7 @@ private slots: void actNextTurn(); void addMentionTag(QString value); + void commandFinished(const Response &response); public: TabGame(TabSupervisor *_tabSupervisor, QList &_clients, const Event_GameJoined &event, const QMap &_roomGameTypes); TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay); From 5463255516af4cbc777e22a75590b6fd7cb1fb21 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 19 Mar 2015 10:50:46 +0100 Subject: [PATCH 3/5] Added whitelist for game commands --- common/server_protocolhandler.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index a7ec3ade..2515fad6 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -184,6 +184,18 @@ Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { + static QList antifloodCommandsWhiteList = QList() + // draw, undraw cards (eg: drawing 10 cards one by one from the deck) + << GameCommand::DRAW_CARDS + << GameCommand::UNDO_DRAW + // create, delete arrows (eg: targeting with 10 cards during an attack) + << GameCommand::CREATE_ARROW + << GameCommand::DELETE_ARROW + // set card attributes (eg: tapping 10 cards at once) + << GameCommand::SET_CARD_ATTR + // increment / decrement counter (eg: -10 lifepoints one by one) + << GameCommand::INC_COUNTER; + if (authState == NotLoggedIn) return Response::RespLoginNeeded; @@ -229,7 +241,10 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const int totalCount = 0; if (commandCountOverTime.isEmpty()) commandCountOverTime.prepend(0); - ++commandCountOverTime[0]; + + if(!antifloodCommandsWhiteList.contains((GameCommand::GameCommandType) getPbExtension(sc))) + ++commandCountOverTime[0]; + for (int i = 0; i < commandCountOverTime.size(); ++i) totalCount += commandCountOverTime[i]; From 4568adb498956bcdcd5545eb3e994d51500e0fb6 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Thu, 19 Mar 2015 10:54:01 +0100 Subject: [PATCH 4/5] Updated translation file --- cockatrice/translations/cockatrice_en.ts | 66 +++++++++++++----------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index e506fba1..78f65a58 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -1776,16 +1776,6 @@ Local version is %1, remote version is %2. &Help - - - Are you sure? - - - - - There are still open games. Are you sure you want to quit? - - MessageLogWidget @@ -3609,7 +3599,7 @@ Local version is %1, remote version is %2. - + Number: @@ -3634,27 +3624,27 @@ Local version is %1, remote version is %2. - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters @@ -4074,42 +4064,42 @@ Local version is %1, remote version is %2. - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4342,17 +4332,22 @@ Please enter a name: - + + You are flooding the game. Please wait a couple of seconds. + + + + You have been kicked out of the game. - + Replay %1: %2 - + Game %1: %2 @@ -4496,6 +4491,19 @@ Please enter a name: + + TabSupervisor + + + Are you sure? + + + + + There are still open games. Are you sure you want to quit? + + + TabUserLists From 7c2db752a5e2753aa6a3bc970b62b4f5f1a37d8a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 22 Mar 2015 21:51:38 +0100 Subject: [PATCH 5/5] Revert "Updated translation file" This reverts commit 4568adb498956bcdcd5545eb3e994d51500e0fb6. --- cockatrice/translations/cockatrice_en.ts | 66 +++++++++++------------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/cockatrice/translations/cockatrice_en.ts b/cockatrice/translations/cockatrice_en.ts index 78f65a58..e506fba1 100644 --- a/cockatrice/translations/cockatrice_en.ts +++ b/cockatrice/translations/cockatrice_en.ts @@ -1776,6 +1776,16 @@ Local version is %1, remote version is %2. &Help + + + Are you sure? + + + + + There are still open games. Are you sure you want to quit? + + MessageLogWidget @@ -3599,7 +3609,7 @@ Local version is %1, remote version is %2. - + Number: @@ -3624,27 +3634,27 @@ Local version is %1, remote version is %2. - + Set power/toughness - + Please enter the new PT: - + Set annotation - + Please enter the new annotation: - + Set counters @@ -4064,42 +4074,42 @@ Local version is %1, remote version is %2. - + Are you sure? - + The decklist has been modified. Do you want to save the changes? - + Load deck - - - + + + Error - + The deck could not be saved. - - + + The deck could not be saved. Please check that the directory is writable and try again. - + Save deck @@ -4332,22 +4342,17 @@ Please enter a name: - - You are flooding the game. Please wait a couple of seconds. - - - - + You have been kicked out of the game. - + Replay %1: %2 - + Game %1: %2 @@ -4491,19 +4496,6 @@ Please enter a name: - - TabSupervisor - - - Are you sure? - - - - - There are still open games. Are you sure you want to quit? - - - TabUserLists