Skip to content

Commit 8b83253

Browse files
theanarkhruyadorno
authored andcommitted
dgram: support blocklist in udp
PR-URL: #56087 Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
1 parent 8a24aa2 commit 8b83253

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

doc/api/dgram.md

+7
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,13 @@ changes:
957957
* `sendBufferSize` {number} Sets the `SO_SNDBUF` socket value.
958958
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
959959
* `signal` {AbortSignal} An AbortSignal that may be used to close a socket.
960+
* `receiveBlockList` {net.BlockList} `receiveBlockList` can be used for discarding
961+
inbound datagram to specific IP addresses, IP ranges, or IP subnets. This does not
962+
work if the server is behind a reverse proxy, NAT, etc. because the address
963+
checked against the blocklist is the address of the proxy, or the one
964+
specified by the NAT.
965+
* `sendBlockList` {net.BlockList} `sendBlockList` can be used for disabling outbound
966+
access to specific IP addresses, IP ranges, or IP subnets.
960967
* `callback` {Function} Attached as a listener for `'message'` events. Optional.
961968
* Returns: {dgram.Socket}
962969

lib/dgram.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const {
3939
ERR_BUFFER_OUT_OF_BOUNDS,
4040
ERR_INVALID_ARG_TYPE,
4141
ERR_INVALID_FD_TYPE,
42+
ERR_IP_BLOCKED,
4243
ERR_MISSING_ARGS,
4344
ERR_SOCKET_ALREADY_BOUND,
4445
ERR_SOCKET_BAD_BUFFER_SIZE,
@@ -53,6 +54,7 @@ const {
5354
_createSocketHandle,
5455
newHandle,
5556
} = require('internal/dgram');
57+
const { isIP } = require('internal/net');
5658
const {
5759
isInt32,
5860
validateAbortSignal,
@@ -97,12 +99,18 @@ let _cluster = null;
9799
function lazyLoadCluster() {
98100
return _cluster ??= require('cluster');
99101
}
102+
let _blockList = null;
103+
function lazyLoadBlockList() {
104+
return _blockList ??= require('internal/blocklist').BlockList;
105+
}
100106

101107
function Socket(type, listener) {
102108
FunctionPrototypeCall(EventEmitter, this);
103109
let lookup;
104110
let recvBufferSize;
105111
let sendBufferSize;
112+
let receiveBlockList;
113+
let sendBlockList;
106114

107115
let options;
108116
if (type !== null && typeof type === 'object') {
@@ -117,6 +125,18 @@ function Socket(type, listener) {
117125
}
118126
recvBufferSize = options.recvBufferSize;
119127
sendBufferSize = options.sendBufferSize;
128+
if (options.receiveBlockList) {
129+
if (!lazyLoadBlockList().isBlockList(options.receiveBlockList)) {
130+
throw new ERR_INVALID_ARG_TYPE('options.receiveBlockList', 'net.BlockList', options.receiveBlockList);
131+
}
132+
receiveBlockList = options.receiveBlockList;
133+
}
134+
if (options.sendBlockList) {
135+
if (!lazyLoadBlockList().isBlockList(options.sendBlockList)) {
136+
throw new ERR_INVALID_ARG_TYPE('options.sendBlockList', 'net.BlockList', options.sendBlockList);
137+
}
138+
sendBlockList = options.sendBlockList;
139+
}
120140
}
121141

122142
const handle = newHandle(type, lookup);
@@ -139,6 +159,8 @@ function Socket(type, listener) {
139159
ipv6Only: options?.ipv6Only,
140160
recvBufferSize,
141161
sendBufferSize,
162+
receiveBlockList,
163+
sendBlockList,
142164
};
143165

144166
if (options?.signal !== undefined) {
@@ -437,7 +459,9 @@ function doConnect(ex, self, ip, address, port, callback) {
437459
const state = self[kStateSymbol];
438460
if (!state.handle)
439461
return;
440-
462+
if (!ex && state.sendBlockList?.check(ip, `ipv${isIP(ip)}`)) {
463+
ex = new ERR_IP_BLOCKED(ip);
464+
}
441465
if (!ex) {
442466
const err = state.handle.connect(ip, port);
443467
if (err) {
@@ -701,6 +725,13 @@ function doSend(ex, self, ip, list, address, port, callback) {
701725
return;
702726
}
703727

728+
if (ip && state.sendBlockList?.check(ip, `ipv${isIP(ip)}`)) {
729+
if (callback) {
730+
process.nextTick(callback, new ERR_IP_BLOCKED(ip));
731+
}
732+
return;
733+
}
734+
704735
const req = new SendWrap();
705736
req.list = list; // Keep reference alive.
706737
req.address = address;
@@ -949,6 +980,10 @@ function onMessage(nread, handle, buf, rinfo) {
949980
if (nread < 0) {
950981
return self.emit('error', new ErrnoException(nread, 'recvmsg'));
951982
}
983+
if (self[kStateSymbol]?.receiveBlockList?.check(rinfo.address,
984+
rinfo.family?.toLocaleLowerCase())) {
985+
return;
986+
}
952987
rinfo.size = buf.length; // compatibility
953988
self.emit('message', buf, rinfo);
954989
}

test/parallel/test-dgram-blocklist.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const dgram = require('dgram');
5+
const net = require('net');
6+
7+
{
8+
const blockList = new net.BlockList();
9+
blockList.addAddress(common.localhostIPv4);
10+
11+
const connectSocket = dgram.createSocket({ type: 'udp4', sendBlockList: blockList });
12+
connectSocket.connect(9999, common.localhostIPv4, common.mustCall((err) => {
13+
assert.ok(err.code === 'ERR_IP_BLOCKED', err);
14+
connectSocket.close();
15+
}));
16+
}
17+
18+
{
19+
const blockList = new net.BlockList();
20+
blockList.addAddress(common.localhostIPv4);
21+
const sendSocket = dgram.createSocket({ type: 'udp4', sendBlockList: blockList });
22+
sendSocket.send('hello', 9999, common.localhostIPv4, common.mustCall((err) => {
23+
assert.ok(err.code === 'ERR_IP_BLOCKED', err);
24+
sendSocket.close();
25+
}));
26+
}
27+
28+
{
29+
const blockList = new net.BlockList();
30+
blockList.addAddress(common.localhostIPv4);
31+
const receiveSocket = dgram.createSocket({ type: 'udp4', receiveBlockList: blockList });
32+
// Hack to close the socket
33+
const check = blockList.check;
34+
blockList.check = function() {
35+
process.nextTick(() => {
36+
receiveSocket.close();
37+
});
38+
return check.apply(this, arguments);
39+
};
40+
receiveSocket.on('message', common.mustNotCall());
41+
receiveSocket.bind(0, common.localhostIPv4, common.mustCall(() => {
42+
const addressInfo = receiveSocket.address();
43+
const client = dgram.createSocket('udp4');
44+
client.send('hello', addressInfo.port, addressInfo.address, common.mustCall((err) => {
45+
assert.ok(!err);
46+
client.close();
47+
}));
48+
}));
49+
}

0 commit comments

Comments
 (0)