From 83871b6ebdd31a13dfc0777f62071c35cb1f0962 Mon Sep 17 00:00:00 2001
From: theanarkh <theratliter@gmail.com>
Date: Sat, 30 Nov 2024 00:22:33 +0800
Subject: [PATCH] net: support blocklist for net.Server

---
 doc/api/net.md                             |  3 +++
 lib/net.js                                 | 17 ++++++++++++++++-
 test/parallel/test-net-server-blocklist.js | 19 +++++++++++++++++++
 3 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 test/parallel/test-net-server-blocklist.js

diff --git a/doc/api/net.md b/doc/api/net.md
index 7d8a27bf0b1769..bc36a0833be8ef 100644
--- a/doc/api/net.md
+++ b/doc/api/net.md
@@ -1713,6 +1713,9 @@ changes:
     **Default:** `false`.
   * `pauseOnConnect` {boolean} Indicates whether the socket should be
     paused on incoming connections. **Default:** `false`.
+  * `blocklist` {net.BlockList} `blocklist` can be used for disabling inbound
+    access to specific IP addresses, IP ranges, or IP subnets. The IP is the
+    peer address of the accepted TCP socket, such as browser, reverse proxy.
 
 * `connectionListener` {Function} Automatically set as a listener for the
   [`'connection'`][] event.
diff --git a/lib/net.js b/lib/net.js
index 52085df6ba0574..94f99b75ea5c76 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -1791,6 +1791,13 @@ function Server(options, connectionListener) {
   this.keepAlive = Boolean(options.keepAlive);
   this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
   this.highWaterMark = options.highWaterMark ?? getDefaultHighWaterMark();
+  if (options.blocklist) {
+    // TODO: use BlockList.isBlockList (https://github.com/nodejs/node/pull/56078)
+    if (!(options.blocklist instanceof module.exports.BlockList)) {
+      throw new ERR_INVALID_ARG_TYPE('options.blocklist', 'net.BlockList', options.blocklist);
+    }
+    this.blocklist = options.blocklist;
+  }
 }
 ObjectSetPrototypeOf(Server.prototype, EventEmitter.prototype);
 ObjectSetPrototypeOf(Server, EventEmitter);
@@ -2239,7 +2246,15 @@ function onconnection(err, clientHandle) {
     clientHandle.close();
     return;
   }
-
+  if (self.blocklist && typeof clientHandle.getpeername === 'function') {
+    const remoteInfo = { __proto__: null };
+    clientHandle.getpeername(remoteInfo);
+    const addressType = isIP(remoteInfo.address);
+    if (addressType && self.blocklist.check(remoteInfo.address, `ipv${addressType}`)) {
+      clientHandle.close();
+      return;
+    }
+  }
   const socket = new Socket({
     handle: clientHandle,
     allowHalfOpen: self.allowHalfOpen,
diff --git a/test/parallel/test-net-server-blocklist.js b/test/parallel/test-net-server-blocklist.js
new file mode 100644
index 00000000000000..617c1e70b8d200
--- /dev/null
+++ b/test/parallel/test-net-server-blocklist.js
@@ -0,0 +1,19 @@
+'use strict';
+const common = require('../common');
+const net = require('net');
+
+const blocklist = new net.BlockList();
+blocklist.addAddress('127.0.0.1');
+
+const server = net.createServer({ blocklist }, common.mustNotCall());
+server.listen(0, common.mustCall(() => {
+  const adddress = server.address();
+  const socket = net.connect({
+    localAddress: '127.0.0.1',
+    host: adddress.host,
+    port: adddress.port
+  });
+  socket.on('close', common.mustCall(() => {
+    server.close();
+  }));
+}));