Skip to content

Commit d493986

Browse files
jasnellBethGriggs
authored andcommitted
net: introduce net.BlockList
`net.BlockList` provides an object intended to be used by net APIs to specify rules for disallowing network activity with specific IP addresses. This commit adds the basic mechanism but does not add the specific uses. PR-URL: #34625 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Bradley Farias <[email protected]>
1 parent 884ee82 commit d493986

10 files changed

+1189
-2
lines changed

doc/api/net.md

+80
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,86 @@ net.createServer().listen(
5555
path.join('\\\\?\\pipe', process.cwd(), 'myctl'));
5656
```
5757

58+
## Class: `net.BlockList`
59+
<!-- YAML
60+
added: REPLACEME
61+
-->
62+
63+
The `BlockList` object can be used with some network APIs to specify rules for
64+
disabling inbound or outbound access to specific IP addresses, IP ranges, or
65+
IP subnets.
66+
67+
### `blockList.addAddress(address[, type])`
68+
<!-- YAML
69+
added: REPLACEME
70+
-->
71+
72+
* `address` {string} An IPv4 or IPv6 address.
73+
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default**: `'ipv4'`.
74+
75+
Adds a rule to block the given IP address.
76+
77+
### `blockList.addRange(start, end[, type])`
78+
<!-- YAML
79+
added: REPLACEME
80+
-->
81+
82+
* `start` {string} The starting IPv4 or IPv6 address in the range.
83+
* `end` {string} The ending IPv4 or IPv6 address in the range.
84+
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default**: `'ipv4'`.
85+
86+
Adds a rule to block a range of IP addresses from `start` (inclusive) to
87+
`end` (inclusive).
88+
89+
### `blockList.addSubnet(net, prefix[, type])`
90+
<!-- YAML
91+
added: REPLACEME
92+
-->
93+
94+
* `net` {string} The network IPv4 or IPv6 address.
95+
* `prefix` {number} The number of CIDR prefix bits. For IPv4, this
96+
must be a value between `0` and `32`. For IPv6, this must be between
97+
`0` and `128`.
98+
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default**: `'ipv4'`.
99+
100+
Adds a rule to block a range of IP addresses specified as a subnet mask.
101+
102+
### `blockList.check(address[, type])`
103+
<!-- YAML
104+
added: REPLACEME
105+
-->
106+
107+
* `address` {string} The IP address to check
108+
* `type` {string} Either `'ipv4'` or `'ipv6'`. **Default**: `'ipv4'`.
109+
* Returns: {boolean}
110+
111+
Returns `true` if the given IP address matches any of the rules added to the
112+
`BlockList`.
113+
114+
```js
115+
const blockList = new net.BlockList();
116+
blockList.addAddress('123.123.123.123');
117+
blockList.addRange('10.0.0.1', '10.0.0.10');
118+
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
119+
120+
console.log(blockList.check('123.123.123.123')); // Prints: true
121+
console.log(blockList.check('10.0.0.3')); // Prints: true
122+
console.log(blockList.check('222.111.111.222')); // Prints: false
123+
124+
// IPv6 notation for IPv4 addresses works:
125+
console.log(blockList.check('::ffff:7b7b:7b7b', 'ipv6')); // Prints: true
126+
console.log(blockList.check('::ffff:123.123.123.123', 'ipv6')); // Prints: true
127+
```
128+
129+
### `blockList.rules`
130+
<!-- YAML
131+
added: REPLACEME
132+
-->
133+
134+
* Type: {string[]}
135+
136+
The list of rules added to the blocklist.
137+
58138
## Class: `net.Server`
59139
<!-- YAML
60140
added: v0.1.90

lib/internal/blocklist.js

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
'use strict';
2+
3+
const {
4+
Boolean,
5+
Symbol
6+
} = primordials;
7+
8+
const {
9+
BlockList: BlockListHandle,
10+
AF_INET,
11+
AF_INET6,
12+
} = internalBinding('block_list');
13+
14+
const {
15+
customInspectSymbol: kInspect,
16+
} = require('internal/util');
17+
const { inspect } = require('internal/util/inspect');
18+
19+
const kHandle = Symbol('kHandle');
20+
const { owner_symbol } = internalBinding('symbols');
21+
22+
const {
23+
ERR_INVALID_ARG_TYPE,
24+
ERR_INVALID_ARG_VALUE,
25+
ERR_OUT_OF_RANGE,
26+
} = require('internal/errors').codes;
27+
28+
class BlockList {
29+
constructor() {
30+
this[kHandle] = new BlockListHandle();
31+
this[kHandle][owner_symbol] = this;
32+
}
33+
34+
[kInspect](depth, options) {
35+
if (depth < 0)
36+
return this;
37+
38+
const opts = {
39+
...options,
40+
depth: options.depth == null ? null : options.depth - 1
41+
};
42+
43+
return `BlockList ${inspect({
44+
rules: this.rules
45+
}, opts)}`;
46+
}
47+
48+
addAddress(address, family = 'ipv4') {
49+
if (typeof address !== 'string')
50+
throw new ERR_INVALID_ARG_TYPE('address', 'string', address);
51+
if (typeof family !== 'string')
52+
throw new ERR_INVALID_ARG_TYPE('family', 'string', family);
53+
if (family !== 'ipv4' && family !== 'ipv6')
54+
throw new ERR_INVALID_ARG_VALUE('family', family);
55+
const type = family === 'ipv4' ? AF_INET : AF_INET6;
56+
this[kHandle].addAddress(address, type);
57+
}
58+
59+
addRange(start, end, family = 'ipv4') {
60+
if (typeof start !== 'string')
61+
throw new ERR_INVALID_ARG_TYPE('start', 'string', start);
62+
if (typeof end !== 'string')
63+
throw new ERR_INVALID_ARG_TYPE('end', 'string', end);
64+
if (typeof family !== 'string')
65+
throw new ERR_INVALID_ARG_TYPE('family', 'string', family);
66+
if (family !== 'ipv4' && family !== 'ipv6')
67+
throw new ERR_INVALID_ARG_VALUE('family', family);
68+
const type = family === 'ipv4' ? AF_INET : AF_INET6;
69+
const ret = this[kHandle].addRange(start, end, type);
70+
if (ret === false)
71+
throw new ERR_INVALID_ARG_VALUE('start', start, 'must come before end');
72+
}
73+
74+
addSubnet(network, prefix, family = 'ipv4') {
75+
if (typeof network !== 'string')
76+
throw new ERR_INVALID_ARG_TYPE('network', 'string', network);
77+
if (typeof prefix !== 'number')
78+
throw new ERR_INVALID_ARG_TYPE('prefix', 'number', prefix);
79+
if (typeof family !== 'string')
80+
throw new ERR_INVALID_ARG_TYPE('family', 'string', family);
81+
let type;
82+
switch (family) {
83+
case 'ipv4':
84+
type = AF_INET;
85+
if (prefix < 0 || prefix > 32)
86+
throw new ERR_OUT_OF_RANGE(prefix, '>= 0 and <= 32', prefix);
87+
break;
88+
case 'ipv6':
89+
type = AF_INET6;
90+
if (prefix < 0 || prefix > 128)
91+
throw new ERR_OUT_OF_RANGE(prefix, '>= 0 and <= 128', prefix);
92+
break;
93+
default:
94+
throw new ERR_INVALID_ARG_VALUE('family', family);
95+
}
96+
this[kHandle].addSubnet(network, type, prefix);
97+
}
98+
99+
check(address, family = 'ipv4') {
100+
if (typeof address !== 'string')
101+
throw new ERR_INVALID_ARG_TYPE('address', 'string', address);
102+
if (typeof family !== 'string')
103+
throw new ERR_INVALID_ARG_TYPE('family', 'string', family);
104+
if (family !== 'ipv4' && family !== 'ipv6')
105+
throw new ERR_INVALID_ARG_VALUE('family', family);
106+
const type = family === 'ipv4' ? AF_INET : AF_INET6;
107+
return Boolean(this[kHandle].check(address, type));
108+
}
109+
110+
get rules() {
111+
return this[kHandle].getRules();
112+
}
113+
}
114+
115+
module.exports = BlockList;

lib/net.js

+6
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ const {
115115
// Lazy loaded to improve startup performance.
116116
let cluster;
117117
let dns;
118+
let BlockList;
118119

119120
const { clearTimeout } = require('timers');
120121
const { kTimeout } = require('internal/timers');
@@ -1755,6 +1756,11 @@ module.exports = {
17551756
_createServerHandle: createServerHandle,
17561757
_normalizeArgs: normalizeArgs,
17571758
_setSimultaneousAccepts,
1759+
get BlockList() {
1760+
if (BlockList === undefined)
1761+
BlockList = require('internal/blocklist');
1762+
return BlockList;
1763+
},
17581764
connect,
17591765
createConnection: connect,
17601766
createServer,

node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
'lib/internal/assert/assertion_error.js',
102102
'lib/internal/assert/calltracker.js',
103103
'lib/internal/async_hooks.js',
104+
'lib/internal/blocklist.js',
104105
'lib/internal/buffer.js',
105106
'lib/internal/cli_table.js',
106107
'lib/internal/child_process.js',

src/node_binding.cc

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
// __attribute__((constructor)) like mechanism in GCC.
3838
#define NODE_BUILTIN_STANDARD_MODULES(V) \
3939
V(async_wrap) \
40+
V(block_list) \
4041
V(buffer) \
4142
V(cares_wrap) \
4243
V(config) \

src/node_sockaddr-inl.h

+26-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

66
#include "node.h"
7+
#include "env-inl.h"
78
#include "node_internals.h"
89
#include "node_sockaddr.h"
910
#include "util-inl.h"
@@ -88,11 +89,11 @@ SocketAddress& SocketAddress::operator=(const SocketAddress& addr) {
8889
}
8990

9091
const sockaddr& SocketAddress::operator*() const {
91-
return *this->data();
92+
return *data();
9293
}
9394

9495
const sockaddr* SocketAddress::operator->() const {
95-
return this->data();
96+
return data();
9697
}
9798

9899
size_t SocketAddress::length() const {
@@ -166,6 +167,24 @@ bool SocketAddress::operator!=(const SocketAddress& other) const {
166167
return !(*this == other);
167168
}
168169

170+
bool SocketAddress::operator<(const SocketAddress& other) const {
171+
return compare(other) == CompareResult::LESS_THAN;
172+
}
173+
174+
bool SocketAddress::operator>(const SocketAddress& other) const {
175+
return compare(other) == CompareResult::GREATER_THAN;
176+
}
177+
178+
bool SocketAddress::operator<=(const SocketAddress& other) const {
179+
CompareResult c = compare(other);
180+
return c == CompareResult::NOT_COMPARABLE ? false :
181+
c <= CompareResult::SAME;
182+
}
183+
184+
bool SocketAddress::operator>=(const SocketAddress& other) const {
185+
return compare(other) >= CompareResult::SAME;
186+
}
187+
169188
template <typename T>
170189
SocketAddressLRU<T>::SocketAddressLRU(
171190
size_t max_size)
@@ -231,6 +250,11 @@ typename T::Type* SocketAddressLRU<T>::Upsert(
231250
return &map_[address]->second;
232251
}
233252

253+
v8::MaybeLocal<v8::Value> SocketAddressBlockList::Rule::ToV8String(
254+
Environment* env) {
255+
std::string str = ToString();
256+
return ToV8Value(env->context(), str);
257+
}
234258
} // namespace node
235259

236260
#endif // NODE_WANT_INTERNALS

0 commit comments

Comments
 (0)