From e3b0af1b67d77549666edeec0e90fdbe10959696 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Fri, 27 Aug 2021 14:43:09 -0700 Subject: [PATCH] Expand upon flood detection and chat rate limiting. Closes #1349 --- core/chat/chatclient.go | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/core/chat/chatclient.go b/core/chat/chatclient.go index a85511fb2..3d90dbe2b 100644 --- a/core/chat/chatclient.go +++ b/core/chat/chatclient.go @@ -25,6 +25,8 @@ type ChatClient struct { // Buffered channel of outbound messages. send chan []byte rateLimiter *rate.Limiter + timeoutTimer *time.Timer + inTimeout bool Geo *geoip.GeoDetails `json:"geo"` MessageCount int `json:"messageCount"` UserAgent string `json:"userAgent"` @@ -72,7 +74,9 @@ func (c *ChatClient) sendConnectedClientInfo() { } func (c *ChatClient) readPump() { - c.rateLimiter = rate.NewLimiter(0.6, 5) + // Allow 3 messages every two seconds. + limit := rate.Every(2 * time.Second / 3) + c.rateLimiter = rate.NewLimiter(limit, 1) defer func() { c.close() @@ -100,8 +104,16 @@ func (c *ChatClient) readPump() { continue } + // Check if this client is temporarily blocked from sending messages. + if c.inTimeout { + continue + } + // Guard against floods. if !c.passesRateLimit() { + log.Warnln("Client", c.id, c.User.DisplayName, "has exceeded the messaging rate limiting thresholds and messages are being rejected temporarily.") + c.startChatRejectionTimeout() + continue } @@ -171,12 +183,24 @@ func (c *ChatClient) close() { } func (c *ChatClient) passesRateLimit() bool { - if !c.rateLimiter.Allow() { - log.Debugln("Client", c.id, c.User.DisplayName, "has exceeded the messaging rate limiting thresholds.") - return false + return c.rateLimiter.Allow() && !c.inTimeout +} + +func (c *ChatClient) startChatRejectionTimeout() { + if c.timeoutTimer != nil { + return } - return true + c.inTimeout = true + c.timeoutTimer = time.NewTimer(10 * time.Second) + go func(c *ChatClient) { + for range c.timeoutTimer.C { + c.inTimeout = false + c.timeoutTimer = nil + } + }(c) + + c.sendAction("You are temporarily blocked from sending chat messages due to perceived flooding.") } func (c *ChatClient) sendPayload(payload events.EventPayload) {