Local WebRTC signaling server - node.js

I am using firebase signaling server for my webRTC website.
I am informed I can use websocket for implementation of my own local signaling server.
I tried the example on Link and it works. When I replace my firebase code it doesn't work.
Firebase code
var config = {
openSocket: function(config) {
var channel = config.channel || location.href.replace(/\/|:|#|%|\.|\[|\]/g, '');
var socket = new Firebase('https://webrtc.firebaseIO.com/' + channel); //EXTERNAL SERVER need change
socket.channel = channel;
socket.on("child_added", function(data) {
config.onmessage && config.onmessage(data.val());
});
socket.send = function(data) {
this.push(data);
};
config.onopen && setTimeout(config.onopen, 1);
socket.onDisconnect().remove();
return socket;
},
Websocket over nodejs code
var channelName = location.href.replace(/\/|:|#|%|\.|\[|\]/g, ''); // using URL as room-name
var SIGNALING_SERVER = 'ws://172.20.34.132:12034';
connection.openSignalingChannel = function(config) {
config.channel = config.channel || this.channel;
var websocket = new WebSocket(SIGNALING_SERVER);
websocket.channel = config.channel;
websocket.onopen = function() {
websocket.push(JSON.stringify({
open: true,
channel: config.channel
}));
if (config.callback)
config.callback(websocket);
};
websocket.onmessage = function(event) {
config.onmessage(JSON.parse(event.data));
};
websocket.push = websocket.send;
websocket.send = function(data) {
websocket.push(JSON.stringify({
data: data,
channel: config.channel
}));
};
}

Related

NodeJS Parse raw minecraft packet

I'm trying to create a minecraft proxy server to intercept and read packets between client and server.
I've already read the documentation about minecraft protocol here: https://wiki.vg , but my raw packets and the documentation didn't match...
Can someone explain me how i have to parse my packets to get length, id and data? Thanks ^^
packets examples:
1000d402093132372e302e302e3163dd0210000e4275726e47656d696f7333363433
bb01011035353730626633306336626363343266a20130819f300d06092a864886f70d010101050003818d0030818902818100a6f8df08dce2072565b5beef1bb27ed5c7cd02dd02e165019b9e4c4ae25a51a288c9474c643812036d07dc6715b0fc407824b6af1cb5b3efc1cf8739cb0a1f8e128b432f156ebe443bb69dd85c112d10426ab78ec99b0de8ab85b51140d0f5fc7b2d6b9c466b1272be09bc9551f1a0ab2121a0f35fd6bfc9d62b24cef2bb0e53020301000104051a9b77
8502018001584fe6b9c8388760fc20f4bab8bc3bb2c3026613eaff02d3cda41a541456d945b0e6dbdc62d5396b806e3ba5cda446ac6af681db1c584300fe7317d73a4310aea4f98dbecfcc71a1a0a6880ed0d7c6f7fa1b99c6b003226f38703c07871904574f855474cd41975e0ceccbd99b35a7da26121938af63484a26a8afb3772f947e800187a53861917ac19e8ffa04bb88e69a23a594ac22d268b25782e8de1b8f9dd3d8aff0c5222a286945a0de25c2ec75ee21c7bd49980910f9270d67f4290ae47b0aedc75f4e60406e406827bea309696fd9628aaf33270d7119292c10bcf0bbd955c54d97e57c7514e0080c373f8cad1fd3bbca6c90c6131c1592834446f28a1807
Edit:
my actual code:
const mc = require("minecraft-protocol");
const states = mc.states
const mcData = require('minecraft-data')('1.12.2');
const version = mcData.version
const Cserializer = mc.createSerializer({ state: states.PLAY, isServer: false, version: '1.12.2' })
const Sserializer = mc.createSerializer({ state: states.PLAY, isServer: true, version: '1.12.2' })
//var serialized = Cserializer.createPacketBuffer({ name: 'chat', params: { message: '!p' } })
//console.log("serialisé", serialized);
const Cdeserializer = mc.createDeserializer({ state: states.PLAY, isServer: false, version: '1.12.2' })
const Sdeserializer = mc.createDeserializer({ state: states.PLAY, isServer: true, version: '1.12.2' })
//console.log("deserialisé", Sdeserializer.parsePacketBuffer(Buffer.from("0c010d50455449542046445021 ", "hex")));
var net = require("net");
var fs = require("fs");
if(fs.existsSync("logs.txt"))fs.unlinkSync("logs.txt");
//packet format: [length][id][data...............]
function parsePacket(buffer, server){
let packet;
try {
if(server){
packet = Sdeserializer.parsePacketBuffer(buffer);
}else{
packet = Cdeserializer.parsePacketBuffer(buffer);
}
} catch (error) {
console.log("!");
}
return packet;
}
process.on("uncaughtException", function(error) {
console.error(error);
});
if (process.argv.length != 5) {
console.log("usage: %s <localport> <remotehost> <remoteport>", process.argv[1]);
process.exit();
}
var localport = process.argv[2];
var remotehost = process.argv[3];
var remoteport = process.argv[4];
var remotesocket = null;
var Clocalsocket = null;
var server = net.createServer(function (localsocket) {
Clocalsocket = localsocket;
remotesocket = new net.Socket();
remotesocket.connect(remoteport, remotehost);
localsocket.on('connect', function (data) {
console.log(">>> connection #%d from %s:%d",
server.connections,
localsocket.remoteAddress,
localsocket.remotePort
);
});
localsocket.on('data', function (data) {
let custom = doPacket(data, true);
if(!custom) custom = data;
fs.appendFileSync('logs.txt', '\n>>> remote >>> '+toHexString(data));
let p = parsePacket(data, true);
console.log(p);
if(custom != "cancel"){
var flushed = remotesocket.write(custom);
if (!flushed) {
console.log("remote not flushed; pausing local");
}
}
});
remotesocket.on('data', function(data) {
let custom = doPacket(data, false);
if(!custom) custom = data;
fs.appendFileSync('logs.txt', '\n>>> local >>> '+toHexString(data));
let p = parsePacket(data, false);
console.log(p);
if(custom != "cancel"){
var flushed = localsocket.write(custom);
if (!flushed) {
console.log("local not flushed; pausing remote");
}
}
});
localsocket.on('drain', function() {
console.log("%s:%d - resuming remote",
localsocket.remoteAddress,
localsocket.remotePort
);
remotesocket.resume();
});
remotesocket.on('drain', function() {
console.log("%s:%d - resuming local",
localsocket.remoteAddress,
localsocket.remotePort
);
localsocket.resume();
});
localsocket.on('close', function(had_error) {
console.log("%s:%d - closing remote",
localsocket.remoteAddress,
localsocket.remotePort
);
remotesocket.end();
});
remotesocket.on('close', function(had_error) {
console.log("%s:%d - closing local",
localsocket.remoteAddress,
localsocket.remotePort
);
localsocket.end();
});
});
server.listen(localport);
console.log("redirecting connections from 127.0.0.1:%d to %s:%d", localport, remotehost, remoteport);
function toHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('')
}

Nodejs Websocket connection in an ES6 class

I am not sure if this is the correct way to implement a websocket connection in NodeJS, but the problem I am having is not with WebSockets but with class variables.
This is my WebSocketClass:
class WebSocketCalss {
constructor ( httpserver )
{
console.log("Initializing WebSocketCalss");
this.httpServer = httpserver;
this.connection = null;
this.client = null;
this.initializeWebSocket();
}
initializeWebSocket()
{
var WebSocketServer = require('websocket').server;
var wsServer = new WebSocketServer({
httpServer: this.httpServer
});
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
this.connection = request.accept(null, request.origin);
console.log((new Date()) + ' Connection accepted.');
this.connection.sendUTF(JSON.stringify({ type: "history", data: "data"} ));
var t = 0;
/* ---- Client ---- */
var W3CWebSocket = require('websocket').w3cwebsocket;
this.client = new W3CWebSocket('wss://ws.bitstamp.net');
this.client.onerror = function() {
console.log('Connection Error');
};
this.client.onopen = function() {
console.log('WebSocket Client Connected');
var subscribeMsg = {
"event": "bts:subscribe",
"data": {
"channel": "live_trades_btcusd"
}
};
this.client.send(JSON.stringify(subscribeMsg));
};
this.client.onclose = function() {
console.log('echo-protocol Client Closed');
};
this.client.onmessage = function(e) {
if (typeof e.data === 'string') {
var bitstampPrice = JSON.parse(e.data).data.price;
console.log(bitstampPrice);
this.connection.sendUTF(bitstampPrice);
}
};
});
//this.connection.sendUTF(JSON.stringify({ type: "history", data: "data"} ));
}
}
module.exports = (httpserver) => { return new WebSocketCalss(httpserver) }
It maybe hairy, so this is what I am trying to do:
My NodeJS server will open a WebSocket connection to my client (browser)
In this WebSocket, I want to send a value that is received from another WebSocket (that is, my NodeJS will connect as a client)
Things seem to work fine individually, however, when I try to send the value (that I received as a client), to my own client (as I am the server), I get
Cannot read property 'send' of undefined
Basically, inside the callback, the this variable are not defined. It is as if this is a new object.
I am not familiar with ES6 so I believe I am doing something fundamentally wrong.
If anyone could shed some light in to this that'd be very much appreciated.
When you use this inside a function(), the context of the this is bound to the function and not the outside class.
this.client.onopen = () => {
console.log('WebSocket Client Connected');
var subscribeMsg = {
"event": "bts:subscribe",
"data": {
"channel": "live_trades_btcusd"
}
};
this.client.send(JSON.stringify(subscribeMsg));
};

