Skip to content

Commit 24f06c5

Browse files
joyeecheungMylesBorins
authored andcommitted
net: check EADDRINUSE after binding localPort
PR-URL: #15097 Fixes: #15084 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 23df8fc commit 24f06c5

File tree

2 files changed

+49
-14
lines changed

2 files changed

+49
-14
lines changed

lib/net.js

+23-14
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,27 @@ function afterWrite(status, handle, req, err) {
871871
}
872872

873873

874+
function checkBindError(err, port, handle) {
875+
// EADDRINUSE may not be reported until we call listen() or connect().
876+
// To complicate matters, a failed bind() followed by listen() or connect()
877+
// will implicitly bind to a random port. Ergo, check that the socket is
878+
// bound to the expected port before calling listen() or connect().
879+
//
880+
// FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a
881+
// getsockname() method. Non-issue for now, the cluster module doesn't
882+
// really support pipes anyway.
883+
if (err === 0 && port > 0 && handle.getsockname) {
884+
var out = {};
885+
err = handle.getsockname(out);
886+
if (err === 0 && port !== out.port) {
887+
debug(`checkBindError, bound to ${out.port} instead of ${port}`);
888+
err = uv.UV_EADDRINUSE;
889+
}
890+
}
891+
return err;
892+
}
893+
894+
874895
function internalConnect(
875896
self, address, port, addressType, localAddress, localPort) {
876897
// TODO return promise from Socket.prototype.connect which
@@ -894,6 +915,7 @@ function internalConnect(
894915
debug('binding to localAddress: %s and localPort: %d (addressType: %d)',
895916
localAddress, localPort, addressType);
896917

918+
err = checkBindError(err, localPort, self._handle);
897919
if (err) {
898920
const ex = exceptionWithHostPort(err, 'bind', localAddress, localPort);
899921
self.destroy(ex);
@@ -1382,20 +1404,7 @@ function listenInCluster(server, address, port, addressType,
13821404
cluster._getServer(server, serverQuery, listenOnMasterHandle);
13831405

13841406
function listenOnMasterHandle(err, handle) {
1385-
// EADDRINUSE may not be reported until we call listen(). To complicate
1386-
// matters, a failed bind() followed by listen() will implicitly bind to
1387-
// a random port. Ergo, check that the socket is bound to the expected
1388-
// port before calling listen().
1389-
//
1390-
// FIXME(bnoordhuis) Doesn't work for pipe handles, they don't have a
1391-
// getsockname() method. Non-issue for now, the cluster module doesn't
1392-
// really support pipes anyway.
1393-
if (err === 0 && port > 0 && handle.getsockname) {
1394-
var out = {};
1395-
err = handle.getsockname(out);
1396-
if (err === 0 && port !== out.port)
1397-
err = uv.UV_EADDRINUSE;
1398-
}
1407+
err = checkBindError(err, port, handle);
13991408

14001409
if (err) {
14011410
var ex = exceptionWithHostPort(err, 'bind', address, port);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
// This tests that net.connect() from a used local port throws EADDRINUSE.
4+
5+
const common = require('../common');
6+
const assert = require('assert');
7+
const net = require('net');
8+
9+
const server1 = net.createServer(common.mustNotCall());
10+
server1.listen(0, common.localhostIPv4, common.mustCall(() => {
11+
const server2 = net.createServer(common.mustNotCall());
12+
server2.listen(0, common.localhostIPv4, common.mustCall(() => {
13+
const client = net.connect({
14+
host: common.localhostIPv4,
15+
port: server1.address().port,
16+
localAddress: common.localhostIPv4,
17+
localPort: server2.address().port
18+
}, common.mustNotCall());
19+
20+
client.on('error', common.mustCall((err) => {
21+
assert.strictEqual(err.code, 'EADDRINUSE');
22+
server1.close();
23+
server2.close();
24+
}));
25+
}));
26+
}));

0 commit comments

Comments
 (0)