net: check EADDRINUSE after binding localPort
PR-URL: https://github.com/nodejs/node/pull/15097 Fixes: https://github.com/nodejs/node/issues/15084 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
parent
d7200d6823
commit
c419adff1d
37
lib/net.js
37
lib/net.js
@ -877,6 +877,27 @@ function afterWrite(status, handle, req, err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkBindError(err, port, handle) {
|
||||||
|
// EADDRINUSE may not be reported until we call listen() or connect().
|
||||||
|
// To complicate matters, a failed bind() followed by listen() or connect()
|
||||||
|
// will implicitly bind to a random port. Ergo, check that the socket is
|
||||||
|
// bound to the expected port before calling listen() or connect().
|
||||||
|
//
|
||||||
|
// FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a
|
||||||
|
// getsockname() method. Non-issue for now, the cluster module doesn't
|
||||||
|
// really support pipes anyway.
|
||||||
|
if (err === 0 && port > 0 && handle.getsockname) {
|
||||||
|
var out = {};
|
||||||
|
err = handle.getsockname(out);
|
||||||
|
if (err === 0 && port !== out.port) {
|
||||||
|
debug(`checkBindError, bound to ${out.port} instead of ${port}`);
|
||||||
|
err = UV_EADDRINUSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function internalConnect(
|
function internalConnect(
|
||||||
self, address, port, addressType, localAddress, localPort) {
|
self, address, port, addressType, localAddress, localPort) {
|
||||||
// TODO return promise from Socket.prototype.connect which
|
// TODO return promise from Socket.prototype.connect which
|
||||||
@ -900,6 +921,7 @@ function internalConnect(
|
|||||||
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
|
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
|
||||||
localAddress, localPort, addressType);
|
localAddress, localPort, addressType);
|
||||||
|
|
||||||
|
err = checkBindError(err, localPort, self._handle);
|
||||||
if (err) {
|
if (err) {
|
||||||
const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort);
|
const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort);
|
||||||
self.destroy(ex);
|
self.destroy(ex);
|
||||||
@ -1380,20 +1402,7 @@ function listenInCluster(server, address, port, addressType,
|
|||||||
cluster._getServer(server, serverQuery, listenOnMasterHandle);
|
cluster._getServer(server, serverQuery, listenOnMasterHandle);
|
||||||
|
|
||||||
function listenOnMasterHandle(err, handle) {
|
function listenOnMasterHandle(err, handle) {
|
||||||
// EADDRINUSE may not be reported until we call listen(). To complicate
|
err = checkBindError(err, port, handle);
|
||||||
// matters, a failed bind() followed by listen() will implicitly bind to
|
|
||||||
// a random port. Ergo, check that the socket is bound to the expected
|
|
||||||
// port before calling listen().
|
|
||||||
//
|
|
||||||
// FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a
|
|
||||||
// getsockname() method. Non-issue for now, the cluster module doesn't
|
|
||||||
// really support pipes anyway.
|
|
||||||
if (err === 0 && port > 0 && handle.getsockname) {
|
|
||||||
var out = {};
|
|
||||||
err = handle.getsockname(out);
|
|
||||||
if (err === 0 && port !== out.port)
|
|
||||||
err = UV_EADDRINUSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
var ex = exceptionWithHostPort(err, 'bind', address, port);
|
var ex = exceptionWithHostPort(err, 'bind', address, port);
|
||||||
|
26
test/parallel/test-net-client-bind-twice.js
Normal file
26
test/parallel/test-net-client-bind-twice.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This tests that net.connect() from a used local port throws EADDRINUSE.
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const net = require('net');
|
||||||
|
|
||||||
|
const server1 = net.createServer(common.mustNotCall());
|
||||||
|
server1.listen(0, common.localhostIPv4, common.mustCall(() => {
|
||||||
|
const server2 = net.createServer(common.mustNotCall());
|
||||||
|
server2.listen(0, common.localhostIPv4, common.mustCall(() => {
|
||||||
|
const client = net.connect({
|
||||||
|
host: common.localhostIPv4,
|
||||||
|
port: server1.address().port,
|
||||||
|
localAddress: common.localhostIPv4,
|
||||||
|
localPort: server2.address().port
|
||||||
|
}, common.mustNotCall());
|
||||||
|
|
||||||
|
client.on('error', common.mustCall((err) => {
|
||||||
|
assert.strictEqual(err.code, 'EADDRINUSE');
|
||||||
|
server1.close();
|
||||||
|
server2.close();
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}));
|
Loading…
x
Reference in New Issue
Block a user