From b156e01a65219af602b26d7589bd40ac6d234302 Mon Sep 17 00:00:00 2001 From: Jack Allnutt Date: Sat, 13 Jul 2013 11:49:38 +0100 Subject: [PATCH] Allow configuration of IP addresses to use for outgoing connections Fixes #285 --- config.example.js | 4 ++ package.json | 2 +- server/irc/connection.js | 129 +++++++++++++++++++++++++++------------ server/weblistener.js | 12 ++-- 4 files changed, 102 insertions(+), 45 deletions(-) diff --git a/config.example.js b/config.example.js index 2ca38ee..d082d38 100644 --- a/config.example.js +++ b/config.example.js @@ -33,6 +33,10 @@ conf.servers.push({ // ssl_cert: "cert.pem" //}); +conf.outgoing_addresses = { + IPv4: '0.0.0.0', + IPv6: '::' +}; // Do we want to enable the built in Identd server? diff --git a/package.json b/package.json index 5912528..05b9e6c 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "daemonize2": "0.4.0-rc.5", "eventemitter2": "0.4.11", "ipaddr.js": "0.1.1", - "socksjs": "0.3.3", + "socksjs": "0.4.1", "iconv-lite" : "0.2.10", "spdy": "1.9.1" } diff --git a/server/irc/connection.js b/server/irc/connection.js index df9b5b9..be22740 100644 --- a/server/irc/connection.js +++ b/server/irc/connection.js @@ -1,6 +1,7 @@ var net = require('net'), tls = require('tls'), util = require('util'), + dns = require('dns'), _ = require('lodash'), EventBinder = require('./eventbinder.js'), IrcServer = require('./server.js'), @@ -121,8 +122,9 @@ IrcConnection.prototype.applyIrcEvents = function () { * Start the connection to the IRCd */ IrcConnection.prototype.connect = function () { - var that = this; - var socks; + var that = this, + socks, + addSocketHandlers; // The socket connect event to listener for var socket_connect_event_name = 'connect'; @@ -133,53 +135,80 @@ IrcConnection.prototype.connect = function () { // Are we connecting through a SOCKS proxy? if (this.socks) { - this.socket = Socks.connect({ - host: this.irc_host.hostname, - port: this.irc_host.port, - ssl: this.ssl, - rejectUnauthorized: global.config.reject_unauthorised_certificates - }, {host: this.socks.host, - port: this.socks.port, - user: this.socks.user, - pass: this.socks.pass - }); + getConnectionFamily(this.socks.host, function (err, family, host) { + var outgoing; + if ((family === 'IPv6') && (global.config.outgoing_address.IPv6) && (global.config.outgoing_address.IPv6 !== '')) { + outgoing = global.config.outgoing_address.IPv6; + } else { + outgoing = global.config.outgoing_address.IPv4; + } + that.socket = Socks.connect({ + host: that.irc_host.hostname, + port: that.irc_host.port, + ssl: that.ssl, + rejectUnauthorized: global.config.reject_unauthorised_certificates + }, {host: host, + port: that.socks.port, + user: that.socks.user, + pass: that.socks.pass, + localAddress: outgoing + }); - } else if (this.ssl) { - this.socket = tls.connect({ - host: this.irc_host.hostname, - port: this.irc_host.port, - rejectUnauthorized: global.config.reject_unauthorised_certificates + addSocketHandlers.call(that); }); + } else { + getConnectionFamily(this.irc_host.hostname, function (err, family, host) { + var outgoing; + if ((family === 'IPv6') && (global.config.outgoing_addresses.IPv6) && (global.config.outgoing_addresses.IPv6 !== '')) { + outgoing = global.config.outgoing_addresses.IPv6; + } else { + outgoing = global.config.outgoing_addresses.IPv4; + } - socket_connect_event_name = 'secureConnect'; + if (that.ssl) { + that.socket = tls.connect({ + host: host, + port: that.irc_host.port, + rejectUnauthorized: global.config.reject_unauthorised_certificates, + localAddress: outgoing + }); + + socket_connect_event_name = 'secureConnect'; + + } else { + that.socket = net.connect({ + host: host, + port: that.irc_host.port, + localAddress: outgoing + }); + } - } else { - this.socket = net.connect({ - host: this.irc_host.hostname, - port: this.irc_host.port + addSocketHandlers.call(that); }); } - this.socket.on(socket_connect_event_name, function () { - that.connected = true; - socketConnectHandler.call(that); - }); + addSocketHandlers = function () { + this.socket.on(socket_connect_event_name, function () { + that.connected = true; + socketConnectHandler.call(that); + }); - this.socket.on('error', function (event) { - that.emit('error', event); - }); + this.socket.on('error', function (event) { + that.emit('error', event); + }); - this.socket.on('data', function () { - parse.apply(that, arguments); - }); + this.socket.on('data', function () { + parse.apply(that, arguments); + }); - this.socket.on('close', function (had_error) { - that.connected = false; - that.emit('close'); + this.socket.on('close', function (had_error) { + that.connected = false; + that.emit('close'); - // Close the whole socket down - that.disposeSocket(); - }); + // Close the whole socket down + that.disposeSocket(); + }); + }; }; /** @@ -285,6 +314,30 @@ IrcConnection.prototype.setEncoding = function (encoding) { } }; +function getConnectionFamily(host, callback) { + if (net.isIP(host)) { + if (net.isIPv4(host)) { + setImmediate(callback, null, 'IPv4', host); + } else { + setImmediate(callback, null, 'IPv6', host); + } + } else { + dns.resolve6(host, function (err, addresses) { + if (!err) { + callback(null, 'IPv6', addresses[0]); + } else { + dns.resolve4(host, function (err, addresses) { + if (!err) { + callback(null, 'IPv4',addresses[0]); + } else { + callback(err); + } + }); + } + }); + } +} + function onChannelJoin(event) { var chan; diff --git a/server/weblistener.js b/server/weblistener.js index a8b7850..ce75b3e 100644 --- a/server/weblistener.js +++ b/server/weblistener.js @@ -31,9 +31,9 @@ var WebListener = function (web_config, transports) { events.EventEmitter.call(this); - + http_handler = new HttpHandler(web_config); - + // Standard options for the socket.io connections ws_opts = { 'log level': 0, @@ -79,8 +79,8 @@ var WebListener = function (web_config, transports) { hs.on('error', function (err) { that.emit('error', err); - }) - + }); + this.ws.enable('browser client minification'); this.ws.enable('browser client etag'); this.ws.set('transports', transports); @@ -128,7 +128,7 @@ function authoriseConnection(handshakeData, callback) { } handshakeData.real_address = address; - + // If enabled, don't go over the connection limit if (global.config.max_client_conns && global.config.max_client_conns > 0) { if (global.clients.numOnAddress(address) + 1 > global.config.max_client_conns) { @@ -144,7 +144,7 @@ function authoriseConnection(handshakeData, callback) { } else { handshakeData.revdns = _.first(domains) || address; } - + // All is well, authorise the connection callback(null, true); }); -- 2.25.1