GeezerVison is showing github.com — dgram: support blocklist in udp · nodejs/node@ba9d539
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/dgram.md +7Lines changed: 7 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -957,6 +957,13 @@ changes: | 957 | 957 | * `sendBufferSize` {number} Sets the `SO_SNDBUF` socket value. | 958 | 958 | * `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][]. | 959 | 959 | * `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. | 960 | 967 | * `callback` {Function} Attached as a listener for `'message'` events. Optional. | 961 | 968 | * Returns: {dgram.Socket} | 962 | 969 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/dgram.js +36-1Lines changed: 36 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -39,6 +39,7 @@ const { | 39 | 39 | ERR_BUFFER_OUT_OF_BOUNDS, | 40 | 40 | ERR_INVALID_ARG_TYPE, | 41 | 41 | ERR_INVALID_FD_TYPE, | | 42 | + ERR_IP_BLOCKED, | 42 | 43 | ERR_MISSING_ARGS, | 43 | 44 | ERR_SOCKET_ALREADY_BOUND, | 44 | 45 | ERR_SOCKET_BAD_BUFFER_SIZE, | @@ -53,6 +54,7 @@ const { | 53 | 54 | _createSocketHandle, | 54 | 55 | newHandle, | 55 | 56 | } = require('internal/dgram'); | | 57 | +const { isIP } = require('internal/net'); | 56 | 58 | const { | 57 | 59 | isInt32, | 58 | 60 | validateAbortSignal, | @@ -97,12 +99,18 @@ let _cluster = null; | 97 | 99 | function lazyLoadCluster() { | 98 | 100 | return _cluster ??= require('cluster'); | 99 | 101 | } | | 102 | +let _blockList = null; | | 103 | +function lazyLoadBlockList() { | | 104 | + return _blockList ??= require('internal/blocklist').BlockList; | | 105 | +} | 100 | 106 |
| 101 | 107 | function Socket(type, listener) { | 102 | 108 | FunctionPrototypeCall(EventEmitter, this); | 103 | 109 | let lookup; | 104 | 110 | let recvBufferSize; | 105 | 111 | let sendBufferSize; | | 112 | + let receiveBlockList; | | 113 | + let sendBlockList; | 106 | 114 |
| 107 | 115 | let options; | 108 | 116 | if (type !== null && typeof type === 'object') { | @@ -117,6 +125,18 @@ function Socket(type, listener) { | 117 | 125 | } | 118 | 126 | recvBufferSize = options.recvBufferSize; | 119 | 127 | 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 | + } | 120 | 140 | } | 121 | 141 |
| 122 | 142 | const handle = newHandle(type, lookup); | @@ -139,6 +159,8 @@ function Socket(type, listener) { | 139 | 159 | ipv6Only: options?.ipv6Only, | 140 | 160 | recvBufferSize, | 141 | 161 | sendBufferSize, | | 162 | + receiveBlockList, | | 163 | + sendBlockList, | 142 | 164 | }; | 143 | 165 |
| 144 | 166 | if (options?.signal !== undefined) { | @@ -437,7 +459,9 @@ function doConnect(ex, self, ip, address, port, callback) { | 437 | 459 | const state = self[kStateSymbol]; | 438 | 460 | if (!state.handle) | 439 | 461 | return; | 440 | | - | | 462 | + if (!ex && state.sendBlockList?.check(ip, `ipv${isIP(ip)}`)) { | | 463 | + ex = new ERR_IP_BLOCKED(ip); | | 464 | + } | 441 | 465 | if (!ex) { | 442 | 466 | const err = state.handle.connect(ip, port); | 443 | 467 | if (err) { | @@ -701,6 +725,13 @@ function doSend(ex, self, ip, list, address, port, callback) { | 701 | 725 | return; | 702 | 726 | } | 703 | 727 |
| | 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 | + | 704 | 735 | const req = new SendWrap(); | 705 | 736 | req.list = list; // Keep reference alive. | 706 | 737 | req.address = address; | @@ -949,6 +980,10 @@ function onMessage(nread, handle, buf, rinfo) { | 949 | 980 | if (nread < 0) { | 950 | 981 | return self.emit('error', new ErrnoException(nread, 'recvmsg')); | 951 | 982 | } | | 983 | + if (self[kStateSymbol]?.receiveBlockList?.check(rinfo.address, | | 984 | + rinfo.family?.toLocaleLowerCase())) { | | 985 | + return; | | 986 | + } | 952 | 987 | rinfo.size = buf.length; // compatibility | 953 | 988 | self.emit('message', buf, rinfo); | 954 | 989 | } | |
Collapse file Copy file name to clipboard +49Lines changed: 49 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 | +} |
|