Skip to content

Commit 87c7106

Browse files
jasnellMylesBorins
authored andcommittedAug 31, 2021
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 <anna@addaleax.net> Reviewed-By: Bradley Farias <bradley.meck@gmail.com>
1 parent 1008c80 commit 87c7106

File tree

10 files changed

+1189
-2
lines changed

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

‎src/node_sockaddr.cc

+592
Large diffs are not rendered by default.

‎src/node_sockaddr.h

+146
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33

44
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
55

6+
#include "env.h"
67
#include "memory_tracker.h"
8+
#include "base_object.h"
79
#include "node.h"
810
#include "uv.h"
911
#include "v8.h"
1012

13+
#include <memory>
1114
#include <string>
1215
#include <list>
1316
#include <unordered_map>
@@ -18,13 +21,25 @@ class Environment;
1821

1922
class SocketAddress : public MemoryRetainer {
2023
public:
24+
enum class CompareResult {
25+
NOT_COMPARABLE = -2,
26+
LESS_THAN,
27+
SAME,
28+
GREATER_THAN
29+
};
30+
2131
struct Hash {
2232
size_t operator()(const SocketAddress& addr) const;
2333
};
2434

2535
inline bool operator==(const SocketAddress& other) const;
2636
inline bool operator!=(const SocketAddress& other) const;
2737

38+
inline bool operator<(const SocketAddress& other) const;
39+
inline bool operator>(const SocketAddress& other) const;
40+
inline bool operator<=(const SocketAddress& other) const;
41+
inline bool operator>=(const SocketAddress& other) const;
42+
2843
inline static bool is_numeric_host(const char* hostname);
2944
inline static bool is_numeric_host(const char* hostname, int family);
3045

@@ -78,6 +93,20 @@ class SocketAddress : public MemoryRetainer {
7893
inline std::string address() const;
7994
inline int port() const;
8095

96+
// Returns true if the given other SocketAddress is a match
97+
// for this one. The addresses are a match if:
98+
// 1. They are the same family and match identically
99+
// 2. They are different family but match semantically (
100+
// for instance, an IPv4 addres in IPv6 notation)
101+
bool is_match(const SocketAddress& other) const;
102+
103+
// Compares this SocketAddress to the given other SocketAddress.
104+
CompareResult compare(const SocketAddress& other) const;
105+
106+
// Returns true if this SocketAddress is within the subnet
107+
// identified by the given network address and CIDR prefix.
108+
bool is_in_network(const SocketAddress& network, int prefix) const;
109+
81110
// If the SocketAddress is an IPv6 address, returns the
82111
// current value of the IPv6 flow label, if set. Otherwise
83112
// returns 0.
@@ -152,6 +181,123 @@ class SocketAddressLRU : public MemoryRetainer {
152181
size_t max_size_;
153182
};
154183

184+
// A BlockList is used to evaluate whether a given
185+
// SocketAddress should be accepted for inbound or
186+
// outbound network activity.
187+
class SocketAddressBlockList : public MemoryRetainer {
188+
public:
189+
explicit SocketAddressBlockList(
190+
std::shared_ptr<SocketAddressBlockList> parent = {});
191+
~SocketAddressBlockList() = default;
192+
193+
void AddSocketAddress(
194+
const SocketAddress& address);
195+
196+
void RemoveSocketAddress(
197+
const SocketAddress& address);
198+
199+
void AddSocketAddressRange(
200+
const SocketAddress& start,
201+
const SocketAddress& end);
202+
203+
void AddSocketAddressMask(
204+
const SocketAddress& address,
205+
int prefix);
206+
207+
bool Apply(const SocketAddress& address);
208+
209+
size_t size() const { return rules_.size(); }
210+
211+
v8::MaybeLocal<v8::Array> ListRules(Environment* env);
212+
213+
struct Rule : public MemoryRetainer {
214+
virtual bool Apply(const SocketAddress& address) = 0;
215+
inline v8::MaybeLocal<v8::Value> ToV8String(Environment* env);
216+
virtual std::string ToString() = 0;
217+
};
218+
219+
struct SocketAddressRule final : Rule {
220+
SocketAddress address;
221+
222+
explicit SocketAddressRule(const SocketAddress& address);
223+
224+
bool Apply(const SocketAddress& address) override;
225+
std::string ToString() override;
226+
227+
void MemoryInfo(node::MemoryTracker* tracker) const override;
228+
SET_MEMORY_INFO_NAME(SocketAddressRule)
229+
SET_SELF_SIZE(SocketAddressRule)
230+
};
231+
232+
struct SocketAddressRangeRule final : Rule {
233+
SocketAddress start;
234+
SocketAddress end;
235+
236+
SocketAddressRangeRule(
237+
const SocketAddress& start,
238+
const SocketAddress& end);
239+
240+
bool Apply(const SocketAddress& address) override;
241+
std::string ToString() override;
242+
243+
void MemoryInfo(node::MemoryTracker* tracker) const override;
244+
SET_MEMORY_INFO_NAME(SocketAddressRangeRule)
245+
SET_SELF_SIZE(SocketAddressRangeRule)
246+
};
247+
248+
struct SocketAddressMaskRule final : Rule {
249+
SocketAddress network;
250+
int prefix;
251+
252+
SocketAddressMaskRule(
253+
const SocketAddress& address,
254+
int prefix);
255+
256+
bool Apply(const SocketAddress& address) override;
257+
std::string ToString() override;
258+
259+
void MemoryInfo(node::MemoryTracker* tracker) const override;
260+
SET_MEMORY_INFO_NAME(SocketAddressMaskRule)
261+
SET_SELF_SIZE(SocketAddressMaskRule)
262+
};
263+
264+
void MemoryInfo(node::MemoryTracker* tracker) const override;
265+
SET_MEMORY_INFO_NAME(SocketAddressBlockList)
266+
SET_SELF_SIZE(SocketAddressBlockList)
267+
268+
private:
269+
std::shared_ptr<SocketAddressBlockList> parent_;
270+
std::list<std::unique_ptr<Rule>> rules_;
271+
SocketAddress::Map<std::list<std::unique_ptr<Rule>>::iterator> address_rules_;
272+
};
273+
274+
class SocketAddressBlockListWrap :
275+
public BaseObject,
276+
public SocketAddressBlockList {
277+
public:
278+
static void Initialize(v8::Local<v8::Object> target,
279+
v8::Local<v8::Value> unused,
280+
v8::Local<v8::Context> context,
281+
void* priv);
282+
283+
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
284+
static void AddAddress(const v8::FunctionCallbackInfo<v8::Value>& args);
285+
static void AddRange(const v8::FunctionCallbackInfo<v8::Value>& args);
286+
static void AddSubnet(const v8::FunctionCallbackInfo<v8::Value>& args);
287+
static void Check(const v8::FunctionCallbackInfo<v8::Value>& args);
288+
static void GetRules(const v8::FunctionCallbackInfo<v8::Value>& args);
289+
290+
SocketAddressBlockListWrap(
291+
Environment* env,
292+
v8::Local<v8::Object> wrap);
293+
294+
void MemoryInfo(node::MemoryTracker* tracker) const override {
295+
SocketAddressBlockList::MemoryInfo(tracker);
296+
}
297+
SET_MEMORY_INFO_NAME(SocketAddressBlockListWrap)
298+
SET_SELF_SIZE(SocketAddressBlockListWrap)
299+
};
300+
155301
} // namespace node
156302

157303
#endif // NOE_WANT_INTERNALS

‎test/cctest/test_sockaddr.cc

+86
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "gtest/gtest.h"
33

44
using node::SocketAddress;
5+
using node::SocketAddressBlockList;
56
using node::SocketAddressLRU;
67

78
TEST(SocketAddress, SocketAddress) {
@@ -84,6 +85,7 @@ TEST(SocketAddressLRU, SocketAddressLRU) {
8485
SocketAddress::ToSockAddr(AF_INET, "123.123.123.125", 443, &storage[2]);
8586
SocketAddress::ToSockAddr(AF_INET, "123.123.123.123", 443, &storage[3]);
8687

88+
8789
SocketAddress addr1(reinterpret_cast<const sockaddr*>(&storage[0]));
8890
SocketAddress addr2(reinterpret_cast<const sockaddr*>(&storage[1]));
8991
SocketAddress addr3(reinterpret_cast<const sockaddr*>(&storage[2]));
@@ -125,3 +127,87 @@ TEST(SocketAddressLRU, SocketAddressLRU) {
125127
CHECK_NULL(lru.Peek(addr1));
126128
CHECK_NULL(lru.Peek(addr2));
127129
}
130+
131+
TEST(SocketAddress, Comparison) {
132+
sockaddr_storage storage[6];
133+
134+
SocketAddress::ToSockAddr(AF_INET, "10.0.0.1", 0, &storage[0]);
135+
SocketAddress::ToSockAddr(AF_INET, "10.0.0.2", 0, &storage[1]);
136+
SocketAddress::ToSockAddr(AF_INET6, "::1", 0, &storage[2]);
137+
SocketAddress::ToSockAddr(AF_INET6, "::2", 0, &storage[3]);
138+
SocketAddress::ToSockAddr(AF_INET6, "::ffff:10.0.0.1", 0, &storage[4]);
139+
SocketAddress::ToSockAddr(AF_INET6, "::ffff:10.0.0.2", 0, &storage[5]);
140+
141+
SocketAddress addr1(reinterpret_cast<const sockaddr*>(&storage[0]));
142+
SocketAddress addr2(reinterpret_cast<const sockaddr*>(&storage[1]));
143+
SocketAddress addr3(reinterpret_cast<const sockaddr*>(&storage[2]));
144+
SocketAddress addr4(reinterpret_cast<const sockaddr*>(&storage[3]));
145+
SocketAddress addr5(reinterpret_cast<const sockaddr*>(&storage[4]));
146+
SocketAddress addr6(reinterpret_cast<const sockaddr*>(&storage[5]));
147+
148+
CHECK_EQ(addr1.compare(addr1), SocketAddress::CompareResult::SAME);
149+
CHECK_EQ(addr1.compare(addr2), SocketAddress::CompareResult::LESS_THAN);
150+
CHECK_EQ(addr2.compare(addr1), SocketAddress::CompareResult::GREATER_THAN);
151+
CHECK(addr1 <= addr1);
152+
CHECK(addr1 < addr2);
153+
CHECK(addr1 <= addr2);
154+
CHECK(addr2 >= addr2);
155+
CHECK(addr2 > addr1);
156+
CHECK(addr2 >= addr1);
157+
158+
CHECK_EQ(addr3.compare(addr3), SocketAddress::CompareResult::SAME);
159+
CHECK_EQ(addr3.compare(addr4), SocketAddress::CompareResult::LESS_THAN);
160+
CHECK_EQ(addr4.compare(addr3), SocketAddress::CompareResult::GREATER_THAN);
161+
CHECK(addr3 <= addr3);
162+
CHECK(addr3 < addr4);
163+
CHECK(addr3 <= addr4);
164+
CHECK(addr4 >= addr4);
165+
CHECK(addr4 > addr3);
166+
CHECK(addr4 >= addr3);
167+
168+
// Not comparable
169+
CHECK_EQ(addr1.compare(addr3), SocketAddress::CompareResult::NOT_COMPARABLE);
170+
CHECK_EQ(addr3.compare(addr1), SocketAddress::CompareResult::NOT_COMPARABLE);
171+
CHECK(!(addr1 < addr3));
172+
CHECK(!(addr1 > addr3));
173+
CHECK(!(addr1 >= addr3));
174+
CHECK(!(addr1 <= addr3));
175+
CHECK(!(addr3 < addr1));
176+
CHECK(!(addr3 > addr1));
177+
CHECK(!(addr3 >= addr1));
178+
CHECK(!(addr3 <= addr1));
179+
180+
// Comparable
181+
CHECK_EQ(addr1.compare(addr5), SocketAddress::CompareResult::SAME);
182+
CHECK_EQ(addr2.compare(addr6), SocketAddress::CompareResult::SAME);
183+
CHECK_EQ(addr1.compare(addr6), SocketAddress::CompareResult::LESS_THAN);
184+
CHECK_EQ(addr6.compare(addr1), SocketAddress::CompareResult::GREATER_THAN);
185+
CHECK(addr1 <= addr5);
186+
CHECK(addr1 <= addr6);
187+
CHECK(addr1 < addr6);
188+
CHECK(addr6 > addr1);
189+
CHECK(addr6 >= addr1);
190+
CHECK(addr2 >= addr6);
191+
CHECK(addr2 >= addr5);
192+
}
193+
194+
TEST(SocketAddressBlockList, Simple) {
195+
SocketAddressBlockList bl;
196+
197+
sockaddr_storage storage[2];
198+
SocketAddress::ToSockAddr(AF_INET, "10.0.0.1", 0, &storage[0]);
199+
SocketAddress::ToSockAddr(AF_INET, "10.0.0.2", 0, &storage[1]);
200+
SocketAddress addr1(reinterpret_cast<const sockaddr*>(&storage[0]));
201+
SocketAddress addr2(reinterpret_cast<const sockaddr*>(&storage[1]));
202+
203+
bl.AddSocketAddress(addr1);
204+
bl.AddSocketAddress(addr2);
205+
206+
CHECK(bl.Apply(addr1));
207+
CHECK(bl.Apply(addr2));
208+
209+
bl.RemoveSocketAddress(addr1);
210+
211+
CHECK(!bl.Apply(addr1));
212+
CHECK(bl.Apply(addr2));
213+
}

‎test/parallel/test-blocklist.js

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
'use strict';
2+
3+
require('../common');
4+
5+
const { BlockList } = require('net');
6+
const assert = require('assert');
7+
8+
{
9+
const blockList = new BlockList();
10+
11+
[1, [], {}, null, 1n, undefined, null].forEach((i) => {
12+
assert.throws(() => blockList.addAddress(i), {
13+
code: 'ERR_INVALID_ARG_TYPE'
14+
});
15+
});
16+
17+
[1, [], {}, null, 1n, null].forEach((i) => {
18+
assert.throws(() => blockList.addAddress('1.1.1.1', i), {
19+
code: 'ERR_INVALID_ARG_TYPE'
20+
});
21+
});
22+
23+
assert.throws(() => blockList.addAddress('1.1.1.1', 'foo'), {
24+
code: 'ERR_INVALID_ARG_VALUE'
25+
});
26+
27+
[1, [], {}, null, 1n, undefined, null].forEach((i) => {
28+
assert.throws(() => blockList.addRange(i), {
29+
code: 'ERR_INVALID_ARG_TYPE'
30+
});
31+
assert.throws(() => blockList.addRange('1.1.1.1', i), {
32+
code: 'ERR_INVALID_ARG_TYPE'
33+
});
34+
});
35+
36+
[1, [], {}, null, 1n, null].forEach((i) => {
37+
assert.throws(() => blockList.addRange('1.1.1.1', '1.1.1.2', i), {
38+
code: 'ERR_INVALID_ARG_TYPE'
39+
});
40+
});
41+
42+
assert.throws(() => blockList.addRange('1.1.1.1', '1.1.1.2', 'foo'), {
43+
code: 'ERR_INVALID_ARG_VALUE'
44+
});
45+
}
46+
47+
{
48+
const blockList = new BlockList();
49+
blockList.addAddress('1.1.1.1');
50+
blockList.addAddress('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17', 'ipv6');
51+
blockList.addAddress('::ffff:1.1.1.2', 'ipv6');
52+
53+
assert(blockList.check('1.1.1.1'));
54+
assert(!blockList.check('1.1.1.1', 'ipv6'));
55+
assert(!blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17'));
56+
assert(blockList.check('8592:757c:efae:4e45:fb5d:d62a:0d00:8e17', 'ipv6'));
57+
58+
assert(blockList.check('::ffff:1.1.1.1', 'ipv6'));
59+
60+
assert(blockList.check('1.1.1.2'));
61+
62+
assert(!blockList.check('1.2.3.4'));
63+
assert(!blockList.check('::1', 'ipv6'));
64+
}
65+
66+
{
67+
const blockList = new BlockList();
68+
blockList.addRange('1.1.1.1', '1.1.1.10');
69+
blockList.addRange('::1', '::f', 'ipv6');
70+
71+
assert(!blockList.check('1.1.1.0'));
72+
for (let n = 1; n <= 10; n++)
73+
assert(blockList.check(`1.1.1.${n}`));
74+
assert(!blockList.check('1.1.1.11'));
75+
76+
assert(!blockList.check('::0', 'ipv6'));
77+
for (let n = 0x1; n <= 0xf; n++) {
78+
assert(blockList.check(`::${n.toString(16)}`, 'ipv6'),
79+
`::${n.toString(16)} check failed`);
80+
}
81+
assert(!blockList.check('::10', 'ipv6'));
82+
}
83+
84+
{
85+
const blockList = new BlockList();
86+
blockList.addSubnet('1.1.1.0', 16);
87+
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
88+
89+
assert(blockList.check('1.1.0.1'));
90+
assert(blockList.check('1.1.1.1'));
91+
assert(!blockList.check('1.2.0.1'));
92+
assert(blockList.check('::ffff:1.1.0.1', 'ipv6'));
93+
94+
assert(blockList.check('8592:757c:efae:4e45:f::', 'ipv6'));
95+
assert(blockList.check('8592:757c:efae:4e45::f', 'ipv6'));
96+
assert(!blockList.check('8592:757c:efae:4f45::f', 'ipv6'));
97+
}
98+
99+
{
100+
const blockList = new BlockList();
101+
blockList.addAddress('1.1.1.1');
102+
blockList.addRange('10.0.0.1', '10.0.0.10');
103+
blockList.addSubnet('8592:757c:efae:4e45::', 64, 'ipv6');
104+
105+
const rulesCheck = [
106+
'Subnet: IPv6 8592:757c:efae:4e45::/64',
107+
'Range: IPv4 10.0.0.1-10.0.0.10',
108+
'Address: IPv4 1.1.1.1'
109+
];
110+
assert.deepStrictEqual(blockList.rules, rulesCheck);
111+
console.log(blockList);
112+
113+
assert(blockList.check('1.1.1.1'));
114+
assert(blockList.check('10.0.0.5'));
115+
assert(blockList.check('::ffff:10.0.0.5', 'ipv6'));
116+
assert(blockList.check('8592:757c:efae:4e45::f', 'ipv6'));
117+
118+
assert(!blockList.check('123.123.123.123'));
119+
assert(!blockList.check('8592:757c:efaf:4e45:fb5d:d62a:0d00:8e17', 'ipv6'));
120+
assert(!blockList.check('::ffff:123.123.123.123', 'ipv6'));
121+
}
122+
123+
{
124+
// This test validates boundaries of non-aligned CIDR bit prefixes
125+
const blockList = new BlockList();
126+
blockList.addSubnet('10.0.0.0', 27);
127+
blockList.addSubnet('8592:757c:efaf::', 51, 'ipv6');
128+
129+
for (let n = 0; n <= 31; n++)
130+
assert(blockList.check(`10.0.0.${n}`));
131+
assert(!blockList.check('10.0.0.32'));
132+
133+
assert(blockList.check('8592:757c:efaf:0:0:0:0:0', 'ipv6'));
134+
assert(blockList.check('8592:757c:efaf:1fff:ffff:ffff:ffff:ffff', 'ipv6'));
135+
assert(!blockList.check('8592:757c:efaf:2fff:ffff:ffff:ffff:ffff', 'ipv6'));
136+
}

0 commit comments

Comments
 (0)
Please sign in to comment.