TTS with a websocket:Creating a WAV from binary data fails

I try to create a WAV from binary data using the websocket connection on a node.js server. I use the BluemixTTS to create the speech. Here is my current code:
'use strict';
const WebSocket = require('ws');
var express = require('express');
var watson = require('watson-developer-cloud');
var vcapServices = require('vcap_services');
var extend = (extend = require('util')._extend);
var fs = require('fs');
var ttsConfig = extend(
{
version: 'v1',
url: 'https://stream.watsonplatform.net/text-to-speech/api',
username: 'myusernamehere',
password: "mypasswordhere"
},
vcapServices.getCredentials('text_to_speech')
);
var ttsAuthService = watson.authorization(ttsConfig);
var websocket;
ttsAuthService.getToken({ url: ttsConfig.url }, function(err, token) {
if (err) {
console.log('Error retrieving token: ', err);
return;
}
var voice = 'en-US_AllisonVoice';
var wsURI = 'wss://stream.watsonplatform.net/text-to-speech/api/v1/synthesize?voice=' +
voice + '&watson-token=' + token;
websocket = new WebSocket(wsURI);
websocket.onopen = function(evt) { onOpen(evt) };
websocket.onclose = function(evt) { onClose(evt) };
websocket.onmessage = function(evt) { onMessage(evt) };
websocket.onerror = function(evt) { onError(evt) };
});
function onOpen(evt) {
var message = {
text: 'Hello world',
accept: 'audio/wav',
timings: ['words']
};
websocket.send(JSON.stringify(message));
}
var messages;
var audioStream = null;
function onMessage(evt) {
if (typeof evt.data === 'string') {
messages += evt.data;
} else {
if(audioStream == null){
audioStream = evt.data;
}else{
audioStream += evt.data;
}
}
}
function onClose(evt) {
console.log(messages);
var wstream = fs.createWriteStream('test.wav');
wstream.write((audioStream));
wstream.end();
}
function onError(evt) {
}
I get the token, trigger the TTS specifying audio/wav, build my buffer in the onMessage method and then write it into a .wav file. Everything seems fine.
However the file is somehow broken, and cannot be opened with any music-player. Do I miss some special encoding ?
Any help is appreciated
Regards,
Rambazamba
As the data contains a buffer one has to write the buffer in the file directly each time you get a message and then close the file stream in the onClose event. Like this:
var messages;
var wstream = fs.createWriteStream('test.wav');
function onMessage(evt) {
if (typeof evt.data === 'string') {
messages += evt.data;
} else {
wstream.write(evt.data)
}
}
function onClose(evt) {
console.log(messages);
wstream.end();
}
function onError(evt) {
}

