From fd09bebebc9925e8f16edc992657de87cd36b399 Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Tue, 13 Aug 2019 11:49:05 -0700 Subject: [PATCH] Improved session recording support. --- agents/meshcmd.js | 2 +- agents/meshcore.js | 2 +- agents/recoverycore.js | 2 +- meshrelay.js | 16 +++---- public/commander.htm | 21 ++++----- public/player.htm | 62 ++++++++++++++++++++------ public/scripts/agent-redir-ws-0.1.0.js | 4 +- public/scripts/amt-redir-ws-0.1.0.js | 5 +++ views/default-min.handlebars | 2 +- views/default-mobile-min.handlebars | 2 +- views/default.handlebars | 11 +++++ views/messenger-min.handlebars | 2 +- views/messenger.handlebars | 2 +- webserver.js | 37 +++++++++++---- 14 files changed, 123 insertions(+), 47 deletions(-) diff --git a/agents/meshcmd.js b/agents/meshcmd.js index eb51f458..55d381a3 100644 --- a/agents/meshcmd.js +++ b/agents/meshcmd.js @@ -1982,7 +1982,7 @@ function OnWebSocket(msg, s, head) { s.on('data', function (msg) { if (this.parent.tunneling == false) { msg = msg.toString(); - if (msg == 'c') { + if ((msg == 'c') || (msg == 'cr')) { this.parent.tunneling = true; this.pipe(this.parent.tcp); this.parent.tcp.pipe(this); debug(1, 'Tunnel active'); } else if ((msg.length > 6) && (msg.substring(0, 6) == 'error:')) { console.log(msg.substring(6)); diff --git a/agents/meshcore.js b/agents/meshcore.js index d1266058..27cbbcf5 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -1042,7 +1042,7 @@ function createMeshCore(agent) if (this.httprequest.state == 0) { // Check if this is a relay connection - if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } + if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ } } else { // Handle tunnel data if (this.httprequest.protocol == 0) { // 1 = Terminal, 2 = Desktop, 5 = Files, 6 = PowerShell diff --git a/agents/recoverycore.js b/agents/recoverycore.js index 762fa91f..cdd29028 100644 --- a/agents/recoverycore.js +++ b/agents/recoverycore.js @@ -188,7 +188,7 @@ require('MeshAgent').AddCommandHandler(function (data) if (this.httprequest.state == 0) { // Check if this is a relay connection - if (data == 'c') { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); } + if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid); } } else { // Handle tunnel data if (this.httprequest.protocol == 0) diff --git a/meshrelay.js b/meshrelay.js index f4a3cb15..43efeb4a 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -188,8 +188,8 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie var firstBlock = JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, userid: sessionUser._id, username: sessionUser.name, sessionid: obj.id, ipaddr1: cleanRemoteAddr(ws._socket.remoteAddress), ipaddr2: cleanRemoteAddr(obj.peer.ws._socket.remoteAddress), time: new Date().toLocaleString(), protocol: req.query.p, nodeid: req.query.nodeid }); recordingEntry(fd, 1, ((req.query.browser) ? 2 : 0), firstBlock, function () { relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false }; - ws.send('c'); // Send connect to both peers - relayinfo.peer1.ws.send('c'); + ws.send('cr'); // Send connect to both peers, 'cr' indicates the session is being recorded. + relayinfo.peer1.ws.send('cr'); }); } }); @@ -285,7 +285,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie var peer = (relayinfo.peer1 == obj) ? relayinfo.peer2 : relayinfo.peer1; // Close the recording file - if (ws.logfile != null) { parent.parent.fs.close(ws.logfile.fd); ws.logfile = null; peer.ws.logfile = null; } + if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, tag) { parent.parent.fs.close(fd); tag.ws.logfile = null; tag.pws.logfile = null; }, { ws: ws, pws: peer.ws }); } // Disconnect the peer try { if (peer.relaySessionCounted) { parent.relaySessionCount--; delete peer.relaySessionCounted; } } catch (ex) { console.log(ex); } @@ -327,7 +327,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie } // Record a new entry in a recording log - function recordingEntry(fd, type, flags, data, func) { + function recordingEntry(fd, type, flags, data, func, tag) { try { if (typeof data == 'string') { // String write @@ -337,7 +337,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie header.writeInt32BE(blockData.length, 4); // Size header.writeIntBE(new Date(), 10, 6); // Time var block = Buffer.concat([header, blockData]); - parent.parent.fs.write(fd, block, 0, block.length, func); + parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); }); } else { // Binary write var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8) @@ -346,9 +346,9 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie header.writeInt32BE(data.length, 4); // Size header.writeIntBE(new Date(), 10, 6); // Time var block = Buffer.concat([header, data]); - parent.parent.fs.write(fd, block, 0, block.length, func); + parent.parent.fs.write(fd, block, 0, block.length, function () { func(fd, tag); }); } - } catch (ex) { console.log(ex); func(); } + } catch (ex) { console.log(ex); func(fd, tag); } } // Mark this relay session as authenticated if this is the user end. @@ -418,7 +418,7 @@ The recording files are binary and contain a set of: The header is always 16 bytes long and is encoded like this: - TYPE 2 bytes, 1 = Header, 2 = Network Data + TYPE 2 bytes, 1 = Header, 2 = Network Data, 3 = EndBlock FLAGS 2 bytes, 0x0001 = Binary, 0x0002 = User SIZE 4 bytes, Size of the data following this header. TIME 8 bytes, Time this record was written, number of milliseconds since 1 January, 1970 UTC. diff --git a/public/commander.htm b/public/commander.htm index a12ab302..77be6702 100644 --- a/public/commander.htm +++ b/public/commander.htm @@ -1,4 +1,4 @@ -
  
Disconnected
{{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}

{{{logoutControl}}}

 

\ No newline at end of file diff --git a/views/default-mobile-min.handlebars b/views/default-mobile-min.handlebars index d71d4a39..038ce246 100644 --- a/views/default-mobile-min.handlebars +++ b/views/default-mobile-min.handlebars @@ -1 +1 @@ - {{{title}}}
{{{title}}}
{{{title2}}}
\ No newline at end of file + {{{title}}}
{{{title}}}
{{{title2}}}
\ No newline at end of file diff --git a/views/default.handlebars b/views/default.handlebars index 6483c4d9..f2628508 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -443,6 +443,7 @@
+ @@ -541,6 +542,7 @@
+
@@ -600,6 +602,7 @@
+
@@ -4816,6 +4819,7 @@ desktopNode = desktop = null; QV('DeskFocus', false); QV('termdisplays', false); + QV('deskRecordIcon', false); deskFocusBtn.value = 'All Focus'; if (fullscreen == true) { deskToggleFull(); } webRtcDesktopReset(); @@ -4823,6 +4827,9 @@ break; case 2: break; + case 3: + if (desktop.serverIsRecording == true) { QV('deskRecordIcon', true); } + break; default: //console.log('Unknown onDesktopStateChange state', state); break; @@ -5380,12 +5387,14 @@ // Disconnected, clear the terminal QE('termSizeList', true); QH('termtitle', ''); + QV('termRecordIcon', false); xterminal.m.TermResetScreen(); xterminal.m.TermDraw(); if (terminal != null) { terminal.Stop(); terminal = null; } break; case 3: QE('termSizeList', false); + if (xterminal.serverIsRecording == true) { QV('termRecordIcon', true); } break; default: QE('termSizeList', false); @@ -5531,12 +5540,14 @@ p13filetreelocation = []; QH('p13currentpath', ''); QE('p13FolderUp', false); + QV('filesRecordIcon', false); p13setActions(); if (files != null) { files.Stop(); files = null; } break; case 3: p13targetpath = ''; files.sendText({ action: 'ls', reqid: 1, path: '' }); + if (files.serverIsRecording == true) { QV('filesRecordIcon', true); } break; default: //console.log('Unknown onFilesStateChange state', state); diff --git a/views/messenger-min.handlebars b/views/messenger-min.handlebars index d9fc54dc..e0ea5675 100644 --- a/views/messenger-min.handlebars +++ b/views/messenger-min.handlebars @@ -1 +1 @@ - MeshMessenger
MeshMessenger
\ No newline at end of file + MeshMessenger
MeshMessenger
\ No newline at end of file diff --git a/views/messenger.handlebars b/views/messenger.handlebars index f0adcc0d..0ea4f482 100644 --- a/views/messenger.handlebars +++ b/views/messenger.handlebars @@ -579,7 +579,7 @@ socket.onerror = function (e) { /*console.error(e);*/ } socket.onclose = function () { disconnect(); } socket.onmessage = function (msg) { - if ((state < 2) && (typeof msg.data == 'string') && (msg.data == 'c')) { + if ((state < 2) && (typeof msg.data == 'string') && ((msg.data == 'c') || (msg.data == 'cr'))) { hangUpButtonClick(0, true); hangUpButtonClick(1, true); hangUpButtonClick(2, true); diff --git a/webserver.js b/webserver.js index b03eaab8..afc0c8b8 100644 --- a/webserver.js +++ b/webserver.js @@ -1978,6 +1978,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var firstBlock = JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, userid: user._id, username: user.name, ipaddr: cleanRemoteAddr(ws._socket.remoteAddress), nodeid: node._id, intelamt: true, protocol: (req.query.p == 2) ? 101 : 100, time: new Date().toLocaleString() }) recordingEntry(fd, 1, 0, firstBlock, function () { }); ws.logfile = { fd: fd, lock: false }; + if (req.query.p == 2) { ws.send(Buffer.from(String.fromCharCode(0xF0), 'binary')); } // Intel AMT Redirection: Indicate the session is being recorded } } @@ -2073,7 +2074,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS // Close the recording file - if (ws.logfile != null) { obj.fs.close(ws.logfile.fd); ws.logfile = null; } + if (ws.logfile != null) { + recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { + obj.fs.close(fd); + ws.logfile = null; + }, ws); + } }); // If the web socket is closed, close the associated TCP connection. @@ -2082,7 +2088,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (ws.forwardclient && ws.forwardclient.close) { ws.forwardclient.close(); } // TODO: If TLS is used, we need to close the socket that is wrapped by TLS // Close the recording file - if (ws.logfile != null) { obj.fs.close(ws.logfile.fd); ws.logfile = null; } + if (ws.logfile != null) { + recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { + obj.fs.close(fd); + ws.logfile = null; + }, ws); + } }); ws.forwardclient.onStateChange = function (ciraconn, state) { @@ -2156,7 +2167,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } } // Close the recording file - if (ws.logfile != null) { obj.fs.close(ws.logfile.fd); ws.logfile = null; } + if (ws.logfile != null) { + recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) { + obj.fs.close(fd); + ws.logfile = null; + }); + } }); // If the web socket is closed, close the associated TCP connection. @@ -2165,7 +2181,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } } // Close the recording file - if (ws.logfile != null) { obj.fs.close(ws.logfile.fd); ws.logfile = null; } + if (ws.logfile != null) { + recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) { + obj.fs.close(fd); + ws.logfile = null; + }); + } }); // Compute target port @@ -3467,7 +3488,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { function cleanRemoteAddr(addr) { if (addr.startsWith('::ffff:')) { return addr.substring(7); } else { return addr; } } // Record a new entry in a recording log - function recordingEntry(fd, type, flags, data, func) { + function recordingEntry(fd, type, flags, data, func, tag) { try { if (typeof data == 'string') { // String write @@ -3477,7 +3498,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { header.writeInt32BE(blockData.length, 4); // Size header.writeIntBE(new Date(), 10, 6); // Time var block = Buffer.concat([header, blockData]); - obj.fs.write(fd, block, 0, block.length, func); + obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); }); } else { // Binary write var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8) @@ -3486,9 +3507,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { header.writeInt32BE(data.length, 4); // Size header.writeIntBE(new Date(), 10, 6); // Time var block = Buffer.concat([header, data]); - obj.fs.write(fd, block, 0, block.length, func); + obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); }); } - } catch (ex) { console.log(ex); func(); } + } catch (ex) { console.log(ex); func(fd, tag); } } return obj;