From 38dde9684f43dff636d1b798e7537b93d2b6b7fd Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 27 Oct 2010 12:08:47 -0700 Subject: [PATCH] Add extra anti-DoS tech to net.Server --- benchmark/idle_clients.js | 17 +---------- lib/net.js | 61 +++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/benchmark/idle_clients.js b/benchmark/idle_clients.js index 65988ab3ad..da96b8ac65 100644 --- a/benchmark/idle_clients.js +++ b/benchmark/idle_clients.js @@ -4,21 +4,6 @@ var errors = 0, connections = 0; var lastClose = 0; -function maybeConnect (s) { - var now = new Date(); - if (now - lastClose > 5000) { - // Just connect immediately - connect(); - } else { - // Otherwise wait a little - see if this one is connected still. Just to - // avoid spinning at 100% cpu when the server totally rejects our - // connections. - setTimeout(function () { - if (s.writable && s.readable) connect(); - }, 100); - } -} - function connect () { process.nextTick(function () { var s = net.Stream(); @@ -28,7 +13,7 @@ function connect () { s.on('connect', function () { gotConnected = true; connections++; - maybeConnect(s); + connect(); }); s.on('close', function () { diff --git a/lib/net.js b/lib/net.js index 1e11c4aa2e..3c537e0337 100644 --- a/lib/net.js +++ b/lib/net.js @@ -911,7 +911,7 @@ function Server (/* [ options, ] listener */) { if (typeof arguments[0] == "object") { options = arguments[0]; } - + // listener: find the last argument that is a function for (var l = arguments.length - 1; l >= 0; l--) { if (typeof arguments[l] == "function") { @@ -930,33 +930,56 @@ function Server (/* [ options, ] listener */) { // Just in case we don't have a dummy fd. if (!self._dummyFD) self._getDummyFD(); + if (self._acceptTimer) { + // Somehow the watcher got started again. Need to wait until + // the timer finishes. + self.watcher.stop(); + } + while (self.fd) { try { var peerInfo = accept(self.fd); } catch (e) { - if (e.errno == EMFILE) { - // Output a warning, but only at most every 5 seconds. - var now = new Date(); - if (now - self._lastEMFILEWarning > 5000) { - console.error("(node) Hit max file limit. Increase 'ulimit -n'."); - } + if (e.errno != EMFILE) throw e; + + // Output a warning, but only at most every 5 seconds. + var now = new Date(); + if (now - self._lastEMFILEWarning > 5000) { + console.error("(node) Hit max file limit. Increase 'ulimit -n'."); self._lastEMFILEWarning = now; + } - // Gracefully reject pending clients by freeing up a file - // descriptor. - if (self._dummyFD) { - close(self._dummyFD); - self._dummyFD = null; - while (true) { - peerInfo = accept(self.fd); - if (!peerInfo) break; - close(peerInfo.fd); + var acceptCount = 0; + + // Gracefully reject pending clients by freeing up a file + // descriptor. + if (self._dummyFD) { + close(self._dummyFD); // Free up an fd + self._dummyFD = null; + // Accept and close the waiting clients one at a time. + // Single threaded programming ftw. + while (true) { + peerInfo = accept(self.fd); + if (!peerInfo) break; + close(peerInfo.fd); + + // Don't become DoS'd by incoming requests + if (++acceptCount > 50) { + assert(!self._acceptTimer); + self.watcher.stop(); + // Wait a second before accepting more. + self._acceptTimer = setTimeout(function () { + assert(parseInt(self.fd) >= 0); + self._acceptTimer = null; + self.watcher.start(); + }, 1000); + break; } - self._getDummyFD(); } - return; + // Reacquire the dummy fd + self._getDummyFD(); } - throw e; + return; } if (!peerInfo) return;