Skip to content

Commit c7d859e

Browse files
committed
quic: refactor and improve ipv6Only
Ignore `ipv6Only: true` when binding to `'udp4'` (this differs from dgram which will still attempt to apply the flag and will fail during bind). Improve the test so that it should work consistently. Signed-off-by: James M Snell <[email protected]> PR-URL: #33935 Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 1b7434d commit c7d859e

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

Diff for: doc/api/quic.md

+15-6
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,11 @@ added: REPLACEME
261261
to an IP address.
262262
* `port` {number} The local port to bind to.
263263
* `type` {string} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6,
264-
respectively.
265-
* `ipv6Only` {boolean}
264+
respectively. **Default**: `'udp4'`.
265+
* `ipv6Only` {boolean} If `type` is `'udp6'`, then setting `ipv6Only` to
266+
`true` will disable dual-stack support on the UDP binding -- that is,
267+
binding to address `'::'` will not make `'0.0.0.0'` be bound. The option
268+
is ignored if `type` is `'udp4'`. **Default**: `false`.
266269
* `lookup` {Function} A custom DNS lookup function. Default `dns.lookup()`.
267270
* `maxConnections` {number} The maximum number of total active inbound
268271
connections.
@@ -1387,8 +1390,11 @@ added: REPLACEME
13871390
to an IP address.
13881391
* `port` {number} The local port to bind to.
13891392
* `type` {string} Either `'udp4'` or `'upd6'` to use either IPv4 or IPv6,
1390-
respectively.
1391-
* `ipv6Only` {boolean}
1393+
respectively. **Default**: `'udp4'`.
1394+
* `ipv6Only` {boolean} If `type` is `'udp6'`, then setting `ipv6Only` to
1395+
`true` will disable dual-stack support on the UDP binding -- that is,
1396+
binding to address `'::'` will not make `'0.0.0.0'` be bound. The option
1397+
is ignored if `type` is `'udp4'`. **Default**: `false`.
13921398
* Returns: {QuicEndpoint}
13931399

13941400
Creates and adds a new `QuicEndpoint` to the `QuicSocket` instance.
@@ -1519,7 +1525,10 @@ added: REPLACEME
15191525
`SSL_OP_CIPHER_SERVER_PREFERENCE` to be set in `secureOptions`, see
15201526
[OpenSSL Options][] for more information.
15211527
* `idleTimeout` {number}
1522-
* `ipv6Only` {boolean}
1528+
* `ipv6Only` {boolean} If `type` is `'udp6'`, then setting `ipv6Only` to
1529+
`true` will disable dual-stack support on the UDP binding -- that is,
1530+
binding to address `'::'` will not make `'0.0.0.0'` be bound. The option
1531+
is ignored if `type` is `'udp4'`. **Default**: `false`.
15231532
* `key` {string|string[]|Buffer|Buffer[]|Object[]} Private keys in PEM format.
15241533
PEM allows the option of private keys being encrypted. Encrypted keys will
15251534
be decrypted with `options.passphrase`. Multiple keys using different
@@ -1578,7 +1587,7 @@ added: REPLACEME
15781587
`QuicClientSession` object.
15791588
* `type`: {string} Identifies the type of UDP socket. The value must either
15801589
be `'udp4'`, indicating UDP over IPv4, or `'udp6'`, indicating UDP over
1581-
IPv6. Defaults to `'udp4'`.
1590+
IPv6. **Default**: `'udp4'`.
15821591

15831592
Create a new `QuicClientSession`. This function can be called multiple times
15841593
to create sessions associated with different endpoints on the same

