Skip to content

Commit

Permalink
net: add localPort to connect options
Browse files Browse the repository at this point in the history
Expose localPort for binding to a specific port for outbound
connections.

If localAddress is not specified '0.0.0.0' is used for ip4 and '::'
for ip6 connections.

Fixes #7092
  • Loading branch information
tjfontaine authored and indutny committed Feb 18, 2014
1 parent a226be4 commit 59baab2
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 7 deletions.
2 changes: 2 additions & 0 deletions doc/api/net.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ For TCP sockets, `options` argument should be an object which specifies:

- `localAddress`: Local interface to bind to for network connections.

- `localPort`: Local port to bind to for network connections.

- `family` : Version of IP stack. Defaults to `4`.

For local domain sockets, `options` argument should be an object which
Expand Down
50 changes: 43 additions & 7 deletions lib/net.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,20 +776,51 @@ function afterWrite(status, handle, req, err) {
}


function connect(self, address, port, addressType, localAddress) {
function connect(self, address, port, addressType, localAddress, localPort) {
// TODO return promise from Socket.prototype.connect which
// wraps _connectReq.

assert.ok(self._connecting);

var err;
if (localAddress) {
if (addressType === 6) {
err = self._handle.bind6(localAddress);
} else {
err = self._handle.bind(localAddress);
if (localAddress || localPort) {
if (localAddress && !exports.isIP(localAddress))
err = new TypeError(
'localAddress should be a valid IP: ' + localAddress);

if (localPort && !util.isNumber(localPort))
err = new TypeError('localPort should be a number: ' + localPort);

var bind;

switch (addressType) {
case 4:
if (!localAddress)
localAddress = '0.0.0.0';
bind = self._handle.bind;
break;
case 6:
if (!localAddress)
localAddress = '::';
bind = self._handle.bind6;
break;
default:
err = new TypeError('Invalid addressType: ' + addressType);
break;
}

if (err) {
self._destroy(err);
return;
}

debug('binding to localAddress: %s and localPort: %d',
localAddress,
localPort);

bind = bind.bind(self._handle);
err = bind(localAddress, localPort);

if (err) {
self._destroy(errnoException(err, 'bind'));
return;
Expand Down Expand Up @@ -897,7 +928,12 @@ Socket.prototype.connect = function(options, cb) {
// expects remoteAddress to have a meaningful value
ip = ip || (addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1');

connect(self, ip, options.port, addressType, options.localAddress);
connect(self,
ip,
options.port,
addressType,
options.localAddress,
options.localPort);
}
});
}
Expand Down
61 changes: 61 additions & 0 deletions test/simple/test-net-localerror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var common = require('../common');
var assert = require('assert');
var net = require('net');

var server = net.createServer(function(socket) {
assert.ok(false, 'no clients should connect');
}).listen(common.PORT).on('listening', function() {
server.unref();

function test1(next) {
connect({
host: '127.0.0.1',
port: common.PORT,
localPort: 'foobar',
},
'localPort should be a number: foobar',
next);
}

function test2(next) {
connect({
host: '127.0.0.1',
port: common.PORT,
localAddress: 'foobar',
},
'localAddress should be a valid IP: foobar',
next)
}

test1(test2);
})

function connect(opts, msg, cb) {
var client = net.connect(opts).on('connect', function() {
assert.ok(false, 'we should never connect');
}).on('error', function(err) {
assert.strictEqual(err.message, msg);
if (cb) cb();
});
}
41 changes: 41 additions & 0 deletions test/simple/test-net-localport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

var common = require('../common');
var assert = require('assert');
var net = require('net');

var server = net.createServer(function(socket) {
console.log(socket.remotePort);
assert.strictEqual(socket.remotePort, common.PORT + 1);
socket.end();
socket.on('close', function() {
server.close();
});
}).listen(common.PORT).on('listening', function() {
var client = net.connect({
host: '127.0.0.1',
port: common.PORT,
localPort: common.PORT + 1,
}).on('connect', function() {
assert.strictEqual(client.localPort, common.PORT + 1);
});
})

0 comments on commit 59baab2

Please sign in to comment.