summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOuyang Yadong <oyydoibh@gmail.com>2018-10-21 15:59:38 +0800
committerOuyang Yadong <oyydoibh@gmail.com>2018-11-22 21:45:08 +0800
commit33a25b29a4d654f5c2a5c74725862bccb2fcccfb (patch)
treea963b15bcba72cabfb317cbb00e47ff33753fbd9
parent91748dd89c652939d52f38b94afe9eae4eb8fd5d (diff)
downloadandroid-node-v8-33a25b29a4d654f5c2a5c74725862bccb2fcccfb.tar.gz
android-node-v8-33a25b29a4d654f5c2a5c74725862bccb2fcccfb.tar.bz2
android-node-v8-33a25b29a4d654f5c2a5c74725862bccb2fcccfb.zip
net,dgram: add ipv6Only option for net and dgram
For TCP servers, the dual-stack support is enable by default, i.e. binding host "::" will also make "0.0.0.0" bound. This commit add ipv6Only option in `net.Server.listen()` and `dgram.createSocket()` methods which allows to disable dual-stack support. Support for cluster module is also provided in this commit. Fixes: https://github.com/nodejs/node/issues/17664 PR-URL: https://github.com/nodejs/node/pull/23798 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: James M Snell <jasnell@gmail.com>
-rw-r--r--doc/api/dgram.md6
-rw-r--r--doc/api/net.md7
-rw-r--r--lib/dgram.js9
-rw-r--r--lib/internal/cluster/round_robin_handle.js14
-rw-r--r--lib/internal/cluster/shared_handle.js2
-rw-r--r--lib/net.js35
-rw-r--r--src/tcp_wrap.cc5
-rw-r--r--src/udp_wrap.cc6
-rw-r--r--test/parallel/test-cluster-dgram-ipv6only.js51
-rw-r--r--test/parallel/test-cluster-net-listen-ipv6only-false.js55
-rw-r--r--test/parallel/test-cluster-net-listen-ipv6only-none.js58
-rw-r--r--test/parallel/test-cluster-net-listen-ipv6only-rr.js58
-rw-r--r--test/parallel/test-dgram-ipv6only.js33
-rw-r--r--test/parallel/test-net-listen-ipv6only.js30
14 files changed, 347 insertions, 22 deletions
diff --git a/doc/api/dgram.md b/doc/api/dgram.md
index e263627723..31abaf450c 100644
--- a/doc/api/dgram.md
+++ b/doc/api/dgram.md
@@ -601,6 +601,9 @@ changes:
pr-url: https://github.com/nodejs/node/pull/13623
description: The `recvBufferSize` and `sendBufferSize` options are
supported now.
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/23798
+ description: The `ipv6Only` option is supported.
-->
* `options` {Object} Available options are:
@@ -609,6 +612,9 @@ changes:
* `reuseAddr` {boolean} When `true` [`socket.bind()`][] will reuse the
address, even if another process has already bound a socket on it.
**Default:** `false`.
+ * `ipv6Only` {boolean} Setting `ipv6Only` to `true` will
+ disable dual-stack support, i.e., binding to address `::` won't make
+ `0.0.0.0` be bound. **Default:** `false`.
* `recvBufferSize` {number} - Sets the `SO_RCVBUF` socket value.
* `sendBufferSize` {number} - Sets the `SO_SNDBUF` socket value.
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
diff --git a/doc/api/net.md b/doc/api/net.md
index b925245e7a..35fb0a6171 100644
--- a/doc/api/net.md
+++ b/doc/api/net.md
@@ -252,6 +252,10 @@ Listening on a file descriptor is not supported on Windows.
#### server.listen(options[, callback])
<!-- YAML
added: v0.11.14
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/23798
+ description: The `ipv6Only` option is supported.
-->
* `options` {Object} Required. Supports the following properties:
@@ -266,6 +270,9 @@ added: v0.11.14
for all users. **Default:** `false`
* `writableAll` {boolean} For IPC servers makes the pipe writable
for all users. **Default:** `false`
+ * `ipv6Only` {boolean} For TCP servers, setting `ipv6Only` to `true` will
+ disable dual-stack support, i.e., binding to host `::` won't make
+ `0.0.0.0` be bound. **Default:** `false`.
* `callback` {Function} Common parameter of [`server.listen()`][]
functions.
* Returns: {net.Server}
diff --git a/lib/dgram.js b/lib/dgram.js
index 549b6dd738..55662313d6 100644
--- a/lib/dgram.js
+++ b/lib/dgram.js
@@ -54,7 +54,11 @@ const {
} = require('internal/async_hooks');
const { UV_UDP_REUSEADDR } = internalBinding('constants').os;
-const { UDP, SendWrap } = internalBinding('udp_wrap');
+const {
+ constants: { UV_UDP_IPV6ONLY },
+ UDP,
+ SendWrap
+} = internalBinding('udp_wrap');
const BIND_STATE_UNBOUND = 0;
const BIND_STATE_BINDING = 1;
@@ -99,6 +103,7 @@ function Socket(type, listener) {
bindState: BIND_STATE_UNBOUND,
queue: undefined,
reuseAddr: options && options.reuseAddr, // Use UV_UDP_REUSEADDR if true.
+ ipv6Only: options && options.ipv6Only,
recvBufferSize,
sendBufferSize
};
@@ -270,6 +275,8 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) {
var flags = 0;
if (state.reuseAddr)
flags |= UV_UDP_REUSEADDR;
+ if (state.ipv6Only)
+ flags |= UV_UDP_IPV6ONLY;
if (cluster.isWorker && !exclusive) {
bindServerHandle(this, {
diff --git a/lib/internal/cluster/round_robin_handle.js b/lib/internal/cluster/round_robin_handle.js
index 6b2bc37254..95f33ad1d0 100644
--- a/lib/internal/cluster/round_robin_handle.js
+++ b/lib/internal/cluster/round_robin_handle.js
@@ -3,10 +3,11 @@ const assert = require('assert');
const net = require('net');
const { sendHelper } = require('internal/cluster/utils');
const uv = internalBinding('uv');
+const { constants } = internalBinding('tcp_wrap');
module.exports = RoundRobinHandle;
-function RoundRobinHandle(key, address, port, addressType, fd) {
+function RoundRobinHandle(key, address, port, addressType, fd, flags) {
this.key = key;
this.all = new Map();
this.free = [];
@@ -16,9 +17,14 @@ function RoundRobinHandle(key, address, port, addressType, fd) {
if (fd >= 0)
this.server.listen({ fd });
- else if (port >= 0)
- this.server.listen(port, address);
- else
+ else if (port >= 0) {
+ this.server.listen({
+ port,
+ host: address,
+ // Currently, net module only supports `ipv6Only` option in `flags`.
+ ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY),
+ });
+ } else
this.server.listen(address); // UNIX socket path.
this.server.once('listening', () => {
diff --git a/lib/internal/cluster/shared_handle.js b/lib/internal/cluster/shared_handle.js
index 0bb8c44f5d..0b5f153193 100644
--- a/lib/internal/cluster/shared_handle.js
+++ b/lib/internal/cluster/shared_handle.js
@@ -15,7 +15,7 @@ function SharedHandle(key, address, port, addressType, fd, flags) {
if (addressType === 'udp4' || addressType === 'udp6')
rval = dgram._createSocketHandle(address, port, addressType, fd, flags);
else
- rval = net._createServerHandle(address, port, addressType, fd);
+ rval = net._createServerHandle(address, port, addressType, fd, flags);
if (typeof rval === 'number')
this.errno = rval;
diff --git a/lib/net.js b/lib/net.js
index 01cfed98e8..25767b2574 100644
--- a/lib/net.js
+++ b/lib/net.js
@@ -97,6 +97,10 @@ const {
function noop() {}
+function getFlags(ipv6Only) {
+ return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0;
+}
+
function createHandle(fd, is_server) {
validateInt32(fd, 'fd', 0);
const type = TTYWrap.guessHandleType(fd);
@@ -798,7 +802,7 @@ function checkBindError(err, port, handle) {
function internalConnect(
- self, address, port, addressType, localAddress, localPort) {
+ self, address, port, addressType, localAddress, localPort, flags) {
// TODO return promise from Socket.prototype.connect which
// wraps _connectReq.
@@ -812,7 +816,7 @@ function internalConnect(
err = self._handle.bind(localAddress, localPort);
} else { // addressType === 6
localAddress = localAddress || '::';
- err = self._handle.bind6(localAddress, localPort);
+ err = self._handle.bind6(localAddress, localPort, flags);
}
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
localAddress, localPort, addressType);
@@ -1148,7 +1152,7 @@ util.inherits(Server, EventEmitter);
function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
// Returns handle if it can be created, or error code if it can't
-function createServerHandle(address, port, addressType, fd) {
+function createServerHandle(address, port, addressType, fd, flags) {
var err = 0;
// assign handle in listen, and clean up if bind or listen fails
var handle;
@@ -1187,14 +1191,14 @@ function createServerHandle(address, port, addressType, fd) {
debug('bind to', address || 'any');
if (!address) {
// Try binding to ipv6 first
- err = handle.bind6('::', port);
+ err = handle.bind6('::', port, flags);
if (err) {
handle.close();
// Fallback to ipv4
return createServerHandle('0.0.0.0', port);
}
} else if (addressType === 6) {
- err = handle.bind6(address, port);
+ err = handle.bind6(address, port, flags);
} else {
err = handle.bind(address, port);
}
@@ -1208,7 +1212,7 @@ function createServerHandle(address, port, addressType, fd) {
return handle;
}
-function setupListenHandle(address, port, addressType, backlog, fd) {
+function setupListenHandle(address, port, addressType, backlog, fd, flags) {
debug('setupListenHandle', address, port, addressType, backlog, fd);
// If there is not yet a handle, we need to create one and bind.
@@ -1222,7 +1226,7 @@ function setupListenHandle(address, port, addressType, backlog, fd) {
// Try to bind to the unspecified IPv6 address, see if IPv6 is available
if (!address && typeof fd !== 'number') {
- rval = createServerHandle('::', port, 6, fd);
+ rval = createServerHandle('::', port, 6, fd, flags);
if (typeof rval === 'number') {
rval = null;
@@ -1235,7 +1239,7 @@ function setupListenHandle(address, port, addressType, backlog, fd) {
}
if (rval === null)
- rval = createServerHandle(address, port, addressType, fd);
+ rval = createServerHandle(address, port, addressType, fd, flags);
if (typeof rval === 'number') {
var error = uvExceptionWithHostPort(rval, 'listen', address, port);
@@ -1294,7 +1298,7 @@ function emitListeningNT(self) {
function listenInCluster(server, address, port, addressType,
- backlog, fd, exclusive) {
+ backlog, fd, exclusive, flags) {
exclusive = !!exclusive;
if (cluster === undefined) cluster = require('cluster');
@@ -1303,7 +1307,7 @@ function listenInCluster(server, address, port, addressType,
// Will create a new handle
// _listen2 sets up the listened handle, it is still named like this
// to avoid breaking code that wraps this method
- server._listen2(address, port, addressType, backlog, fd);
+ server._listen2(address, port, addressType, backlog, fd, flags);
return;
}
@@ -1312,7 +1316,7 @@ function listenInCluster(server, address, port, addressType,
port: port,
addressType: addressType,
fd: fd,
- flags: 0
+ flags,
};
// Get the master's server handle, and listen on it
@@ -1330,7 +1334,7 @@ function listenInCluster(server, address, port, addressType,
server._handle = handle;
// _listen2 sets up the listened handle, it is still named like this
// to avoid breaking code that wraps this method
- server._listen2(address, port, addressType, backlog, fd);
+ server._listen2(address, port, addressType, backlog, fd, flags);
}
}
@@ -1353,6 +1357,7 @@ Server.prototype.listen = function(...args) {
toNumber(args.length > 2 && args[2]); // (port, host, backlog)
options = options._handle || options.handle || options;
+ const flags = getFlags(options.ipv6Only);
// (handle[, backlog][, cb]) where handle is an object with a handle
if (options instanceof TCP) {
this._handle = options;
@@ -1387,7 +1392,7 @@ Server.prototype.listen = function(...args) {
// start TCP server listening on host:port
if (options.host) {
lookupAndListen(this, options.port | 0, options.host, backlog,
- options.exclusive);
+ options.exclusive, flags);
} else { // Undefined host, listens on unspecified address
// Default addressType 4 will be used to search for master server
listenInCluster(this, null, options.port | 0, 4,
@@ -1434,7 +1439,7 @@ Server.prototype.listen = function(...args) {
throw new ERR_INVALID_OPT_VALUE('options', util.inspect(options));
};
-function lookupAndListen(self, port, address, backlog, exclusive) {
+function lookupAndListen(self, port, address, backlog, exclusive, flags) {
if (dns === undefined) dns = require('dns');
dns.lookup(address, function doListen(err, ip, addressType) {
if (err) {
@@ -1442,7 +1447,7 @@ function lookupAndListen(self, port, address, backlog, exclusive) {
} else {
addressType = ip ? addressType : 4;
listenInCluster(self, ip, port, addressType,
- backlog, undefined, exclusive);
+ backlog, undefined, exclusive, flags);
}
});
}
diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc
index e1316b42cd..504fda3de6 100644
--- a/src/tcp_wrap.cc
+++ b/src/tcp_wrap.cc
@@ -127,6 +127,7 @@ void TCPWrap::Initialize(Local<Object> target,
Local<Object> constants = Object::New(env->isolate());
NODE_DEFINE_CONSTANT(constants, SOCKET);
NODE_DEFINE_CONSTANT(constants, SERVER);
+ NODE_DEFINE_CONSTANT(constants, UV_TCP_IPV6ONLY);
target->Set(context,
env->constants_string(),
constants).FromJust();
@@ -252,13 +253,15 @@ void TCPWrap::Bind6(const FunctionCallbackInfo<Value>& args) {
Environment* env = wrap->env();
node::Utf8Value ip6_address(env->isolate(), args[0]);
int port;
+ unsigned int flags;
if (!args[1]->Int32Value(env->context()).To(&port)) return;
+ if (!args[2]->Uint32Value(env->context()).To(&flags)) return;
sockaddr_in6 addr;
int err = uv_ip6_addr(*ip6_address, port, &addr);
if (err == 0) {
err = uv_tcp_bind(&wrap->handle_,
reinterpret_cast<const sockaddr*>(&addr),
- 0);
+ flags);
}
args.GetReturnValue().Set(err);
}
diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc
index 4c7072f2a2..b4c859e594 100644
--- a/src/udp_wrap.cc
+++ b/src/udp_wrap.cc
@@ -149,6 +149,12 @@ void UDPWrap::Initialize(Local<Object> target,
target->Set(env->context(),
sendWrapString,
swt->GetFunction(env->context()).ToLocalChecked()).FromJust();
+
+ Local<Object> constants = Object::New(env->isolate());
+ NODE_DEFINE_CONSTANT(constants, UV_UDP_IPV6ONLY);
+ target->Set(context,
+ env->constants_string(),
+ constants).FromJust();
}
diff --git a/test/parallel/test-cluster-dgram-ipv6only.js b/test/parallel/test-cluster-dgram-ipv6only.js
new file mode 100644
index 0000000000..0906baec92
--- /dev/null
+++ b/test/parallel/test-cluster-dgram-ipv6only.js
@@ -0,0 +1,51 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+if (common.isWindows)
+ common.skip('dgram clustering is currently not supported on windows.');
+
+const assert = require('assert');
+const cluster = require('cluster');
+const dgram = require('dgram');
+
+// This test ensures that the `ipv6Only` option in `dgram.createSock()`
+// works as expected.
+if (cluster.isMaster) {
+ cluster.fork().on('exit', common.mustCall((code) => {
+ assert.strictEqual(code, 0);
+ }));
+} else {
+ let waiting = 2;
+ function close() {
+ if (--waiting === 0)
+ cluster.worker.disconnect();
+ }
+
+ const socket1 = dgram.createSocket({
+ type: 'udp6',
+ ipv6Only: true
+ });
+ const socket2 = dgram.createSocket({
+ type: 'udp4',
+ });
+ socket1.on('error', common.mustNotCall());
+ socket2.on('error', common.mustNotCall());
+
+ socket1.bind({
+ port: 0,
+ address: '::',
+ }, common.mustCall(() => {
+ const { port } = socket1.address();
+ socket2.bind({
+ port,
+ address: '0.0.0.0',
+ }, common.mustCall(() => {
+ process.nextTick(() => {
+ socket1.close(close);
+ socket2.close(close);
+ });
+ }));
+ }));
+}
diff --git a/test/parallel/test-cluster-net-listen-ipv6only-false.js b/test/parallel/test-cluster-net-listen-ipv6only-false.js
new file mode 100644
index 0000000000..4d495d8faf
--- /dev/null
+++ b/test/parallel/test-cluster-net-listen-ipv6only-false.js
@@ -0,0 +1,55 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+
+const assert = require('assert');
+const cluster = require('cluster');
+const net = require('net');
+const Countdown = require('../common/countdown');
+
+// This test ensures that dual-stack support still works for cluster module
+// when `ipv6Only` is not `true`.
+const host = '::';
+const WORKER_COUNT = 3;
+
+if (cluster.isMaster) {
+ const workers = new Map();
+ let address;
+
+ const countdown = new Countdown(WORKER_COUNT, () => {
+ const socket = net.connect({
+ port: address.port,
+ host: '0.0.0.0',
+ }, common.mustCall(() => {
+ socket.destroy();
+ workers.forEach((worker) => {
+ worker.disconnect();
+ });
+ }));
+ socket.on('error', common.mustNotCall());
+ });
+
+ for (let i = 0; i < WORKER_COUNT; i += 1) {
+ const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
+ assert.strictEqual(statusCode, 0);
+ })).on('listening', common.mustCall((workerAddress) => {
+ if (!address) {
+ address = workerAddress;
+ } else {
+ assert.strictEqual(address.addressType, workerAddress.addressType);
+ assert.strictEqual(address.host, workerAddress.host);
+ assert.strictEqual(address.port, workerAddress.port);
+ }
+ countdown.dec();
+ }));
+
+ workers.set(i, worker);
+ }
+} else {
+ net.createServer().listen({
+ host,
+ port: 0,
+ }, common.mustCall());
+}
diff --git a/test/parallel/test-cluster-net-listen-ipv6only-none.js b/test/parallel/test-cluster-net-listen-ipv6only-none.js
new file mode 100644
index 0000000000..401afbc035
--- /dev/null
+++ b/test/parallel/test-cluster-net-listen-ipv6only-none.js
@@ -0,0 +1,58 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+
+const assert = require('assert');
+const cluster = require('cluster');
+const net = require('net');
+const Countdown = require('../common/countdown');
+
+// This test ensures that the `ipv6Only` option in `net.Server.listen()`
+// works as expected when we use cluster with `SCHED_NONE` schedulingPolicy.
+cluster.schedulingPolicy = cluster.SCHED_NONE;
+const host = '::';
+const WORKER_ACCOUNT = 3;
+
+if (cluster.isMaster) {
+ const workers = new Map();
+ let address;
+
+ const countdown = new Countdown(WORKER_ACCOUNT, () => {
+ // Make sure the `ipv6Only` option works.
+ const server = net.createServer().listen({
+ host: '0.0.0.0',
+ port: address.port,
+ }, common.mustCall(() => {
+ // Exit.
+ server.close();
+ workers.forEach((worker) => {
+ worker.disconnect();
+ });
+ }));
+ });
+
+ for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
+ const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
+ assert.strictEqual(statusCode, 0);
+ })).on('listening', common.mustCall((workerAddress) => {
+ if (!address) {
+ address = workerAddress;
+ } else {
+ assert.strictEqual(address.addressType, workerAddress.addressType);
+ assert.strictEqual(address.host, workerAddress.host);
+ assert.strictEqual(address.port, workerAddress.port);
+ }
+ countdown.dec();
+ }));
+
+ workers.set(i, worker);
+ }
+} else {
+ net.createServer().listen({
+ host,
+ port: 0,
+ ipv6Only: true,
+ }, common.mustCall());
+}
diff --git a/test/parallel/test-cluster-net-listen-ipv6only-rr.js b/test/parallel/test-cluster-net-listen-ipv6only-rr.js
new file mode 100644
index 0000000000..de254a4fe9
--- /dev/null
+++ b/test/parallel/test-cluster-net-listen-ipv6only-rr.js
@@ -0,0 +1,58 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+
+const assert = require('assert');
+const cluster = require('cluster');
+const net = require('net');
+const Countdown = require('../common/countdown');
+
+// This test ensures that the `ipv6Only` option in `net.Server.listen()`
+// works as expected when we use cluster with `SCHED_RR` schedulingPolicy.
+cluster.schedulingPolicy = cluster.SCHED_RR;
+const host = '::';
+const WORKER_ACCOUNT = 3;
+
+if (cluster.isMaster) {
+ const workers = new Map();
+ let address;
+
+ const countdown = new Countdown(WORKER_ACCOUNT, () => {
+ // Make sure the `ipv6Only` option works.
+ const server = net.createServer().listen({
+ host: '0.0.0.0',
+ port: address.port,
+ }, common.mustCall(() => {
+ // Exit.
+ server.close();
+ workers.forEach((worker) => {
+ worker.disconnect();
+ });
+ }));
+ });
+
+ for (let i = 0; i < WORKER_ACCOUNT; i += 1) {
+ const worker = cluster.fork().on('exit', common.mustCall((statusCode) => {
+ assert.strictEqual(statusCode, 0);
+ })).on('listening', common.mustCall((workerAddress) => {
+ if (!address) {
+ address = workerAddress;
+ } else {
+ assert.strictEqual(address.addressType, workerAddress.addressType);
+ assert.strictEqual(address.host, workerAddress.host);
+ assert.strictEqual(address.port, workerAddress.port);
+ }
+ countdown.dec();
+ }));
+
+ workers.set(i, worker);
+ }
+} else {
+ net.createServer().listen({
+ host,
+ port: 0,
+ ipv6Only: true,
+ }, common.mustCall());
+}
diff --git a/test/parallel/test-dgram-ipv6only.js b/test/parallel/test-dgram-ipv6only.js
new file mode 100644
index 0000000000..1187f3084a
--- /dev/null
+++ b/test/parallel/test-dgram-ipv6only.js
@@ -0,0 +1,33 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+
+const dgram = require('dgram');
+
+// This test ensures that dual-stack support is disabled when
+// we specify the `ipv6Only` option in `dgram.createSocket()`.
+const socket = dgram.createSocket({
+ type: 'udp6',
+ ipv6Only: true,
+});
+
+socket.bind({
+ port: 0,
+ address: '::',
+}, common.mustCall(() => {
+ const { port } = socket.address();
+ const client = dgram.createSocket('udp4');
+
+ // We can still bind to '0.0.0.0'.
+ client.bind({
+ port,
+ address: '0.0.0.0',
+ }, common.mustCall(() => {
+ client.close();
+ socket.close();
+ }));
+
+ client.on('error', common.mustNotCall());
+}));
diff --git a/test/parallel/test-net-listen-ipv6only.js b/test/parallel/test-net-listen-ipv6only.js
new file mode 100644
index 0000000000..a329011bcc
--- /dev/null
+++ b/test/parallel/test-net-listen-ipv6only.js
@@ -0,0 +1,30 @@
+'use strict';
+
+const common = require('../common');
+if (!common.hasIPv6)
+ common.skip('no IPv6 support');
+
+// This test ensures that dual-stack support is disabled when
+// we specify the `ipv6Only` option in `net.Server.listen()`.
+const assert = require('assert');
+const net = require('net');
+
+const host = '::';
+const server = net.createServer();
+server.listen({
+ host,
+ port: 0,
+ ipv6Only: true,
+}, common.mustCall(() => {
+ const { port } = server.address();
+ const socket = net.connect({
+ host: '0.0.0.0',
+ port,
+ });
+
+ socket.on('connect', common.mustNotCall());
+ socket.on('error', common.mustCall((err) => {
+ assert.strictEqual(err.code, 'ECONNREFUSED');
+ server.close();
+ }));
+}));