Why i can't use Primus.connect()?

I download a example at https://github.com/strongloop/strong-pubsub-example
When I use Primus.connect(url). It's occurs error that: "Primus.connect is not a function".
This is my pub.js
var PORT = process.env.PORT;
var TOPIC = process.env.TOPIC;
var Client = require('strong-pubsub');
var Adapter = require('strong-pubsub-mqtt');
var readline = require('readline');
var client = new Client({port: PORT}, Adapter);
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
(function prompt() {
rl.question('to "' + TOPIC + '": ', function(answer) {
client.publish(TOPIC, answer);
prompt();
});
})();
And this is my sub.js
var PORT = process.env.PORT;
var TOPIC = process.env.TOPIC;
var Client = require('strong-pubsub');
var Adapter = require('strong-pubsub-mqtt');
var client = new Client({port: PORT}, Adapter);
client.subscribe(TOPIC);
var duplex = require('duplex');
var Primus = require('primus');
Primus.Stream = require('stream');
var server = new Client({port: PORT, host: 'localhost'}, Adapter, {
createConnection: function(port, host) {
var connection = duplex();
var primus = Primus.connect('http://' + host + ':' + port, {
transformer: 'engine.io',
parser: 'binary'
});
connection.on('_data', function(chunk) {
// someone called `connection.write(buf)`
primus.write(chunk);
});
primus.on('data', function(chunk) {
// chunk is an arrayBuffer
connection._data(toBuffer(chunk));
});
primus.on('open', function() {
connection.emit('connect');
});
connection.on('_end', function() {
primus.end();
this._end();
});
return connection;
}
});
client.on('message', function(topic, msg) {
console.log(msg.toString());
server.publish(topic,msg);
});
And errors

