From 62daa4893f374c1cbbecc63e4f1d4eec3fd29325 Mon Sep 17 00:00:00 2001 From: marzavec Date: Sun, 3 Jun 2018 11:08:35 -0700 Subject: Completed protocol decoupling --- server/src/commands/core/chat.js | 1 + server/src/commands/core/disconnect.js | 21 -------------------- server/src/commands/core/ping.js | 12 ++++++++++++ server/src/commands/internal/disconnect.js | 30 +++++++++++++++++++++++++++++ server/src/commands/internal/socketreply.js | 22 +++++++++++++++++++++ server/src/commands/mod/dumb.js | 14 +++++++------- server/src/commands/mod/speak.js | 11 +++++------ server/src/core/server.js | 16 ++++++++++++--- server/src/managers/commands.js | 26 +++++++++++++------------ 9 files changed, 104 insertions(+), 49 deletions(-) delete mode 100644 server/src/commands/core/disconnect.js create mode 100644 server/src/commands/core/ping.js create mode 100644 server/src/commands/internal/disconnect.js create mode 100644 server/src/commands/internal/socketreply.js (limited to 'server/src') diff --git a/server/src/commands/core/chat.js b/server/src/commands/core/chat.js index 80241d5..168c0bb 100644 --- a/server/src/commands/core/chat.js +++ b/server/src/commands/core/chat.js @@ -48,6 +48,7 @@ exports.run = async (core, server, socket, data) => { payload.trip = socket.trip; } + // TODO: Add a more contained way for modules to interact, event hooks or something? if(core.muzzledHashes && core.muzzledHashes[socket.hash]){ server.broadcast( payload, { channel: socket.channel, hash: socket.hash }); if(core.muzzledHashes[socket.hash].allies){ diff --git a/server/src/commands/core/disconnect.js b/server/src/commands/core/disconnect.js deleted file mode 100644 index 9b54214..0000000 --- a/server/src/commands/core/disconnect.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - Description: This module will be directly called by the server event handler - when a socket connection is closed or lost. It can calso be called - by a client to have the connection severed. -*/ - -exports.run = async (core, server, socket, data) => { - if (socket.channel) { - server.broadcast({ - cmd: 'onlineRemove', - nick: socket.nick - }, { channel: socket.channel }); - } - - socket.terminate(); -}; - -exports.info = { - name: 'disconnect', - description: 'Event handler or force disconnect (if your into that kind of thing)' -}; \ No newline at end of file diff --git a/server/src/commands/core/ping.js b/server/src/commands/core/ping.js new file mode 100644 index 0000000..cf1d8a4 --- /dev/null +++ b/server/src/commands/core/ping.js @@ -0,0 +1,12 @@ +/* + Description: This module is only in place to supress error notices legacy sources may get +*/ + +exports.run = async (core, server, socket, data) => { + return; +}; + +exports.info = { + name: 'ping', + description: 'This module is only in place to supress error notices legacy sources may get' +}; diff --git a/server/src/commands/internal/disconnect.js b/server/src/commands/internal/disconnect.js new file mode 100644 index 0000000..bb3981b --- /dev/null +++ b/server/src/commands/internal/disconnect.js @@ -0,0 +1,30 @@ +/* + Description: This module will be directly called by the server event handler + when a socket connection is closed or lost. +*/ + +exports.run = async (core, server, socket, data) => { + if (data.cmdKey !== server._cmdKey) { + // internal command attempt by client, increase rate limit chance and ignore + server._police.frisk(socket.remoteAddress, 20); + + return; + } + + if (socket.channel) { + server.broadcast({ + cmd: 'onlineRemove', + nick: socket.nick + }, { channel: socket.channel }); + } + + socket.terminate(); +}; + +exports.requiredData = ['cmdKey']; + +exports.info = { + name: 'disconnect', + usage: 'Internal Use Only', + description: 'Internally used to relay `onlineRemove` event to clients' +}; diff --git a/server/src/commands/internal/socketreply.js b/server/src/commands/internal/socketreply.js new file mode 100644 index 0000000..82834b7 --- /dev/null +++ b/server/src/commands/internal/socketreply.js @@ -0,0 +1,22 @@ +/* + Description: Used to relay warnings to clients internally +*/ + +exports.run = async (core, server, socket, data) => { + if (data.cmdKey !== server._cmdKey) { + // internal command attempt by client, increase rate limit chance and ignore + server._police.frisk(socket.remoteAddress, 20); + + return; + } + + server.reply({ cmd: 'warn', text: data.text }, socket); +}; + +exports.requiredData = ['cmdKey', 'text']; + +exports.info = { + name: 'socketreply', + usage: 'Internal Use Only', + description: 'Internally used to relay warnings to clients' +}; diff --git a/server/src/commands/mod/dumb.js b/server/src/commands/mod/dumb.js index ef2c5b2..0708fb8 100644 --- a/server/src/commands/mod/dumb.js +++ b/server/src/commands/mod/dumb.js @@ -1,4 +1,4 @@ -/* +/* * Description: Make a user (spammer) dumb * Author: simple */ @@ -16,7 +16,7 @@ exports.run = async (core, server, socket, data) => { if (typeof data.nick !== 'string') { return; } - + let badClient = server.findSockets({ channel: socket.channel, nick: data.nick }); if (badClient.length === 0) { @@ -38,20 +38,20 @@ exports.run = async (core, server, socket, data) => { return; } - + let record = core.muzzledHashes[badClient.hash] = { dumb:true } - + if(data.allies && Array.isArray(data.allies)){ record.allies = data.allies; } - + server.broadcast({ cmd: 'info', text: `${socket.nick} muzzled ${data.nick} in ${socket.channel}, userhash: ${badClient.hash}` }, { uType: 'mod' }); - + } exports.requiredData = ['nick']; @@ -59,5 +59,5 @@ exports.requiredData = ['nick']; exports.info = { name: 'dumb', usage: 'dumb {nick} [allies...]', - description: 'Cleanly disable a user messages and make him dumb' + description: 'Globally shadow mute a connection. Optional allies array will see muted messages.' }; diff --git a/server/src/commands/mod/speak.js b/server/src/commands/mod/speak.js index 422ad2c..643280b 100644 --- a/server/src/commands/mod/speak.js +++ b/server/src/commands/mod/speak.js @@ -1,9 +1,8 @@ -/* +/* * Description: Pardon a dumb user to be able to speak again * Author: simple */ - exports.run = async (core, server, socket, data) => { if (socket.uType == 'user') { // ignore if not mod or admin @@ -18,7 +17,7 @@ exports.run = async (core, server, socket, data) => { return; } - + let target; if (typeof data.ip === 'string') { @@ -26,14 +25,14 @@ exports.run = async (core, server, socket, data) => { } else { target = data.hash; } - + delete core.muzzledHashes[target]; - + server.broadcast({ cmd: 'info', text: `${socket.nick} unmuzzled : ${target}` }, { uType: 'mod' }); - + } exports.info = { diff --git a/server/src/core/server.js b/server/src/core/server.js index 855aeba..ded7cac 100644 --- a/server/src/core/server.js +++ b/server/src/core/server.js @@ -11,6 +11,7 @@ const wsServer = require('ws').Server; const socketReady = require('ws').OPEN; const crypto = require('crypto'); const ipSalt = (Math.random().toString(36).substring(2, 16) + Math.random().toString(36).substring(2, (Math.random() * 16))).repeat(16); +const internalCmdKey = (Math.random().toString(36).substring(2, 16) + Math.random().toString(36).substring(2, (Math.random() * 16))).repeat(16); const Police = require('./rateLimiter'); const pulseSpeed = 16000; // ping all clients every X ms @@ -26,6 +27,8 @@ class server extends wsServer { this._core = core; this._police = new Police(); this._cmdBlacklist = {}; + this._cmdKey = internalCmdKey; + this._heartBeat = setInterval(((data) => { this.beatHeart(); }).bind(this), pulseSpeed); @@ -90,7 +93,11 @@ class server extends wsServer { handleData (socket, data) { // Don't penalize yet, but check whether IP is rate-limited if (this._police.frisk(socket.remoteAddress, 0)) { - this.reply({ cmd: 'warn', text: "Your IP is being rate-limited or blocked." }, socket); + this._core.commands.handleCommand(this, socket, { + cmd: 'socketreply', + cmdKey: this._cmdKey, + text: 'Your IP is being rate-limited or blocked.' + }); return; } @@ -116,7 +123,7 @@ class server extends wsServer { return; } - if (typeof args.cmd === 'undefined' || args.cmd == 'ping') { + if (typeof args.cmd === 'undefined') { return; } @@ -142,7 +149,10 @@ class server extends wsServer { * @param {Object} socket Closing socket object */ handleClose (socket) { - this._core.commands.handleCommand(this, socket, { cmd: 'disconnect' }); + this._core.commands.handleCommand(this, socket, { + cmd: 'disconnect', + cmdKey: this._cmdKey + }); } /** diff --git a/server/src/managers/commands.js b/server/src/managers/commands.js index c38fb4d..fd743fb 100644 --- a/server/src/managers/commands.js +++ b/server/src/managers/commands.js @@ -182,10 +182,11 @@ class CommandManager { if (maybe) { // Found a suggestion, pass it on to their dyslexic self - return server.reply({ - cmd: 'warn', + return this.handleCommand(server, socket, { + cmd: 'socketreply', + cmdKey: server._cmdKey, text: `Command not found, did you mean: \`${maybe}\`?` - }, socket); + }); } // Request so mangled that I don't even, silently fail @@ -209,12 +210,12 @@ class CommandManager { } if (missing.length > 0) { - let errText = `Failed to execute '${command.info.name}': missing required ${missing.join(', ')}\n\n`; - - server.reply({ - cmd: 'warn', - text: errText - }, socket); + console.log(`Failed to execute '${command.info.name}': missing required ${missing.join(', ')}\n\n`); + this.handleCommand(server, socket, { + cmd: 'socketreply', + cmdKey: server._cmdKey, + text: `Failed to execute '${command.info.name}': missing required ${missing.join(', ')}\n\n` + }); return null; } @@ -226,10 +227,11 @@ class CommandManager { let errText = `Failed to execute '${command.info.name}': ${err}`; console.log(errText); - server.reply({ - cmd: 'warn', + this.handleCommand(server, socket, { + cmd: 'socketreply', + cmdKey: server._cmdKey, text: errText - }, socket); + }); return null; } -- cgit v1.2.1