mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
quic: use net.BlockList for limiting access to a QuicSocket
PR-URL: https://github.com/nodejs/node/pull/34741 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
@@ -1445,6 +1445,24 @@ error will be thrown if `quicsock.addEndpoint()` is called either after
|
||||
the `QuicSocket` has already started binding to the local ports, or after
|
||||
the `QuicSocket` has been destroyed.
|
||||
|
||||
#### `quicsocket.blockList`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* Type: {net.BlockList}
|
||||
|
||||
A {net.BlockList} instance used to define rules for remote IPv4 or IPv6
|
||||
addresses that this `QuicSocket` is not permitted to interact with. The
|
||||
rules can be specified as either specific individual addresses, ranges
|
||||
of addresses, or CIDR subnet ranges.
|
||||
|
||||
When listening as a server, if a packet is received from a blocked address,
|
||||
the packet will be ignored.
|
||||
|
||||
When connecting as a client, if the remote IP address is blocked, the
|
||||
connection attempt will be rejected.
|
||||
|
||||
#### `quicsocket.bound`
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
|
||||
@@ -54,6 +54,7 @@ const { Duplex } = require('stream');
|
||||
const {
|
||||
createSecureContext: _createSecureContext
|
||||
} = require('tls');
|
||||
const BlockList = require('internal/blocklist');
|
||||
const {
|
||||
translatePeerCertificate
|
||||
} = require('_tls_common');
|
||||
@@ -891,6 +892,7 @@ class QuicSocket extends EventEmitter {
|
||||
[kInternalState] = {
|
||||
alpn: undefined,
|
||||
bindPromise: undefined,
|
||||
blockList: undefined,
|
||||
client: undefined,
|
||||
closePromise: undefined,
|
||||
closePromiseResolve: undefined,
|
||||
@@ -1007,8 +1009,10 @@ class QuicSocket extends EventEmitter {
|
||||
this[async_id_symbol] = handle.getAsyncId();
|
||||
this[kInternalState].sharedState =
|
||||
new QuicSocketSharedState(handle.state);
|
||||
this[kInternalState].blockList = new BlockList(handle.blockList);
|
||||
} else {
|
||||
this[kInternalState].sharedState = undefined;
|
||||
this[kInternalState].blockList = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1303,6 +1307,9 @@ class QuicSocket extends EventEmitter {
|
||||
if (this.closing)
|
||||
throw new ERR_INVALID_STATE('QuicSocket is closing');
|
||||
|
||||
if (this.blockList.check(ip, type === AF_INET6 ? 'ipv6' : 'ipv4'))
|
||||
throw new ERR_OPERATION_FAILED(`${ip} failed BlockList check`);
|
||||
|
||||
return new QuicClientSession(this, options, type, ip);
|
||||
}
|
||||
|
||||
@@ -1458,6 +1465,10 @@ class QuicSocket extends EventEmitter {
|
||||
return this;
|
||||
}
|
||||
|
||||
get blockList() {
|
||||
return this[kInternalState]?.blockList;
|
||||
}
|
||||
|
||||
get endpoints() {
|
||||
return Array.from(this[kInternalState].endpoints);
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ enum QuicSessionStateFields {
|
||||
V(SMOOTHED_RTT, smoothed_rtt, "Smoothed RTT") \
|
||||
V(CWND, cwnd, "Cwnd") \
|
||||
V(RECEIVE_RATE, receive_rate, "Receive Rate / Sec") \
|
||||
V(SEND_RATE, send_rate, "Send Rate Sec")
|
||||
V(SEND_RATE, send_rate, "Send Rate Sec") \
|
||||
|
||||
#define V(name, _, __) IDX_QUIC_SESSION_STATS_##name,
|
||||
enum QuicSessionStatsIdx : int {
|
||||
|
||||
@@ -252,6 +252,7 @@ QuicSocket::QuicSocket(
|
||||
: AsyncWrap(quic_state->env(), wrap, AsyncWrap::PROVIDER_QUICSOCKET),
|
||||
StatsBase(quic_state->env(), wrap),
|
||||
alloc_info_(MakeAllocator()),
|
||||
block_list_(SocketAddressBlockListWrap::New(quic_state->env())),
|
||||
options_(options),
|
||||
state_(quic_state->env()->isolate()),
|
||||
max_connections_(max_connections),
|
||||
@@ -269,6 +270,12 @@ QuicSocket::QuicSocket(
|
||||
|
||||
EntropySource(token_secret_, kTokenSecretLen);
|
||||
|
||||
wrap->DefineOwnProperty(
|
||||
env()->context(),
|
||||
env()->block_list_string(),
|
||||
block_list_->object(),
|
||||
PropertyAttribute::ReadOnly).Check();
|
||||
|
||||
wrap->DefineOwnProperty(
|
||||
env()->context(),
|
||||
env()->state_string(),
|
||||
@@ -432,6 +439,12 @@ void QuicSocket::OnReceive(
|
||||
return;
|
||||
}
|
||||
|
||||
if (UNLIKELY(block_list_->Apply(remote_addr))) {
|
||||
Debug(this, "Ignoring blocked remote address: %s", remote_addr);
|
||||
IncrementStat(&QuicSocketStats::packets_ignored);
|
||||
return;
|
||||
}
|
||||
|
||||
IncrementStat(&QuicSocketStats::bytes_received, nread);
|
||||
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(buf.data());
|
||||
|
||||
@@ -516,6 +516,7 @@ class QuicSocket : public AsyncWrap,
|
||||
std::vector<BaseObjectPtr<QuicEndpoint>> endpoints_;
|
||||
SocketAddress::Map<BaseObjectWeakPtr<QuicEndpoint>> bound_endpoints_;
|
||||
BaseObjectWeakPtr<QuicEndpoint> preferred_endpoint_;
|
||||
BaseObjectPtr<SocketAddressBlockListWrap> block_list_;
|
||||
|
||||
uint32_t flags_ = 0;
|
||||
uint32_t options_ = 0;
|
||||
|
||||
52
test/parallel/test-quic-blocklist.js
Normal file
52
test/parallel/test-quic-blocklist.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// Flags: --no-warnings
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
if (!common.hasQuic)
|
||||
common.skip('missing quic');
|
||||
|
||||
const { createQuicSocket, BlockList } = require('net');
|
||||
const assert = require('assert');
|
||||
|
||||
const { key, cert, ca } = require('../common/quic');
|
||||
const { once } = require('events');
|
||||
|
||||
const idleTimeout = common.platformTimeout(1);
|
||||
const options = { key, cert, ca, alpn: 'zzz', idleTimeout };
|
||||
|
||||
const client = createQuicSocket({ client: options });
|
||||
const server = createQuicSocket({ server: options });
|
||||
|
||||
assert(client.blockList instanceof BlockList);
|
||||
assert(server.blockList instanceof BlockList);
|
||||
|
||||
client.blockList.addAddress('10.0.0.1');
|
||||
|
||||
assert(client.blockList.check('10.0.0.1'));
|
||||
|
||||
// Connection fails because the IP address is blocked
|
||||
assert.rejects(client.connect({ address: '10.0.0.1' }), {
|
||||
code: 'ERR_OPERATION_FAILED'
|
||||
}).then(common.mustCall());
|
||||
|
||||
server.blockList.addAddress('127.0.0.1');
|
||||
|
||||
(async () => {
|
||||
server.on('session', common.mustNotCall());
|
||||
|
||||
await server.listen();
|
||||
|
||||
const session = await client.connect({
|
||||
address: common.localhostIPv4,
|
||||
port: server.endpoints[0].address.port,
|
||||
idleTimeout,
|
||||
});
|
||||
|
||||
session.on('secure', common.mustNotCall());
|
||||
|
||||
await once(session, 'close');
|
||||
|
||||
await Promise.all([server.close(), client.close()]);
|
||||
|
||||
})().then(common.mustCall());
|
||||
Reference in New Issue
Block a user