Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f5c0e28
http2: allow Host in HTTP/2 requests
mildsunrise Aug 7, 2020
0e30c5b
http2: use and support non-empty DATA frame with END_STREAM flag
clshortfuse Jun 14, 2020
92167e2
doc: fix broken links in commit-queue.md
lpinca Aug 15, 2020
8b818cf
build: add CODEOWNERS linter action
mmarchini Aug 11, 2020
1e6e5c3
quic: resolve minor TODO in QuicSocket
jasnell Aug 6, 2020
34165f0
quic: resolve some minor TODOs
jasnell Aug 7, 2020
344c5e4
quic: limit push check to http/3
jasnell Aug 7, 2020
10d5047
quic: fixup set_socket, fix skipped test
jasnell Aug 7, 2020
442968c
quic: check setSocket natRebinding argument, extend test
jasnell Aug 7, 2020
c17eaa3
quic: add natRebinding argument to docs
jasnell Aug 7, 2020
2405922
quic: fixup session ticket app data todo comments
jasnell Aug 7, 2020
19e712b
quic: resolve InitializeSecureContext TODO comment
jasnell Aug 7, 2020
94aa291
quic: clarify TODO statements
jasnell Aug 10, 2020
bfc3535
quic: consolidate stats collecting in QuicSession
jasnell Aug 10, 2020
1c14810
src: allow instances of net.BlockList to be created internally
jasnell Aug 11, 2020
c855c3e
quic: use net.BlockList for limiting access to a QuicSocket
jasnell Aug 11, 2020
5835367
meta: fix codeowners docs path
mmarchini Aug 17, 2020
81df668
worker: do not crash when JSTransferable lists untransferable value
addaleax Aug 13, 2020
0eca660
tools: update ESLint to 7.7.0
cjihrig Aug 15, 2020
c62cf1d
doc: edit filehandle.close() entry in fs.md
Trott Aug 14, 2020
6726246
lib: allow to validate enums with validateOneOf
lundibundi Jun 26, 2020
15fdd98
doc,lib: remove unused error code
Trott Aug 15, 2020
aa5361c
test: convert assertion that always fails to assert.fail()
Trott Aug 16, 2020
60d572c
doc: remove "is recommended from crypto legacy API text
Trott Aug 9, 2020
ca5ff72
doc: deprecate (doc-only) crypto.Certificate()
Trott Aug 16, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
quic: use net.BlockList for limiting access to a QuicSocket
PR-URL: #34741
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
  • Loading branch information
jasnell committed Aug 17, 2020
commit c855c3e8ca5142e7b7f4a4e6adbf43e0ab18439b
18 changes: 18 additions & 0 deletions doc/api/quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions lib/internal/quic/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const { Duplex } = require('stream');
const {
createSecureContext: _createSecureContext
} = require('tls');
const BlockList = require('internal/blocklist');
const {
translatePeerCertificate
} = require('_tls_common');
Expand Down Expand Up @@ -891,6 +892,7 @@ class QuicSocket extends EventEmitter {
[kInternalState] = {
alpn: undefined,
bindPromise: undefined,
blockList: undefined,
client: undefined,
closePromise: undefined,
closePromiseResolve: undefined,
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -1458,6 +1465,10 @@ class QuicSocket extends EventEmitter {
return this;
}

get blockList() {
return this[kInternalState]?.blockList;
}

get endpoints() {
return Array.from(this[kInternalState].endpoints);
}
Expand Down
2 changes: 1 addition & 1 deletion src/quic/node_quic_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
13 changes: 13 additions & 0 deletions src/quic/node_quic_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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(),
Expand Down Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions src/quic/node_quic_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
52 changes: 52 additions & 0 deletions test/parallel/test-quic-blocklist.js
Original file line number Diff line number Diff line change
@@ -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());