diff --git a/changelog b/changelog index cd5cda16..ee50ba09 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,7 @@ +Mon Oct 14 16:30 2013 CDT + * lib/server.js: Rate-limit socket.io connections + * lib/bgtask.js: Periodically clear out old rate limiters + Sat Oct 12 19:43 2013 CDT * lib/user.js: Fix jumpTo kick bug (and delete) * lib/api.js: Fix unloaded channel API listing bug diff --git a/lib/bgtask.js b/lib/bgtask.js index 9b2b9a1b..bda822ac 100644 --- a/lib/bgtask.js +++ b/lib/bgtask.js @@ -56,12 +56,25 @@ function initAliasCleanup(Server) { }, CLEAN_INTERVAL); } +/* Clean out old rate limiters */ +function initIpThrottleCleanup(Server) { + setInterval(function () { + for (var ip in Server.ipThrottle) { + if (Server.ipThrottle[ip].lastTime < Date.now() - 60 * 1000) { + delete Server.ipThrottle[ip]; + } + } + }, 5 * 60 * 1000); +} + module.exports = function (Server) { if (init === Server) { Logger.errlog.log("WARNING: Attempted to re-init background tasks"); return; } + init = Server; initStats(Server); initAliasCleanup(Server); + initIpThrottleCleanup(Server); }; diff --git a/lib/server.js b/lib/server.js index 1c0681e3..67941790 100644 --- a/lib/server.js +++ b/lib/server.js @@ -56,6 +56,7 @@ var Server = function (cfg) { self.ioWeb = null; self.ioSecure = null; self.ipCount = {}; + self.ipThrottle = {}; self.db = null; self.api = null; self.announcement = null; @@ -257,11 +258,29 @@ Server.prototype.logHTTP = function (req, status) { ].join(" ")); }; +const IP_THROTTLE = { + burst: 5, + sustained: 0.1 +}; + Server.prototype.handleSocketConnection = function (socket) { var self = this; var ip = self.getSocketIP(socket); socket._ip = ip; + if (!(ip in self.ipThrottle)) { + self.ipThrottle[ip] = $util.newRateLimiter(); + } + + if (self.ipThrottle[ip].throttle(IP_THROTTLE)) { + Logger.syslog.log("WARN: IP throttled: " + ip); + socket.emit("kick", { + reason: "Your IP address is connecting too quickly. Please "+ + "wait 10 seconds before joining again." + }); + return; + } + // Check for global ban on the IP self.db.isGlobalIPBanned(ip, function (err, banned) { if (banned) {