Merge pull request #829 from ctrlaltca/antiflood
Anti flood for game commands; fix #753
This commit is contained in:
commit
88b242b34d
8 changed files with 69 additions and 8 deletions
|
@ -837,6 +837,8 @@ void TabGame::sendGameCommand(PendingCommand *pend, int playerId)
|
||||||
AbstractClient *client = getClientForPlayer(playerId);
|
AbstractClient *client = getClientForPlayer(playerId);
|
||||||
if (!client)
|
if (!client)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(commandFinished(const Response &)));
|
||||||
client->sendCommand(pend);
|
client->sendCommand(pend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,7 +847,16 @@ void TabGame::sendGameCommand(const google::protobuf::Message &command, int play
|
||||||
AbstractClient *client = getClientForPlayer(playerId);
|
AbstractClient *client = getClientForPlayer(playerId);
|
||||||
if (!client)
|
if (!client)
|
||||||
return;
|
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)
|
PendingCommand *TabGame::prepareGameCommand(const ::google::protobuf::Message &cmd)
|
||||||
|
|
|
@ -198,6 +198,7 @@ private slots:
|
||||||
void actNextTurn();
|
void actNextTurn();
|
||||||
|
|
||||||
void addMentionTag(QString value);
|
void addMentionTag(QString value);
|
||||||
|
void commandFinished(const Response &response);
|
||||||
public:
|
public:
|
||||||
TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, const Event_GameJoined &event, const QMap<int, QString> &_roomGameTypes);
|
TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, const Event_GameJoined &event, const QMap<int, QString> &_roomGameTypes);
|
||||||
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
|
TabGame(TabSupervisor *_tabSupervisor, GameReplay *replay);
|
||||||
|
|
|
@ -60,8 +60,11 @@ public:
|
||||||
virtual int getMaxMessageCountPerInterval() const { return 0; }
|
virtual int getMaxMessageCountPerInterval() const { return 0; }
|
||||||
virtual int getMaxMessageSizePerInterval() const { return 0; }
|
virtual int getMaxMessageSizePerInterval() const { return 0; }
|
||||||
virtual int getMaxGamesPerUser() 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; }
|
virtual bool getThreaded() const { return false; }
|
||||||
|
|
||||||
Server_DatabaseInterface *getDatabaseInterface() const;
|
Server_DatabaseInterface *getDatabaseInterface() const;
|
||||||
int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; }
|
int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; }
|
||||||
|
|
||||||
|
|
|
@ -184,6 +184,18 @@ Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const
|
||||||
|
|
||||||
Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc)
|
Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc)
|
||||||
{
|
{
|
||||||
|
static QList<GameCommand::GameCommandType> antifloodCommandsWhiteList = QList<GameCommand::GameCommandType>()
|
||||||
|
// 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)
|
if (authState == NotLoggedIn)
|
||||||
return Response::RespLoginNeeded;
|
return Response::RespLoginNeeded;
|
||||||
|
|
||||||
|
@ -217,12 +229,29 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const
|
||||||
if (!player)
|
if (!player)
|
||||||
return Response::RespNotInRoom;
|
return Response::RespNotInRoom;
|
||||||
|
|
||||||
|
int commandCountingInterval = server->getCommandCountingInterval();
|
||||||
|
int maxMessageCountPerInterval = server->getMaxMessageCountPerInterval();
|
||||||
GameEventStorage ges;
|
GameEventStorage ges;
|
||||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||||
for (int i = cont.game_command_size() - 1; i >= 0; --i) {
|
for (int i = cont.game_command_size() - 1; i >= 0; --i) {
|
||||||
const GameCommand &sc = cont.game_command(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()));
|
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);
|
||||||
|
|
||||||
|
if(!antifloodCommandsWhiteList.contains((GameCommand::GameCommandType) getPbExtension(sc)))
|
||||||
|
++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);
|
Response::ResponseCode resp = player->processGameCommand(sc, rc, ges);
|
||||||
|
|
||||||
if (resp != Response::RespOk)
|
if (resp != Response::RespOk)
|
||||||
|
@ -314,7 +343,14 @@ void Server_ProtocolHandler::pingClockTimeout()
|
||||||
if (messageCountOverTime.size() > server->getMessageCountingInterval())
|
if (messageCountOverTime.size() > server->getMessageCountingInterval())
|
||||||
messageCountOverTime.removeLast();
|
messageCountOverTime.removeLast();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interval = server->getCommandCountingInterval();
|
||||||
|
if (interval > 0) {
|
||||||
|
commandCountOverTime.prepend(0);
|
||||||
|
if (commandCountOverTime.size() > server->getCommandCountingInterval())
|
||||||
|
commandCountOverTime.removeLast();
|
||||||
|
}
|
||||||
|
|
||||||
if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime())
|
if (timeRunning - lastDataReceived > server->getMaxPlayerInactivityTime())
|
||||||
prepareDestroy();
|
prepareDestroy();
|
||||||
++timeRunning;
|
++timeRunning;
|
||||||
|
|
|
@ -51,7 +51,7 @@ protected:
|
||||||
bool acceptsRoomListChanges;
|
bool acceptsRoomListChanges;
|
||||||
virtual void logDebugMessage(const QString & /* message */) { }
|
virtual void logDebugMessage(const QString & /* message */) { }
|
||||||
private:
|
private:
|
||||||
QList<int> messageSizeOverTime, messageCountOverTime;
|
QList<int> messageSizeOverTime, messageCountOverTime, commandCountOverTime;
|
||||||
int timeRunning, lastDataReceived;
|
int timeRunning, lastDataReceived;
|
||||||
QTimer *pingClock;
|
QTimer *pingClock;
|
||||||
|
|
||||||
|
|
|
@ -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"
|
; 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"
|
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
|
; This setting defines the length in seconds of the considered interval; default is 10
|
||||||
message_counting_interval=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
|
; Maximum number of games a single user can create; default is 5
|
||||||
max_games_per_user=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]
|
[logging]
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,8 @@ bool Servatrice::initServer()
|
||||||
maxMessageCountPerInterval = settingsCache->value("security/max_message_count_per_interval", 10).toInt();
|
maxMessageCountPerInterval = settingsCache->value("security/max_message_count_per_interval", 10).toInt();
|
||||||
maxMessageSizePerInterval = settingsCache->value("security/max_message_size_per_interval", 1000).toInt();
|
maxMessageSizePerInterval = settingsCache->value("security/max_message_size_per_interval", 1000).toInt();
|
||||||
maxGamesPerUser = settingsCache->value("security/max_games_per_user", 5).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()) {
|
try { if (settingsCache->value("servernetwork/active", 0).toInt()) {
|
||||||
qDebug() << "Connecting to ISL network.";
|
qDebug() << "Connecting to ISL network.";
|
||||||
|
|
|
@ -115,8 +115,8 @@ private:
|
||||||
QMutex txBytesMutex, rxBytesMutex;
|
QMutex txBytesMutex, rxBytesMutex;
|
||||||
quint64 txBytes, rxBytes;
|
quint64 txBytes, rxBytes;
|
||||||
int maxGameInactivityTime, maxPlayerInactivityTime;
|
int maxGameInactivityTime, maxPlayerInactivityTime;
|
||||||
int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser;
|
int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser, commandCountingInterval, maxCommandCountPerInterval;
|
||||||
|
|
||||||
QString shutdownReason;
|
QString shutdownReason;
|
||||||
int shutdownMinutes;
|
int shutdownMinutes;
|
||||||
QTimer *shutdownTimer;
|
QTimer *shutdownTimer;
|
||||||
|
@ -143,6 +143,8 @@ public:
|
||||||
int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; }
|
int getMaxMessageCountPerInterval() const { return maxMessageCountPerInterval; }
|
||||||
int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; }
|
int getMaxMessageSizePerInterval() const { return maxMessageSizePerInterval; }
|
||||||
int getMaxGamesPerUser() const { return maxGamesPerUser; }
|
int getMaxGamesPerUser() const { return maxGamesPerUser; }
|
||||||
|
int getCommandCountingInterval() const { return commandCountingInterval; }
|
||||||
|
int getMaxCommandCountPerInterval() const { return maxCommandCountPerInterval; }
|
||||||
AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; }
|
AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; }
|
||||||
QString getDbPrefix() const { return dbPrefix; }
|
QString getDbPrefix() const { return dbPrefix; }
|
||||||
int getServerId() const { return serverId; }
|
int getServerId() const { return serverId; }
|
||||||
|
|
Loading…
Reference in a new issue