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>
This commit is contained in:
parent
91748dd89c
commit
33a25b29a4
@ -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()`][].
|
||||
|
@ -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}
|
||||
|
@ -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, {
|
||||
|
@ -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', () => {
|
||||
|
@ -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;
|
||||
|
35
lib/net.js
35
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
51
test/parallel/test-cluster-dgram-ipv6only.js
Normal file
51
test/parallel/test-cluster-dgram-ipv6only.js
Normal file
@ -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);
|
||||
});
|
||||
}));
|
||||
}));
|
||||
}
|
55
test/parallel/test-cluster-net-listen-ipv6only-false.js
Normal file
55
test/parallel/test-cluster-net-listen-ipv6only-false.js
Normal file
@ -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());
|
||||
}
|
58
test/parallel/test-cluster-net-listen-ipv6only-none.js
Normal file
58
test/parallel/test-cluster-net-listen-ipv6only-none.js
Normal file
@ -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());
|
||||
}
|
58
test/parallel/test-cluster-net-listen-ipv6only-rr.js
Normal file
58
test/parallel/test-cluster-net-listen-ipv6only-rr.js
Normal file
@ -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());
|
||||
}
|
33
test/parallel/test-dgram-ipv6only.js
Normal file
33
test/parallel/test-dgram-ipv6only.js
Normal file
@ -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());
|
||||
}));
|
30
test/parallel/test-net-listen-ipv6only.js
Normal file
30
test/parallel/test-net-listen-ipv6only.js
Normal file
@ -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();
|
||||
}));
|
||||
}));
|
Loading…
x
Reference in New Issue
Block a user