Skip to content

Commit bacfe6d

Browse files
theanarkhruyadorno
authored andcommitted
net: support blocklist in net.connect
PR-URL: #56075 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent c4a97d8 commit bacfe6d

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

doc/api/errors.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,12 @@ An attempt was made to open an IPC communication channel with a synchronously
21662166
forked Node.js process. See the documentation for the [`child_process`][] module
21672167
for more information.
21682168

2169+
<a id="ERR_IP_BLOCKED"></a>
2170+
2171+
### `ERR_IP_BLOCKED`
2172+
2173+
IP is blocked by `net.BlockList`.
2174+
21692175
<a id="ERR_LOADER_CHAIN_INCOMPLETE"></a>
21702176

21712177
### `ERR_LOADER_CHAIN_INCOMPLETE`

doc/api/net.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,8 @@ For TCP connections, available `options` are:
10891089
* `noDelay` {boolean} If set to `true`, it disables the use of Nagle's algorithm
10901090
immediately after the socket is established. **Default:** `false`.
10911091
* `port` {number} Required. Port the socket should connect to.
1092+
* `blockList` {net.BlockList} `blockList` can be used for disabling outbound
1093+
access to specific IP addresses, IP ranges, or IP subnets.
10921094

10931095
For [IPC][] connections, available `options` are:
10941096

lib/internal/errors.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,6 +1555,9 @@ E('ERR_IPC_CHANNEL_CLOSED', 'Channel closed', Error);
15551555
E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected', Error);
15561556
E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe', Error);
15571557
E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks', Error);
1558+
E('ERR_IP_BLOCKED', function(ip) {
1559+
return `IP(${ip}) is blocked by net.BlockList`;
1560+
}, Error);
15581561
E(
15591562
'ERR_LOADER_CHAIN_INCOMPLETE',
15601563
'"%s" did not call the next hook in its chain and did not' +

lib/net.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const {
103103
ERR_INVALID_FD_TYPE,
104104
ERR_INVALID_HANDLE_TYPE,
105105
ERR_INVALID_IP_ADDRESS,
106+
ERR_IP_BLOCKED,
106107
ERR_MISSING_ARGS,
107108
ERR_SERVER_ALREADY_LISTEN,
108109
ERR_SERVER_NOT_RUNNING,
@@ -510,6 +511,12 @@ function Socket(options) {
510511
// Used after `.destroy()`
511512
this[kBytesRead] = 0;
512513
this[kBytesWritten] = 0;
514+
if (options.blockList) {
515+
if (!module.exports.BlockList.isBlockList(options.blockList)) {
516+
throw new ERR_INVALID_ARG_TYPE('options.blockList', 'net.BlockList', options.blockList);
517+
}
518+
this.blockList = options.blockList;
519+
}
513520
}
514521
ObjectSetPrototypeOf(Socket.prototype, stream.Duplex.prototype);
515522
ObjectSetPrototypeOf(Socket, stream.Duplex);
@@ -1073,6 +1080,10 @@ function internalConnect(
10731080
self.emit('connectionAttempt', address, port, addressType);
10741081

10751082
if (addressType === 6 || addressType === 4) {
1083+
if (self.blockList?.check(address, `ipv${addressType}`)) {
1084+
self.destroy(new ERR_IP_BLOCKED(address));
1085+
return;
1086+
}
10761087
const req = new TCPConnectWrap();
10771088
req.oncomplete = afterConnect;
10781089
req.address = address;
@@ -1162,6 +1173,14 @@ function internalConnectMultiple(context, canceled) {
11621173
}
11631174
}
11641175

1176+
if (self.blockList?.check(address, `ipv${addressType}`)) {
1177+
const ex = new ERR_IP_BLOCKED(address);
1178+
ArrayPrototypePush(context.errors, ex);
1179+
self.emit('connectionAttemptFailed', address, port, addressType, ex);
1180+
internalConnectMultiple(context);
1181+
return;
1182+
}
1183+
11651184
debug('connect/multiple: attempting to connect to %s:%d (addressType: %d)', address, port, addressType);
11661185
self.emit('connectionAttempt', address, port, addressType);
11671186

@@ -1792,8 +1811,7 @@ function Server(options, connectionListener) {
17921811
this.keepAliveInitialDelay = ~~(options.keepAliveInitialDelay / 1000);
17931812
this.highWaterMark = options.highWaterMark ?? getDefaultHighWaterMark();
17941813
if (options.blockList) {
1795-
// TODO: use BlockList.isBlockList (https://github.com/nodejs/node/pull/56078)
1796-
if (!(options.blockList instanceof module.exports.BlockList)) {
1814+
if (!module.exports.BlockList.isBlockList(options.blockList)) {
17971815
throw new ERR_INVALID_ARG_TYPE('options.blockList', 'net.BlockList', options.blockList);
17981816
}
17991817
this.blockList = options.blockList;

test/parallel/test-net-blocklist.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const net = require('net');
5+
const assert = require('assert');
6+
7+
const blockList = new net.BlockList();
8+
blockList.addAddress('127.0.0.1');
9+
blockList.addAddress('127.0.0.2');
10+
11+
function check(err) {
12+
assert.ok(err.code === 'ERR_IP_BLOCKED', err);
13+
}
14+
15+
// Connect without calling dns.lookup
16+
{
17+
const socket = net.connect({
18+
port: 9999,
19+
host: '127.0.0.1',
20+
blockList,
21+
});
22+
socket.on('error', common.mustCall(check));
23+
}
24+
25+
// Connect with single IP returned by dns.lookup
26+
{
27+
const socket = net.connect({
28+
port: 9999,
29+
host: 'localhost',
30+
blockList,
31+
lookup: function(_, __, cb) {
32+
cb(null, '127.0.0.1', 4);
33+
},
34+
autoSelectFamily: false,
35+
});
36+
37+
socket.on('error', common.mustCall(check));
38+
}
39+
40+
// Connect with autoSelectFamily and single IP
41+
{
42+
const socket = net.connect({
43+
port: 9999,
44+
host: 'localhost',
45+
blockList,
46+
lookup: function(_, __, cb) {
47+
cb(null, [{ address: '127.0.0.1', family: 4 }]);
48+
},
49+
autoSelectFamily: true,
50+
});
51+
52+
socket.on('error', common.mustCall(check));
53+
}
54+
55+
// Connect with autoSelectFamily and multiple IPs
56+
{
57+
const socket = net.connect({
58+
port: 9999,
59+
host: 'localhost',
60+
blockList,
61+
lookup: function(_, __, cb) {
62+
cb(null, [{ address: '127.0.0.1', family: 4 }, { address: '127.0.0.2', family: 4 }]);
63+
},
64+
autoSelectFamily: true,
65+
});
66+
67+
socket.on('error', common.mustCall(check));
68+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy