196 lines
5.0 KiB
Diff
196 lines
5.0 KiB
Diff
|
From 44b9004d2c208b42c6f8ffa99938361e31f5a071
|
||
|
From: MadMaurice <madmaurice@zom.bi>
|
||
|
Date: Thu Aug 30 15:08:01 2018 +0200
|
||
|
|
||
|
Prevent instability and crash due to message flood
|
||
|
|
||
|
This patch adds a rate limiting to selected patches. The underlying rate limiter
|
||
|
used is the Leaky-Bucket algorithm. It allows for a burst of messages, but
|
||
|
limits them after a specified amount of messages within a time frame.
|
||
|
|
||
|
From: Ferdinand Thiessen <rpm@fthiessen.de>
|
||
|
|
||
|
Modified this diff, to make it work with 1.2.19 tarball.
|
||
|
"Backported" by manually change the 1.2.19 version according to the original diff.
|
||
|
|
||
|
diff -Nur mumble-1.2.19/src/murmur/Messages.cpp new/src/murmur/Messages.cpp
|
||
|
--- mumble-1.2.19/src/murmur/Messages.cpp 2017-01-27 07:48:33.000000000 +0100
|
||
|
+++ new/src/murmur/Messages.cpp 2019-07-13 00:45:48.281780195 +0200
|
||
|
@@ -42,6 +42,11 @@
|
||
|
#include "ServerUser.h"
|
||
|
#include "Version.h"
|
||
|
|
||
|
+#define RATELIMIT(user) \
|
||
|
+ if (user->leakyBucket.ratelimit(1)) { \
|
||
|
+ return; \
|
||
|
+ }
|
||
|
+
|
||
|
#define MSG_SETUP(st) \
|
||
|
if (uSource->sState != st) { \
|
||
|
return; \
|
||
|
@@ -491,6 +496,10 @@
|
||
|
msg.set_session(pDstServerUser->uiSession);
|
||
|
msg.set_actor(uSource->uiSession);
|
||
|
|
||
|
+ if (uSource == pDstServerUser) {
|
||
|
+ RATELIMIT(uSource);
|
||
|
+ }
|
||
|
+
|
||
|
if (msg.has_channel_id()) {
|
||
|
Channel *c = qhChannels.value(msg.channel_id());
|
||
|
if (!c || (c == pDstServerUser->cChannel))
|
||
|
@@ -798,6 +807,8 @@
|
||
|
p = qhChannels.value(msg.parent());
|
||
|
if (! p)
|
||
|
return;
|
||
|
+ } else {
|
||
|
+ RATELIMIT(uSource);
|
||
|
}
|
||
|
|
||
|
msg.clear_links();
|
||
|
@@ -1074,6 +1085,8 @@
|
||
|
QSet<ServerUser *> users;
|
||
|
QQueue<Channel *> q;
|
||
|
|
||
|
+ RATELIMIT(uSource);
|
||
|
+
|
||
|
QString text = u8(msg.message());
|
||
|
bool changed = false;
|
||
|
|
||
|
@@ -1176,6 +1189,8 @@
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
+ RATELIMIT(uSource);
|
||
|
+
|
||
|
if (msg.has_query() && msg.query()) {
|
||
|
QStack<Channel *> chans;
|
||
|
Channel *p;
|
||
|
@@ -1417,6 +1432,8 @@
|
||
|
}
|
||
|
|
||
|
void Server::msgVersion(ServerUser *uSource, MumbleProto::Version &msg) {
|
||
|
+ RATELIMIT(uSource);
|
||
|
+
|
||
|
if (msg.has_version())
|
||
|
uSource->uiVersion=msg.version();
|
||
|
if (msg.has_release())
|
||
|
diff -Nur mumble-1.2.19/src/murmur/ServerUser.cpp new/src/murmur/ServerUser.cpp
|
||
|
--- mumble-1.2.19/src/murmur/ServerUser.cpp 2017-01-27 07:48:33.000000000 +0100
|
||
|
+++ new/src/murmur/ServerUser.cpp 2019-07-13 00:47:25.974498227 +0200
|
||
|
@@ -128,3 +128,61 @@
|
||
|
return static_cast<int>((sum * 1000000ULL) / elapsed);
|
||
|
}
|
||
|
|
||
|
+#if __cplusplus > 199711LL
|
||
|
+
|
||
|
+inline static
|
||
|
+time_point now() {
|
||
|
+ return std::chrono::steady_clock::now();
|
||
|
+}
|
||
|
+
|
||
|
+inline static
|
||
|
+unsigned long millisecondsBetween(time_point start, time_point end) {
|
||
|
+ return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
|
||
|
+}
|
||
|
+
|
||
|
+#else
|
||
|
+
|
||
|
+inline static
|
||
|
+time_point now() {
|
||
|
+ return clock();
|
||
|
+}
|
||
|
+
|
||
|
+inline static
|
||
|
+unsigned long millisecondsBetween(time_point start, time_point end) {
|
||
|
+ return 1000 * (end - start) / CLOCKS_PER_SEC;
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
+// Rate limiting: burst up to 30, 4 message per sec limit over longer time
|
||
|
+LeakyBucket::LeakyBucket() : tokensPerSec(4), maxTokens(30), currentTokens(0) {
|
||
|
+ lastUpdate = now();
|
||
|
+}
|
||
|
+
|
||
|
+bool LeakyBucket::ratelimit(int tokens) {
|
||
|
+ // First remove tokens we leaked over time
|
||
|
+ time_point tnow = now();
|
||
|
+ long ms = millisecondsBetween(lastUpdate, tnow);
|
||
|
+
|
||
|
+ long drainTokens = (ms * tokensPerSec) / 1000;
|
||
|
+
|
||
|
+ // Prevent constant starvation due to too many updates
|
||
|
+ if (drainTokens > 0) {
|
||
|
+ this->lastUpdate = tnow;
|
||
|
+
|
||
|
+ this->currentTokens -= drainTokens;
|
||
|
+ if (this->currentTokens < 0) {
|
||
|
+ this->currentTokens = 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ // Then try to add tokens
|
||
|
+ bool limit = this->currentTokens > ((static_cast<long>(maxTokens)) - tokens);
|
||
|
+
|
||
|
+ // If the bucket is not overflowed, allow message and add tokens
|
||
|
+ if (!limit) {
|
||
|
+ this->currentTokens += tokens;
|
||
|
+ }
|
||
|
+
|
||
|
+ return limit;
|
||
|
+}
|
||
|
diff -Nur mumble-1.2.19/src/murmur/ServerUser.h new/src/murmur/ServerUser.h
|
||
|
--- mumble-1.2.19/src/murmur/ServerUser.h 2017-01-27 07:48:33.000000000 +0100
|
||
|
+++ new/src/murmur/ServerUser.h 2019-07-13 00:49:28.023395272 +0200
|
||
|
@@ -40,6 +40,13 @@
|
||
|
#include <winsock2.h>
|
||
|
#endif
|
||
|
|
||
|
+// <chrono> was introduced in C++11
|
||
|
+#if __cplusplus > 199711LL
|
||
|
+#include <chrono>
|
||
|
+#else
|
||
|
+#include <ctime>
|
||
|
+#endif
|
||
|
+
|
||
|
#include "Connection.h"
|
||
|
#include "Net.h"
|
||
|
#include "Timer.h"
|
||
|
@@ -80,6 +87,26 @@
|
||
|
|
||
|
class Server;
|
||
|
|
||
|
+#if __cplusplus > 199711L
|
||
|
+ typedef std::chrono::time_point<std::chrono::steady_clock> time_point;
|
||
|
+#else
|
||
|
+ typedef clock_t time_point;
|
||
|
+#endif
|
||
|
+
|
||
|
+// Simple algorithm for rate limiting
|
||
|
+class LeakyBucket {
|
||
|
+ private:
|
||
|
+ unsigned int tokensPerSec, maxTokens;
|
||
|
+ long currentTokens;
|
||
|
+ time_point lastUpdate;
|
||
|
+
|
||
|
+ public:
|
||
|
+ // Returns true if packets should be dropped
|
||
|
+ bool ratelimit(int tokens);
|
||
|
+
|
||
|
+ LeakyBucket();
|
||
|
+};
|
||
|
+
|
||
|
class ServerUser : public Connection, public User {
|
||
|
private:
|
||
|
Q_OBJECT
|
||
|
@@ -119,6 +146,8 @@
|
||
|
QMap<int, TargetCache> qmTargetCache;
|
||
|
QMap<QString, QString> qmWhisperRedirect;
|
||
|
|
||
|
+ LeakyBucket leakyBucket;
|
||
|
+
|
||
|
int iLastPermissionCheck;
|
||
|
QMap<int, unsigned int> qmPermissionSent;
|
||
|
#ifdef Q_OS_UNIX
|