Diff for: lib/internal/quic/core.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ class QuicEndpoint {
667667
this.#lookup = lookup || (type === AF_INET6 ? lookup6 : lookup4);
668668
this.#port = port;
669669
this.#reuseAddr = !!reuseAddr;
670+
this.#type = type;
670671
this.#udpSocket = dgram.createSocket(type === AF_INET6 ? 'udp6' : 'udp4');
671672

672673
// kUDPHandleForTesting is only used in the Node.js test suite to
@@ -688,7 +689,7 @@ class QuicEndpoint {
688689
const obj = {
689690
address: this.address,
690691
fd: this.#fd,
691-
type: this.#type
692+
type: this.#type === AF_INET6 ? 'udp6' : 'udp4'
692693
};
693694
return `QuicEndpoint ${util.format(obj)}`;
694695
}
@@ -726,9 +727,10 @@ class QuicEndpoint {
726727
// what conditions does this happen?
727728
return;
728729
}
730+
729731
const flags =
730732
(this.#reuseAddr ? UV_UDP_REUSEADDR : 0) |
731-
(this.#ipv6Only ? UV_UDP_IPV6ONLY : 0);
733+
(this.#type === AF_INET6 && this.#ipv6Only ? UV_UDP_IPV6ONLY : 0);
732734

733735
const ret = udpHandle.bind(ip, this.#port, flags);
734736
if (ret) {

Diff for: test/parallel/test-quic-ipv6only.js

+26-18
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,24 @@ const { once } = require('events');
1616

1717
const kALPN = 'zzz';
1818

19-
// Setting `type` to `udp4` while setting `ipv6Only` to `true` is possible
20-
// and it will throw an error.
21-
{
19+
// Setting `type` to `udp4` while setting `ipv6Only` to `true` is possible.
20+
// The ipv6Only setting will be ignored.
21+
async function ipv4() {
2222
const server = createQuicSocket({
2323
endpoint: {
2424
type: 'udp4',
2525
ipv6Only: true
26-
} });
27-
28-
server.on('error', common.mustCall((err) => {
29-
assert.strictEqual(err.code, 'EINVAL');
30-
assert.strictEqual(err.message, 'bind EINVAL 0.0.0.0');
31-
}));
32-
26+
}
27+
});
28+
server.on('error', common.mustNotCall());
3329
server.listen({ key, cert, ca, alpn: kALPN });
30+
await once(server, 'ready');
31+
server.close();
3432
}
3533

36-
// Connecting ipv6 server by "127.0.0.1" should work when `ipv6Only`
37-
// is set to `false`.
38-
(async () => {
34+
// Connecting to ipv6 server using "127.0.0.1" should work when
35+
// `ipv6Only` is set to `false`.
36+
async function ipv6() {
3937
const server = createQuicSocket({
4038
endpoint: {
4139
type: 'udp6',
@@ -54,6 +52,7 @@ const kALPN = 'zzz';
5452
const session = client.connect({
5553
address: common.localhostIPv4,
5654
port: server.endpoints[0].address.port,
55+
ipv6Only: true,
5756
});
5857

5958
await once(session, 'secure');
@@ -70,11 +69,11 @@ const kALPN = 'zzz';
7069
once(client, 'close'),
7170
once(server, 'close')
7271
]);
73-
})().then(common.mustCall());
72+
}
7473

7574
// When the `ipv6Only` set to `true`, a client cann't connect to it
7675
// through "127.0.0.1".
77-
(async () => {
76+
async function ipv6Only() {
7877
const server = createQuicSocket({
7978
endpoint: {
8079
type: 'udp6',
@@ -87,10 +86,13 @@ const kALPN = 'zzz';
8786

8887
await once(server, 'ready');
8988

89+
// This will attempt to connect to the ipv4 localhost address
90+
// but should fail as the connection idle timeout expires.
9091
const session = client.connect({
9192
address: common.localhostIPv4,
9293
port: server.endpoints[0].address.port,
9394
idleTimeout: common.platformTimeout(1),
95+
ipv6Only: true,
9496
});
9597

9698
session.on('secure', common.mustNotCall());
@@ -104,11 +106,11 @@ const kALPN = 'zzz';
104106
once(client, 'close'),
105107
once(server, 'close')
106108
]);
107-
})();
109+
}
108110

109111
// Creating the QuicSession fails when connect type does not match the
110112
// the connect IP address...
111-
(async () => {
113+
async function mismatch() {
112114
const server = createQuicSocket({ endpoint: { type: 'udp6' } });
113115
const client = createQuicSocket({ client: { key, cert, ca, alpn: kALPN } });
114116

@@ -137,4 +139,10 @@ const kALPN = 'zzz';
137139
once(client, 'close'),
138140
once(server, 'close')
139141
]);
140-
})().then(common.mustCall());
142+
}
143+
144+
ipv4()
145+
.then(ipv6)
146+
.then(ipv6Only)
147+
.then(mismatch)
148+
.then(common.mustCall());

0 commit comments

Comments
 (0)