Implementing STARTTLS in a protocol in NodeJS

I'm trying to add a STARTTLS upgrade to an existing protocol (which currently works in plaintext).
As a start, I'm using a simple line-based echoing server (it's a horrible kludge with no error handling or processing of packets into lines - but it usually just works as the console sends a line-at-a-time to stdin).
I think my server is right, but both ends exit with identical errors when I type starttls:
events.js:72
throw er; // Unhandled 'error' event
^
Error: 139652888721216:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:766:
at SlabBuffer.use (tls.js:232:18)
at CleartextStream.read [as _read] (tls.js:450:29)
at CleartextStream.Readable.read (_stream_readable.js:320:10)
at EncryptedStream.write [as _write] (tls.js:366:25)
at doWrite (_stream_writable.js:221:10)
at writeOrBuffer (_stream_writable.js:211:5)
at EncryptedStream.Writable.write (_stream_writable.js:180:11)
at Socket.ondata (stream.js:51:26)
at Socket.EventEmitter.emit (events.js:95:17)
at Socket.<anonymous> (_stream_readable.js:746:14)
Have I completely misunderstood how to do an upgrade on the client side?
Currently, I'm using the same method to add TLS-ness to plain streams at each end. This feels wrong, as both client and server will be trying to play the same role in the negotiation.
tlsserver.js:
r tls = require('tls');
var net = require('net');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
// This is necessary only if using the client certificate authentication.
requestCert: true,
// This is necessary only if the client uses the self-signed certificate.
ca: [ fs.readFileSync('client-cert.pem') ],
rejectUnauthorized: false
};
var server = net.createServer(function(socket) {
socket.setEncoding('utf8');
socket.on('data', function(data) {
console.log('plain data: ', data);
// FIXME: this is not robust, it should be processing the stream into lines
if (data.substr(0, 8) === 'starttls') {
console.log('server starting TLS');
//socket.write('server starting TLS');
socket.removeAllListeners('data');
options.socket = socket;
sec_socket = tls.connect(options, (function() {
sec_socket.on('data', function() {
console.log('secure data: ', data);
});
return callback(null, true);
}).bind(this));
} else {
console.log('plain data', data);
}
});
});
server.listen(9999, function() {
console.log('server bound');
});
client.js:
var tls = require('tls');
var fs = require('fs');
var net = require('net');
var options = {
// These are necessary only if using the client certificate authentication
key: fs.readFileSync('client-key.pem'),
cert: fs.readFileSync('client-cert.pem'),
// This is necessary only if the server uses the self-signed certificate
ca: [ fs.readFileSync('server-cert.pem') ],
rejectUnauthorized: false
};
var socket = new net.Socket();
var sec_socket = undefined;
socket.setEncoding('utf8');
socket.on('data', function(data) {
console.log('plain data:', data);
});
socket.connect(9999, function() {
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(data) {
if (!sec_socket) {
console.log('sending plain:', data);
socket.write(data);
} else {
console.log('sending secure:', data);
sec_socket.write(data);
}
if (data.substr(0, 8) === 'starttls') {
console.log('client starting tls');
socket.removeAllListeners('data');
options.socket = socket;
sec_socket = tls.connect(options, (function() {
sec_socket.on('data', function() {
console.log('secure data: ', data);
});
return callback(null, true);
}).bind(this));
}
});
});
Got it working, thanks to Matt Seargeant's answer. My code now looks like:
server.js:
var ts = require('./tls_socket');
var fs = require('fs');
var options = {
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
// This is necessary only if using the client certificate authentication.
requestCert: false,
// This is necessary only if the client uses the self-signed certificate.
ca: [ fs.readFileSync('client-cert.pem') ],
rejectUnauthorized: false
};
var server = ts.createServer(function(socket) {
console.log('connected');
socket.on('data', function(data) {
console.log('data', data);
if (data.length === 9) {
console.log('upgrading to TLS');
socket.upgrade(options, function() {
console.log('upgraded to TLS');
});
}
});
});
server.listen(9999);
client.js:
var ts = require('./tls_socket');
var fs = require('fs');
var crypto = require('crypto');
var options = {
// These are necessary only if using the client certificate authentication
key: fs.readFileSync('client-key.pem'),
cert: fs.readFileSync('client-cert.pem'),
// This is necessary only if the server uses the self-signed certificate
ca: [ fs.readFileSync('server-cert.pem') ],
rejectUnauthorized: false
};
var socket = ts.connect(9999, 'localhost', function() {
console.log('secured');
});
process.stdin.on('data', function(data) {
console.log('sending:', data);
socket.write(data);
if (data.length === 9) {
socket.upgrade(options);
}
});
tls_socket.js:
"use strict";
/*----------------------------------------------------------------------------------------------*/
/* Obtained and modified from http://js.5sh.net/starttls.js on 8/18/2011. */
/*----------------------------------------------------------------------------------------------*/
var tls = require('tls');
var crypto = require('crypto');
var util = require('util');
var net = require('net');
var stream = require('stream');
var SSL_OP_ALL = require('constants').SSL_OP_ALL;
// provides a common socket for attaching
// and detaching from either main socket, or crypto socket
function pluggableStream(socket) {
stream.Stream.call(this);
this.readable = this.writable = true;
this._timeout = 0;
this._keepalive = false;
this._writeState = true;
this._pending = [];
this._pendingCallbacks = [];
if (socket)
this.attach(socket);
}
util.inherits(pluggableStream, stream.Stream);
pluggableStream.prototype.pause = function () {
if (this.targetsocket.pause) {
this.targetsocket.pause();
this.readable = false;
}
}
pluggableStream.prototype.resume = function () {
if (this.targetsocket.resume) {
this.readable = true;
this.targetsocket.resume();
}
}
pluggableStream.prototype.attach = function (socket) {
var self = this;
self.targetsocket = socket;
self.targetsocket.on('data', function (data) {
self.emit('data', data);
});
self.targetsocket.on('connect', function (a, b) {
self.emit('connect', a, b);
});
self.targetsocket.on('secureConnection', function (a, b) {
self.emit('secureConnection', a, b);
self.emit('secure', a, b);
});
self.targetsocket.on('secure', function (a, b) {
self.emit('secureConnection', a, b);
self.emit('secure', a, b);
});
self.targetsocket.on('end', function () {
self.writable = self.targetsocket.writable;
self.emit('end');
});
self.targetsocket.on('close', function (had_error) {
self.writable = self.targetsocket.writable;
self.emit('close', had_error);
});
self.targetsocket.on('drain', function () {
self.emit('drain');
});
self.targetsocket.on('error', function (exception) {
self.writable = self.targetsocket.writable;
self.emit('error', exception);
});
self.targetsocket.on('timeout', function () {
self.emit('timeout');
});
if (self.targetsocket.remotePort) {
self.remotePort = self.targetsocket.remotePort;
}
if (self.targetsocket.remoteAddress) {
self.remoteAddress = self.targetsocket.remoteAddress;
}
};
pluggableStream.prototype.clean = function (data) {
if (this.targetsocket && this.targetsocket.removeAllListeners) {
this.targetsocket.removeAllListeners('data');
this.targetsocket.removeAllListeners('secureConnection');
this.targetsocket.removeAllListeners('secure');
this.targetsocket.removeAllListeners('end');
this.targetsocket.removeAllListeners('close');
this.targetsocket.removeAllListeners('error');
this.targetsocket.removeAllListeners('drain');
}
this.targetsocket = {};
this.targetsocket.write = function () {};
};
pluggableStream.prototype.write = function (data, encoding, callback) {
if (this.targetsocket.write) {
return this.targetsocket.write(data, encoding, callback);
}
return false;
};
pluggableStream.prototype.end = function (data, encoding) {
if (this.targetsocket.end) {
return this.targetsocket.end(data, encoding);
}
}
pluggableStream.prototype.destroySoon = function () {
if (this.targetsocket.destroySoon) {
return this.targetsocket.destroySoon();
}
}
pluggableStream.prototype.destroy = function () {
if (this.targetsocket.destroy) {
return this.targetsocket.destroy();
}
}
pluggableStream.prototype.setKeepAlive = function (bool) {
this._keepalive = bool;
return this.targetsocket.setKeepAlive(bool);
};
pluggableStream.prototype.setNoDelay = function (/* true||false */) {
};
pluggableStream.prototype.setTimeout = function (timeout) {
this._timeout = timeout;
return this.targetsocket.setTimeout(timeout);
};
function pipe(pair, socket) {
pair.encrypted.pipe(socket);
socket.pipe(pair.encrypted);
pair.fd = socket.fd;
var cleartext = pair.cleartext;
cleartext.socket = socket;
cleartext.encrypted = pair.encrypted;
cleartext.authorized = false;
function onerror(e) {
if (cleartext._controlReleased) {
cleartext.emit('error', e);
}
}
function onclose() {
socket.removeListener('error', onerror);
socket.removeListener('close', onclose);
}
socket.on('error', onerror);
socket.on('close', onclose);
return cleartext;
}
function createServer(cb) {
var serv = net.createServer(function (cryptoSocket) {
var socket = new pluggableStream(cryptoSocket);
socket.upgrade = function (options, cb) {
console.log("Upgrading to TLS");
socket.clean();
cryptoSocket.removeAllListeners('data');
// Set SSL_OP_ALL for maximum compatibility with broken clients
// See http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
if (!options) options = {};
// TODO: bug in Node means we can't do this until it's fixed
// options.secureOptions = SSL_OP_ALL;
var sslcontext = crypto.createCredentials(options);
var pair = tls.createSecurePair(sslcontext, true, true, false);
var cleartext = pipe(pair, cryptoSocket);
pair.on('error', function(exception) {
socket.emit('error', exception);
});
pair.on('secure', function() {
var verifyError = (pair.ssl || pair._ssl).verifyError();
console.log("TLS secured.");
if (verifyError) {
cleartext.authorized = false;
cleartext.authorizationError = verifyError;
} else {
cleartext.authorized = true;
}
var cert = pair.cleartext.getPeerCertificate();
if (pair.cleartext.getCipher) {
var cipher = pair.cleartext.getCipher();
}
socket.emit('secure');
if (cb) cb(cleartext.authorized, verifyError, cert, cipher);
});
cleartext._controlReleased = true;
socket.cleartext = cleartext;
if (socket._timeout) {
cleartext.setTimeout(socket._timeout);
}
cleartext.setKeepAlive(socket._keepalive);
socket.attach(socket.cleartext);
};
cb(socket);
});
return serv;
}
if (require('semver').gt(process.version, '0.7.0')) {
var _net_connect = function (options) {
return net.connect(options);
}
}
else {
var _net_connect = function (options) {
return net.connect(options.port, options.host);
}
}
function connect(port, host, cb) {
var options = {};
if (typeof port === 'object') {
options = port;
cb = host;
}
else {
options.port = port;
options.host = host;
}
var cryptoSocket = _net_connect(options);
var socket = new pluggableStream(cryptoSocket);
socket.upgrade = function (options) {
socket.clean();
cryptoSocket.removeAllListeners('data');
// Set SSL_OP_ALL for maximum compatibility with broken servers
// See http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
if (!options) options = {};
// TODO: bug in Node means we can't do this until it's fixed
// options.secureOptions = SSL_OP_ALL;
var sslcontext = crypto.createCredentials(options);
var pair = tls.createSecurePair(sslcontext, false);
socket.pair = pair;
var cleartext = pipe(pair, cryptoSocket);
pair.on('error', function(exception) {
socket.emit('error', exception);
});
pair.on('secure', function() {
var verifyError = (pair.ssl || pair._ssl).verifyError();
console.log("client TLS secured.");
if (verifyError) {
cleartext.authorized = false;
cleartext.authorizationError = verifyError;
} else {
cleartext.authorized = true;
}
if (cb) cb();
socket.emit('secure');
});
cleartext._controlReleased = true;
socket.cleartext = cleartext;
if (socket._timeout) {
cleartext.setTimeout(socket._timeout);
}
cleartext.setKeepAlive(socket._keepalive);
socket.attach(socket.cleartext);
console.log("client TLS upgrade in progress, awaiting secured.");
};
return (socket);
}
exports.connect = connect;
exports.createConnection = connect;
exports.Server = createServer;
exports.createServer = createServer;
tls.connect() doesn't support the server doing the upgrade unfortunately.
You have to use code similar to how Haraka does it - basically creating your own shim using a SecurePair.
See here for the code we use: https://github.com/baudehlo/Haraka/blob/master/tls_socket.js#L171
STARTTLS Client-Server is trivial to implement in node, but it be a little buggy so, we need to go around.
For Server of STARTTLS we need do it:
// sock come from: net.createServer(function(sock) { ... });
sock.removeAllListeners('data');
sock.removeAllListeners('error');
sock.write('220 Go ahead' + CRLF);
sock = new tls.TLSSocket(sock, { secureContext : tls.createSecureContext({ key: cfg.stls.key, cert: cfg.stls.cert }), rejectUnauthorized: false, isServer: true });
sock.setEncoding('utf8');
// 'secureConnect' event is buggy :/ we need to use 'secure' here.
sock.on('secure', function() {
// STARTTLS is done here. sock is a secure socket in server side.
sock.on('error', parseError);
sock.on('data', parseData);
});
For Client of STARTTLS we need do it:
// sock come from: net.connect(cfg.port, curr.exchange);
// here we already read the '220 Go ahead' from the server.
sock.removeAllListeners('data');
sock.removeAllListeners('error');
sock = tls.connect({ socket: sock, secureContext : tls.createSecureContext({ key: cfg.stls.key, cert: cfg.stls.cert }), rejectUnauthorized: false });
sock.on('secureConnect', function() {
// STARTTLS is done here. sock is a secure socket in client side.
sock.on('error', parseError);
sock.on('data', parseData);
// Resend the helo message.
sock.write(helo);
});

Resources