Skip to content

Commit 53d58bb

Browse files
committed
net: fallback to IPv6-only when dual-stack bind fails
1 parent 7aaf9b4 commit 53d58bb

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

lib/net.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const {
6363
const assert = require('internal/assert');
6464
const {
6565
UV_EADDRINUSE,
66+
UV_EAFNOSUPPORT,
6667
UV_EBADF,
6768
UV_EINVAL,
6869
UV_ENOTCONN,
@@ -2264,6 +2265,19 @@ function createServerHandle(address, port, addressType, fd, flags) {
22642265
}
22652266
} else if (addressType === 6) {
22662267
err = handle.bind6(address, port, flags);
2268+
// On IPv6-only systems, setting IPV6_V6ONLY to false can fail with
2269+
// EAFNOSUPPORT because the IPv4 address family is unavailable. In that
2270+
// case, retry with an IPv6-only socket before falling back to IPv4.
2271+
if (err === UV_EAFNOSUPPORT &&
2272+
!(flags & TCPConstants.UV_TCP_IPV6ONLY)) {
2273+
handle.close();
2274+
handle = new TCP(TCPConstants.SERVER);
2275+
err = handle.bind6(
2276+
address,
2277+
port,
2278+
flags | TCPConstants.UV_TCP_IPV6ONLY,
2279+
);
2280+
}
22672281
} else {
22682282
err = handle.bind(address, port, flags);
22692283
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const net = require('net');
7+
const { internalBinding } = require('internal/test/binding');
8+
const { TCP, constants: TCPConstants } = internalBinding('tcp_wrap');
9+
const { UV_EAFNOSUPPORT } = internalBinding('uv');
10+
11+
const probe = new TCP(TCPConstants.SOCKET);
12+
if (probe.bind6('::1', 0, TCPConstants.UV_TCP_IPV6ONLY) !== 0) {
13+
probe.close();
14+
common.skip('no IPv6 support');
15+
}
16+
probe.close();
17+
18+
const bind6 = TCP.prototype.bind6;
19+
let firstBind = true;
20+
21+
TCP.prototype.bind6 = function(...args) {
22+
if (firstBind && args[2] === 0) {
23+
firstBind = false;
24+
return UV_EAFNOSUPPORT;
25+
}
26+
return bind6.apply(this, args);
27+
};
28+
29+
const server = net.createServer();
30+
server.on('error', common.mustNotCall());
31+
32+
server.listen({ port: 0 }, common.mustCall(() => {
33+
assert.strictEqual(server.address().family, 'IPv6');
34+
35+
server.close(common.mustCall(() => {
36+
TCP.prototype.bind6 = bind6;
37+
}));
38+
}));

0 commit comments

Comments
 (0)