Skip to content

Commit

Permalink
add secure websocket support (TLS)
Browse files Browse the repository at this point in the history
  • Loading branch information
Barbosik committed Jul 7, 2016
1 parent 95da302 commit cf1c5dd
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 23 deletions.
62 changes: 39 additions & 23 deletions src/GameServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var pjson = require('../package.json');
var ini = require('./modules/ini.js');
var QuadNode = require('./QuadNode.js');
var PlayerCommand = require('./modules/PlayerCommand');
var HttpsServer = require('./HttpsServer');

// Project imports
var Packet = require('./packet');
Expand All @@ -20,6 +21,9 @@ var UserRoleEnum = require('./enum/UserRoleEnum');

// GameServer implementation
function GameServer() {
this.httpServer = null;
this.wsServer = null;

// Startup
this.run = true;
this.lastNodeId = 1;
Expand Down Expand Up @@ -138,34 +142,32 @@ GameServer.prototype.start = function() {
// Gamemode configurations
this.gameMode.onServerInit(this);

var options = {
port: this.config.serverPort,
perMessageDeflate: false
if (fs.existsSync('./ssl/key.pem') && fs.existsSync('./ssl/cert.pem')) {
// HTTP/TLS
var options = {
key: fs.readFileSync('./ssl/key.pem', 'utf8'),
cert: fs.readFileSync('./ssl/cert.pem', 'utf8')
};
Logger.info("TLS: supported");
this.httpServer = HttpsServer.createServer(options);
} else {
// HTTP only
Logger.warn("TLS: not supported (SSL certificate not found!)");
this.httpServer = http.createServer();
}
var wsOptions = {
server: this.httpServer,
perMessageDeflate: true
};

// Start the server
this.socketServer = new WebSocket.Server(options, this.onServerSocketOpen.bind(this));
this.socketServer.on('error', this.onServerSocketError.bind(this));
this.socketServer.on('connection', this.onClientSocketOpen.bind(this));
this.wsServer = new WebSocket.Server(wsOptions);
this.wsServer.on('error', this.onServerSocketError.bind(this));
this.wsServer.on('connection', this.onClientSocketOpen.bind(this));
this.httpServer.listen(this.config.serverPort, this.onHttpServerOpen.bind(this));

this.startStatsServer(this.config.serverStatsPort);
};

GameServer.prototype.onServerSocketError = function (error) {
Logger.error("WebSocket: "+ error.code + " - " + error.message);
switch (error.code) {
case "EADDRINUSE":
Logger.error("Server could not bind to port " + this.config.serverPort + "!");
Logger.error("Please close out of Skype or change 'serverPort' in gameserver.ini to a different number.");
break;
case "EACCES":
Logger.error("Please make sure you are running Ogar with root privileges.");
break;
}
process.exit(1); // Exits the program
};

GameServer.prototype.onServerSocketOpen = function () {
GameServer.prototype.onHttpServerOpen = function () {
// Spawn starting food
this.startingFood();

Expand All @@ -185,6 +187,20 @@ GameServer.prototype.onServerSocketOpen = function () {
}
};

GameServer.prototype.onServerSocketError = function (error) {
Logger.error("WebSocket: " + error.code + " - " + error.message);
switch (error.code) {
case "EADDRINUSE":
Logger.error("Server could not bind to port " + this.config.serverPort + "!");
Logger.error("Please close out of Skype or change 'serverPort' in gameserver.ini to a different number.");
break;
case "EACCES":
Logger.error("Please make sure you are running Ogar with root privileges.");
break;
}
process.exit(1); // Exits the program
};

GameServer.prototype.onClientSocketOpen = function (ws) {
if (this.config.serverMaxConnections > 0 && this.socketCount >= this.config.serverMaxConnections) {
ws.close(1000, "No slots");
Expand Down
92 changes: 92 additions & 0 deletions src/HttpsServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var http = require('http'),
https = require('https'),
inherits = require('util').inherits,
httpSocketHandler = http._connectionListener;

var isOldNode = /^v0\.10\./.test(process.version);

function Server(tlsconfig, requestListener) {
if (!(this instanceof Server))
return new Server(tlsconfig, requestListener);

if (typeof tlsconfig === 'function') {
requestListener = tlsconfig;
tlsconfig = undefined;
}

if (typeof tlsconfig === 'object') {
this.removeAllListeners('connection');

https.Server.call(this, tlsconfig, requestListener);

// capture https socket handler, it's not exported like http's socket
// handler
var connev = this._events.connection;
if (typeof connev === 'function')
this._tlsHandler = connev;
else
this._tlsHandler = connev[connev.length - 1];
this.removeListener('connection', this._tlsHandler);

this._connListener = connectionListener;
this.on('connection', connectionListener);

// copy from http.Server
this.timeout = 2 * 60 * 1000;
this.allowHalfOpen = true;
this.httpAllowHalfOpen = false;
} else
http.Server.call(this, requestListener);
}
inherits(Server, https.Server);

Server.prototype.setTimeout = function (msecs, callback) {
this.timeout = msecs;
if (callback)
this.on('timeout', callback);
};

Server.prototype.__httpSocketHandler = httpSocketHandler;

var connectionListener;
if (isOldNode) {
connectionListener = function (socket) {
var self = this;
socket.ondata = function (d, start, end) {
var firstByte = d[start];
if (firstByte < 32 || firstByte >= 127) {
// tls/ssl
socket.ondata = null;
self._tlsHandler(socket);
socket.push(d.slice(start, end));
} else {
self.__httpSocketHandler(socket);
socket.ondata(d, start, end);
}
};
};
} else {
connectionListener = function (socket) {
var self = this;
var data = socket.read(1);
if (data === null) {
socket.once('readable', function () {
self._connListener(socket);
});
} else {
var firstByte = data[0];
socket.unshift(data);
if (firstByte < 32 || firstByte >= 127) {
// tls/ssl
this._tlsHandler(socket);
} else
this.__httpSocketHandler(socket);
}
};
}

exports.Server = Server;

exports.createServer = function (tlsconfig, requestListener) {
return new Server(tlsconfig, requestListener);
};
7 changes: 7 additions & 0 deletions ssl/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Place private key and certificate files here:
key.pem - your private key
cert.pem - your certificate

You can create it with openssl:

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 100 -nodes

0 comments on commit cf1c5dd

Please sign in to comment.