X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=server%2Fproxy.js;h=0782fdd97d974a1b6f6f55c5c76cb2575f6a5887;hb=a885d1c9104a578a6667b230841e3466abd53fdb;hp=5950dcf6a16921d6334141475b4366f25c0c9550;hpb=29bc206650b2226f678dddf2bb90b89207649350;p=KiwiIRC.git diff --git a/server/proxy.js b/server/proxy.js index 5950dcf..0782fdd 100644 --- a/server/proxy.js +++ b/server/proxy.js @@ -38,7 +38,8 @@ util.inherits(ProxyServer, events.EventEmitter); ProxyServer.prototype.listen = function(listen_port, listen_addr, opts) { var that = this, - serv_opts = {}; + serv_opts = {}, + connection_event = 'connection'; opts = opts || {}; @@ -62,6 +63,8 @@ ProxyServer.prototype.listen = function(listen_port, listen_addr, opts) { this.server = tls.createServer(serv_opts); + connection_event = 'secureConnection'; + } // No SSL, start a simple clear text server @@ -73,7 +76,7 @@ ProxyServer.prototype.listen = function(listen_port, listen_addr, opts) { that.emit('listening'); }); - this.server.on('connection', function(socket) { + this.server.on(connection_event, function(socket) { new ProxyPipe(socket, that); }); }; @@ -100,19 +103,20 @@ ProxyServer.prototype.close = function(callback) { * 4. If all ok, pipe data between the 2 sockets as a proxy */ function ProxyPipe(kiwi_socket, proxy_server) { + debug('[KiwiProxy] New Kiwi connection'); + this.kiwi_socket = kiwi_socket; this.proxy_server = proxy_server; this.irc_socket = null; - this.buffer = ''; + this.buffers = []; this.meta = null; - kiwi_socket.setEncoding('utf8'); kiwi_socket.on('readable', this.kiwiSocketOnReadable.bind(this)); } ProxyPipe.prototype.destroy = function() { - this.buffer = null; + this.buffers = null; this.meta = null; if (this.irc_socket) { @@ -130,29 +134,35 @@ ProxyPipe.prototype.destroy = function() { ProxyPipe.prototype.kiwiSocketOnReadable = function() { - var chunk, meta; + var chunk, buffer, meta; while ((chunk = this.kiwi_socket.read()) !== null) { - this.buffer += chunk; + this.buffers.push(chunk); } // Not got a complete line yet? Wait some more - if (this.buffer.indexOf('\n') === -1) + chunk = this.buffers[this.buffers.length-1]; + if (!chunk || chunk[chunk.length-1] !== 0x0A) return; + buffer = new Buffer.concat(this.buffers); + this.buffers = null; + try { - meta = JSON.parse(this.buffer.substr(0, this.buffer.indexOf('\n'))); + debug('[KiwiProxy] Found a complete line in the buffer'); + meta = JSON.parse(buffer.toString('utf8')); } catch (err) { + debug('[KiwiProxy] Error parsing meta'); this.destroy(); return; } if (!meta.username) { + debug('[KiwiProxy] Meta does not contain a username'); this.destroy(); return; } - this.buffer = ''; this.meta = meta; this.kiwi_socket.removeAllListeners('readable'); @@ -161,14 +171,43 @@ ProxyPipe.prototype.kiwiSocketOnReadable = function() { ProxyPipe.prototype.makeIrcConnection = function() { - debug('[KiwiProxy] Proxied connection to: ' + this.meta.host + ':' + this.meta.port.toString()); - this.irc_socket = this.meta.ssl ? - tls.connect(parseInt(this.meta.port, 10), this.meta.host, this._onSocketConnect.bind(this)) : - net.connect(parseInt(this.meta.port, 10), this.meta.host, this._onSocketConnect.bind(this)); + debug('[KiwiProxy] Opening proxied connection to: ' + this.meta.host + ':' + this.meta.port.toString()); + + var local_address = this.meta.interface ? + this.meta.interface : + '0.0.0.0'; + + if (this.meta.ssl) { + this.irc_socket = tls.connect({ + port: parseInt(this.meta.port, 10), + host: this.meta.host, + rejectUnauthorized: global.config.reject_unauthorised_certificates, + localAddress: local_address + }, this._onSocketConnect.bind(this)); + + } else { + this.irc_socket = net.connect({ + port: parseInt(this.meta.port, 10), + host: this.meta.host, + localAddress: local_address + }, this._onSocketConnect.bind(this)); + } this.irc_socket.setTimeout(10000); this.irc_socket.on('error', this._onSocketError.bind(this)); this.irc_socket.on('timeout', this._onSocketTimeout.bind(this)); + + // We need the raw socket connect event, not after any SSL handshakes or anything + if (this.irc_socket.socket) { + this.irc_socket.socket.on('connect', this._onRawSocketConnect.bind(this)); + } else { + this.irc_socket.on('connect', this._onRawSocketConnect.bind(this)); + } +}; + + +ProxyPipe.prototype._onRawSocketConnect = function() { + this.proxy_server.emit('socket_connected', this); }; @@ -219,8 +258,6 @@ ProxyPipe.prototype.startPiping = function() { this.irc_socket.on('close', this._onSocketClose.bind(this)); - this.kiwi_socket.setEncoding('binary'); - this.kiwi_socket.pipe(this.irc_socket); this.irc_socket.pipe(this.kiwi_socket); }; @@ -240,7 +277,8 @@ function ProxySocket(proxy_port, proxy_addr, meta, proxy_opts) { this.proxy_addr = proxy_addr; this.proxy_port = proxy_port; this.proxy_opts = proxy_opts || {}; - this.meta = meta || {}; + + this.setMeta(meta || {}); this.state = 'disconnected'; } @@ -253,6 +291,12 @@ ProxySocket.prototype.setMeta = function(meta) { }; +ProxySocket.prototype.connectTls = function() { + this.meta.ssl = true; + return this.connect.apply(this, arguments); +}; + + ProxySocket.prototype.connect = function(dest_port, dest_addr, connected_fn) { this.meta.host = dest_addr; this.meta.port = dest_port; @@ -263,12 +307,19 @@ ProxySocket.prototype.connect = function(dest_port, dest_addr, connected_fn) { return false; } - debug('[KiwiProxy] Connecting to proxy ' + this.proxy_addr + ':' + this.proxy_port.toString()); - this.socket = this.proxy_opts.ssl ? - tls.connect(this.proxy_port, this.proxy_addr, this._onSocketConnect.bind(this)) : - net.connect(this.proxy_port, this.proxy_addr, this._onSocketConnect.bind(this)); - this.socket.setTimeout(10000); + debug('[KiwiProxy] Connecting to proxy ' + this.proxy_addr + ':' + this.proxy_port.toString() + ' SSL: ' + (!!this.proxy_opts.ssl).toString()); + if (this.proxy_opts.ssl) { + this.socket = tls.connect({ + port: this.proxy_port, + host: this.proxy_addr, + rejectUnauthorized: !!global.config.reject_unauthorised_certificates, + }, this._onSocketConnect.bind(this)); + + } else { + this.socket = net.connect(this.proxy_port, this.proxy_addr, this._onSocketConnect.bind(this)); + } + this.socket.setTimeout(10000); this.socket.on('data', this._onSocketData.bind(this)); this.socket.on('close', this._onSocketClose.bind(this)); this.socket.on('error', this._onSocketError.bind(this)); @@ -308,6 +359,7 @@ ProxySocket.prototype._write = function(chunk, encoding, callback) { if (this.state === 'connected' && this.socket) { return this.socket.write(chunk, encoding, callback); } else { + debug('[KiwiProxy] Trying to write to an unfinished socket. State=' + this.state); callback('Not connected'); } }; @@ -361,6 +413,7 @@ ProxySocket.prototype._onSocketData = function(data) { ProxySocket.prototype._onSocketClose = function(had_error) { + debug('[KiwiProxy] _onSocketClose() had_error=' + had_error.toString()); if (this.state === 'connected') { this.emit('close', had_error); return; @@ -372,6 +425,7 @@ ProxySocket.prototype._onSocketClose = function(had_error) { ProxySocket.prototype._onSocketError = function(err) { + debug('[KiwiProxy] _onSocketError() err=' + err.toString()); this.ignore_close = true; this.emit('error', err); }; \ No newline at end of file