add amt relay events and fixamt recordings #6652
Signed-off-by: si458 <simonsmith5521@gmail.com>
si458 committed
Apr 13, 2025 at 19:11 UTC
c24928255421d4e75b3fdd518f67ec6bb14aacc0
5 files changed
+262
-123
meshrelay.js
+67
-65
@@ -78,6 +78,72 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
78
}
79
}
80
81
+// Record a new entry in a recording log
82
+function recordingEntry (logfile, type, flags, data, func, tag) {
83
+ try {
84
+ if (logfile.text) {
85
+ // Text recording format
86
+ var out = '';
87
+ const utcDate = new Date(Date.now());
88
+ if (type == 1) {
89
+ // End of start
90
+ out = data + '\r\n' + utcDate.toUTCString() + ', ' + "<<<START>>>" + '\r\n';
91
+ } else if (type == 3) {
92
+ // End of log
93
+ out = utcDate.toUTCString() + ', ' + "<<<END>>>" + '\r\n';
94
+ } else if (typeof data == 'string') {
95
+ // Log message
96
+ if (logfile.text == 1) {
97
+ out = utcDate.toUTCString() + ', ' + data + '\r\n';
98
+ } else if (logfile.text == 2) {
99
+ try {
100
+ var x = JSON.parse(data);
101
+ if (typeof x.action == 'string') {
102
+ if ((x.action == 'chat') && (typeof x.msg == 'string')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + x.msg + '\r\n'); }
103
+ else if ((x.action == 'file') && (typeof x.name == 'string') && (typeof x.size == 'number')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + "File Transfer" + ', \"' + x.name + '\" (' + x.size + ' ' + "bytes" + ')\r\n'); }
104
+ } else if (x.ctrlChannel == null) { out = utcDate.toUTCString() + ', ' + data + '\r\n'; }
105
+ } catch (ex) {
106
+ out = utcDate.toUTCString() + ', ' + data + '\r\n';
107
+ }
108
+ }
109
+ }
110
+ if (out != null) {
111
+ // Log this event
112
+ const block = Buffer.from(out);
113
+ require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
114
+ logfile.size += block.length;
115
+ } else {
116
+ // Skip logging this.
117
+ func(logfile, tag);
118
+ }
119
+ } else {
120
+ // Binary recording format
121
+ if (typeof data == 'string') {
122
+ // String write
123
+ var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
124
+ header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
125
+ header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
126
+ header.writeInt32BE(blockData.length, 4); // Size
127
+ header.writeIntBE(new Date(), 10, 6); // Time
128
+ var block = Buffer.concat([header, blockData]);
129
+ require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
130
+ logfile.size += block.length;
131
+ } else {
132
+ // Binary write
133
+ var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
134
+ header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
135
+ header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
136
+ header.writeInt32BE(data.length, 4); // Size
137
+ header.writeIntBE(new Date(), 10, 6); // Time
138
+ var block = Buffer.concat([header, data]);
139
+ require('fs').write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
140
+ logfile.size += block.length;
141
+ }
142
+ }
143
+ } catch (ex) { console.log(ex); func(logfile, tag); }
144
+}
145
+module.exports.recordingEntry = recordingEntry;
146
+
147
function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
148
const currentTime = Date.now();
149
if (cookie) {
@@ -751,6 +817,7 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
817
setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
818
recordingEntry(logfile, 3, 0, 'MeshCentralMCREC', function (logfile, tag) {
819
parent.parent.fs.closeSync(logfile.fd);
820
+ parent.parent.debug('relay', 'Relay: Finished recording to file: ' + tag.logfile.filename);
821
822
// Now that the recording file is closed, check if we need to index this file.
823
if (domain.sessionrecording.index && domain.sessionrecording.index !== false) { parent.parent.certificateOperations.acceleratorPerformOperation('indexMcRec', tag.logfile.filename); }
@@ -796,71 +863,6 @@ function CreateMeshRelayEx(parent, ws, req, domain, user, cookie) {
863
if (obj.pid != null) { parent.parent.RemoveAllEventDispatch(obj); }
864
}
865
799
- // Record a new entry in a recording log
800
- function recordingEntry(logfile, type, flags, data, func, tag) {
801
- try {
802
- if (logfile.text) {
803
- // Text recording format
804
- var out = '';
805
- const utcDate = new Date(Date.now());
806
- if (type == 1) {
807
- // End of start
808
- out = data + '\r\n' + utcDate.toUTCString() + ', ' + "<<<START>>>" + '\r\n';
809
- } else if (type == 3) {
810
- // End of log
811
- out = utcDate.toUTCString() + ', ' + "<<<END>>>" + '\r\n';
812
- } else if (typeof data == 'string') {
813
- // Log message
814
- if (logfile.text == 1) {
815
- out = utcDate.toUTCString() + ', ' + data + '\r\n';
816
- } else if (logfile.text == 2) {
817
- try {
818
- var x = JSON.parse(data);
819
- if (typeof x.action == 'string') {
820
- if ((x.action == 'chat') && (typeof x.msg == 'string')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + x.msg + '\r\n'); }
821
- else if ((x.action == 'file') && (typeof x.name == 'string') && (typeof x.size == 'number')) { out = utcDate.toUTCString() + ', ' + (((flags & 2) ? '--> ' : '<-- ') + "File Transfer" + ', \"' + x.name + '\" (' + x.size + ' ' + "bytes" + ')\r\n'); }
822
- } else if (x.ctrlChannel == null) { out = utcDate.toUTCString() + ', ' + data + '\r\n'; }
823
- } catch (ex) {
824
- out = utcDate.toUTCString() + ', ' + data + '\r\n';
825
- }
826
- }
827
- }
828
- if (out != null) {
829
- // Log this event
830
- const block = Buffer.from(out);
831
- parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
832
- logfile.size += block.length;
833
- } else {
834
- // Skip logging this.
835
- func(logfile, tag);
836
- }
837
- } else {
838
- // Binary recording format
839
- if (typeof data == 'string') {
840
- // String write
841
- var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
842
- header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
843
- header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
844
- header.writeInt32BE(blockData.length, 4); // Size
845
- header.writeIntBE(new Date(), 10, 6); // Time
846
- var block = Buffer.concat([header, blockData]);
847
- parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
848
- logfile.size += block.length;
849
- } else {
850
- // Binary write
851
- var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
852
- header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
853
- header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
854
- header.writeInt32BE(data.length, 4); // Size
855
- header.writeIntBE(new Date(), 10, 6); // Time
856
- var block = Buffer.concat([header, data]);
857
- parent.parent.fs.write(logfile.fd, block, 0, block.length, function () { func(logfile, tag); });
858
- logfile.size += block.length;
859
- }
860
- }
861
- } catch (ex) { console.log(ex); func(logfile, tag); }
862
- }
863
-
866
// If this session has a expire time, setup the expire timer now.
867
setExpireTimer();
868
views/default.handlebars
+1
-1
@@ -9740,7 +9740,7 @@
9740
var rdpflags = 0;
9741
for (var i = 1; i < 10; i++) { if ((i != 5) && (Q('d7rdp' + i).checked)) { rdpflags |= (1 << (i - 1)); } }
9742
desktopsettings.rdpflags = rdpflags;
9743
- localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
9743
+ putstore('desktopsettings', JSON.stringify(desktopsettings));
9744
applyDesktopSettings();
9745
updateDesktopButtons();
9746
if (desktop) {
views/default3.handlebars
+1
-1
@@ -10506,7 +10506,7 @@
10506
var rdpflags = 0;
10507
for (var i = 1; i < 10; i++) { if ((i != 5) && (Q('d7rdp' + i).checked)) { rdpflags |= (1 << (i - 1)); } }
10508
desktopsettings.rdpflags = rdpflags;
10509
- localStorage.setItem('desktopsettings', JSON.stringify(desktopsettings));
10509
+ putstore('desktopsettings', JSON.stringify(desktopsettings));
10510
applyDesktopSettings();
10511
updateDesktopButtons();
10512
if (desktop) {
views/player.handlebars
+1
-1
@@ -577,7 +577,7 @@
577
var view = new Uint8Array(data.length);
578
for (var i = 0; i < data.length; i++) { view[i] = data.charCodeAt(i); }
579
580
- if ((readState == 0) && (rstr2hex(data) == '4100000000000000')) {
580
+ if ((readState == 0) && (rstr2hex(data).startsWith('4100000000000000'))) {
581
// We are not authenticated, KVM data starts here.
582
readState = 1;
583
if (data.length > 8) { amtDesktop.ProcessBinaryData(view.slice(8).buffer); }
webserver.js
+192
-55
@@ -4727,8 +4727,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4727
// Fetch information about the target
4728
obj.db.Get(req.query.host, function (err, docs) {
4729
if (docs.length == 0) { console.log('ERR: Node not found'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
4730
- var node = docs[0];
4730
+ var xusername = '', xdevicename = '', xdevicename2 = null, node = null;
4731
+ node = docs[0]; xdevicename2 = node.name; xdevicename = '-' + parent.common.makeFilename(node.name); ws.id = getRandomPassword(); ws.time = Date.now();
4732
if (!node.intelamt) { console.log('ERR: Not AMT node'); try { ws.close(); } catch (e) { } return; } // Disconnect websocket
4733
+ var ciraconn = parent.mpsserver.GetConnectionToNode(req.query.host, null, false);
4734
4735
// Check if this user has permission to manage this computer
4736
if ((obj.GetNodeRights(user, node.meshid, node._id) & MESHRIGHT_REMOTECONTROL) == 0) { console.log('ERR: Access denied (3)'); try { ws.close(); } catch (e) { } return; }
@@ -4782,7 +4784,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4784
4785
if (record == true) {
4786
var now = new Date(Date.now());
4785
- var recFilename = 'relaysession' + ((domain.id == '') ? '' : '-') + domain.id + '-' + now.getUTCFullYear() + '-' + obj.common.zeroPad(now.getUTCMonth() + 1, 2) + '-' + obj.common.zeroPad(now.getUTCDate(), 2) + '-' + obj.common.zeroPad(now.getUTCHours(), 2) + '-' + obj.common.zeroPad(now.getUTCMinutes(), 2) + '-' + obj.common.zeroPad(now.getUTCSeconds(), 2) + '-' + getRandomPassword() + '.mcrec'
4787
+ // Get the username and make it acceptable as a filename
4788
+ if (user._id) { xusername = '-' + parent.common.makeFilename(user._id.split('/')[2]); }
4789
+ var xsessionid = ws.id;
4790
+ var recFilename = 'relaysession' + ((domain.id == '') ? '' : '-') + domain.id + '-' + now.getUTCFullYear() + '-' + obj.common.zeroPad(now.getUTCMonth() + 1, 2) + '-' + obj.common.zeroPad(now.getUTCDate(), 2) + '-' + obj.common.zeroPad(now.getUTCHours(), 2) + '-' + obj.common.zeroPad(now.getUTCMinutes(), 2) + '-' + obj.common.zeroPad(now.getUTCSeconds(), 2) + xusername + xdevicename + '-' + xsessionid + '.mcrec';
4791
var recFullFilename = null;
4792
if (domain.sessionrecording.filepath) {
4793
try { obj.fs.mkdirSync(domain.sessionrecording.filepath); } catch (e) { }
@@ -4794,16 +4799,32 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4799
var fd = obj.fs.openSync(recFullFilename, 'w');
4800
if (fd != null) {
4801
// Write the recording file header
4797
- var firstBlock = JSON.stringify({ magic: 'MeshCentralRelaySession', ver: 1, userid: user._id, username: user.name, ipaddr: req.clientIp, nodeid: node._id, intelamt: true, protocol: (req.query.p == 2) ? 101 : 100, time: new Date().toLocaleString() })
4798
- recordingEntry(fd, 1, 0, firstBlock, function () { });
4799
- ws.logfile = { fd: fd, lock: false };
4802
+ parent.debug('relay', 'Relay: Started recording to file: ' + recFullFilename);
4803
+ var metadata = {
4804
+ magic: 'MeshCentralRelaySession',
4805
+ ver: 1,
4806
+ userid: user._id,
4807
+ username: user.name,
4808
+ sessionid: ws.id,
4809
+ ipaddr1: req.clientIp,
4810
+ time: new Date().toLocaleString(),
4811
+ protocol: (req.query.p == 2) ? 101 : 100,
4812
+ nodeid: node._id,
4813
+ intelamt: true
4814
+ };
4815
+ if (ciraconn != null) { metadata.ipaddr2 = ciraconn.remoteAddr; }
4816
+ else if ((conn & 4) != 0) { metadata.ipaddr2 = node.host; }
4817
+ if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
4818
+ var firstBlock = JSON.stringify(metadata)
4819
+ ws.logfile = { fd: fd, lock: false, filename: recFullFilename, startTime: Date.now(), size: 0, text: 0, req: req };
4820
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 1, 0, firstBlock, function () { });
4821
+ if (node != null) { ws.logfile.nodeid = node._id; ws.logfile.meshid = node.meshid; ws.logfile.name = node.name; ws.logfile.icon = node.icon; }
4822
if (req.query.p == 2) { ws.send(Buffer.from(String.fromCharCode(0xF0), 'binary')); } // Intel AMT Redirection: Indicate the session is being recorded
4823
}
4824
}
4825
}
4826
4827
// If Intel AMT CIRA connection is available, use it
4806
- var ciraconn = parent.mpsserver.GetConnectionToNode(req.query.host, null, false);
4828
if (ciraconn != null) {
4829
parent.debug('web', 'Opening relay CIRA channel connection to ' + req.query.host + '.');
4830
@@ -4865,7 +4886,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4886
try { ws.send(data); } catch (e) { }
4887
} else {
4888
// Log to recording file
4868
- recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
4889
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } }); // TODO: Add TLS support
4890
}
4891
}
4892
};
@@ -4897,7 +4918,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4918
try { ws.send(data); } catch (e) { }
4919
} else {
4920
// Log to recording file
4900
- recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } });
4921
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (ex) { console.log(ex); } });
4922
}
4923
}
4924
};
@@ -4921,7 +4942,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4942
try { ws.forwardclient.write(data); } catch (ex) { }
4943
} else {
4944
// Log to recording file
4924
- recordingEntry(ws.logfile.fd, 2, 2, data, function () { try { ws.forwardclient.write(data); } catch (ex) { } });
4945
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 2, data, function () { try { ws.forwardclient.write(data); } catch (ex) { } });
4946
}
4947
});
4948
@@ -4930,6 +4951,17 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4951
console.log('CIRA server websocket error from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
4952
parent.debug('webrelay', 'Websocket relay closed on error.');
4953
4954
+ // Log the disconnection
4955
+ if (ws.time) {
4956
+ var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
4957
+ var nodeid = node._id;
4958
+ var meshid = node.meshid;
4959
+ if (user) {
4960
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: nodeid };
4961
+ obj.parent.DispatchEvent(['*', user._id, nodeid, meshid], obj, event);
4962
+ }
4963
+ }
4964
+
4965
// Websocket closed, close the CIRA channel and TLS session.
4966
if (ws.forwardclient) {
4967
if (ws.forwardclient.close) { ws.forwardclient.close(); } // NonTLS, close the CIRA channel
@@ -4939,13 +4971,47 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
4971
}
4972
4973
// Close the recording file
4942
- if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); delete ws.logfile; }, ws); }
4974
+ if (ws.logfile != null) {
4975
+ setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
4976
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
4977
+ obj.fs.close(logfile.fd);
4978
+ parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
4979
+ // Compute session length
4980
+ var sessionLength = null;
4981
+ if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
4982
+ // Add a event entry about this recording
4983
+ var basefile = parent.path.basename(ws.logfile.filename);
4984
+ var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
4985
+ if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
4986
+ var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
4987
+ if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
4988
+ var mesh = obj.meshes[ws.logfile.meshid];
4989
+ if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
4990
+ if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
4991
+ if (ws.logfile.name) { event.name = ws.logfile.name; }
4992
+ if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
4993
+ obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
4994
+ delete ws.logfile;
4995
+ }, ws);
4996
+ }, 5000);
4997
+ }
4998
});
4999
5000
// If the web socket is closed, close the associated TCP connection.
4946
- ws.on('close', function (req) {
5001
+ ws.on('close', function () {
5002
parent.debug('webrelay', 'Websocket relay closed.');
5003
5004
+ // Log the disconnection
5005
+ if (ws.time) {
5006
+ var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
5007
+ var nodeid = node._id;
5008
+ var meshid = node.meshid;
5009
+ if (user) {
5010
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: nodeid };
5011
+ obj.parent.DispatchEvent(['*', user._id, nodeid, meshid], obj, event);
5012
+ }
5013
+ }
5014
+
5015
// Websocket closed, close the CIRA channel and TLS session.
5016
if (ws.forwardclient) {
5017
if (ws.forwardclient.close) { ws.forwardclient.close(); } // NonTLS, close the CIRA channel
@@ -4955,7 +5021,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5021
}
5022
5023
// Close the recording file
4958
- if (ws.logfile != null) { recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd, ws) { obj.fs.close(fd); delete ws.logfile; }, ws); }
5024
+ if (ws.logfile != null) {
5025
+ setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
5026
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
5027
+ obj.fs.close(logfile.fd);
5028
+ parent.debug('relay', 'Relay1: Finished recording to file: ' + ws.logfile.filename);
5029
+ // Compute session length
5030
+ var sessionLength = null;
5031
+ if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
5032
+ // Add a event entry about this recording
5033
+ var basefile = parent.path.basename(ws.logfile.filename);
5034
+ var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
5035
+ if (user) { event.userids = [user._id]; }
5036
+ var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
5037
+ if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
5038
+ var mesh = obj.meshes[ws.logfile.meshid];
5039
+ if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
5040
+ if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
5041
+ if (ws.logfile.name) { event.name = ws.logfile.name; }
5042
+ if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
5043
+ obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
5044
+ delete ws.logfile;
5045
+ }, ws);
5046
+ }, 5000);
5047
+ }
5048
});
5049
5050
// Note that here, req.query.p: 1 = WSMAN with server auth, 2 = REDIR with server auth, 3 = WSMAN without server auth, 4 = REDIR with server auth
@@ -4970,12 +5059,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5059
ws.interceptor = obj.interceptor.CreateRedirInterceptor({ user: node.intelamt.user, pass: node.intelamt.pass });
5060
ws.interceptor.blockAmtStorage = true;
5061
}
4973
-
4974
- return;
4975
- }
4976
-
4977
- // If Intel AMT direct connection is possible, option a direct socket
4978
- if ((conn & 4) != 0) { // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
5062
+ } else if ((conn & 4) != 0) { // If Intel AMT direct connection is possible, option a direct socket
5063
+ // We got a new web socket connection, initiate a TCP connection to the target Intel AMT host/port.
5064
parent.debug('webrelay', 'Opening relay TCP socket connection to ' + req.query.host + '.');
5065
5066
// When data is received from the web socket, forward the data into the associated TCP connection.
@@ -4991,7 +5076,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5076
try { ws.forwardclient.write(msg); } catch (ex) { }
5077
} else {
5078
// Log to recording file
4994
- recordingEntry(ws.logfile.fd, 2, 2, msg, function () { try { ws.forwardclient.write(msg); } catch (ex) { } });
5079
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 2, msg, function () { try { ws.forwardclient.write(msg); } catch (ex) { } });
5080
}
5081
});
5082
@@ -4999,28 +5084,84 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5084
ws.on('error', function (err) {
5085
console.log('Error with relay web socket connection from ' + req.clientIp + ', ' + err.toString().split('\r')[0] + '.');
5086
parent.debug('webrelay', 'Error with relay web socket connection from ' + req.clientIp + '.');
5087
+ // Log the disconnection
5088
+ if (ws.time) {
5089
+ var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
5090
+ var nodeid = node._id;
5091
+ var meshid = node.meshid;
5092
+ if (user) {
5093
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: nodeid };
5094
+ obj.parent.DispatchEvent(['*', user._id, nodeid, meshid], obj, event);
5095
+ }
5096
+ }
5097
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
5098
5099
// Close the recording file
5100
if (ws.logfile != null) {
5006
- recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) {
5007
- obj.fs.close(fd);
5008
- ws.logfile = null;
5009
- });
5101
+ setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
5102
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
5103
+ obj.fs.close(logfile.fd);
5104
+ parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
5105
+ // Compute session length
5106
+ var sessionLength = null;
5107
+ if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
5108
+ // Add a event entry about this recording
5109
+ var basefile = parent.path.basename(ws.logfile.filename);
5110
+ var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
5111
+ if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
5112
+ var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
5113
+ if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
5114
+ var mesh = obj.meshes[ws.logfile.meshid];
5115
+ if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
5116
+ if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
5117
+ if (ws.logfile.name) { event.name = ws.logfile.name; }
5118
+ if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
5119
+ obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
5120
+ delete ws.logfile;
5121
+ }, ws);
5122
+ }, 5000);
5123
}
5124
});
5125
5126
// If the web socket is closed, close the associated TCP connection.
5127
ws.on('close', function () {
5128
parent.debug('webrelay', 'Closing relay web socket connection to ' + req.query.host + '.');
5129
+ // Log the disconnection
5130
+ if (ws.time) {
5131
+ var msg = 'Ended relay session', msgid = 9, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
5132
+ var nodeid = node._id;
5133
+ var meshid = node.meshid;
5134
+ if (user) {
5135
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip, Math.floor((Date.now() - ws.time) / 1000)], msg: msg + ' \"' + ws.id + '\" from ' + req.clientIp + ' to ' + ip + ', ' + Math.floor((Date.now() - ws.time) / 1000) + ' second(s)', protocol: ((req.query.p == 2) ? 101 : 100), nodeid: nodeid };
5136
+ obj.parent.DispatchEvent(['*', user._id, nodeid, meshid], obj, event);
5137
+ }
5138
+ }
5139
if (ws.forwardclient) { try { ws.forwardclient.destroy(); } catch (e) { } }
5140
5141
// Close the recording file
5142
if (ws.logfile != null) {
5020
- recordingEntry(ws.logfile.fd, 3, 0, 'MeshCentralMCREC', function (fd) {
5021
- obj.fs.close(fd);
5022
- ws.logfile = null;
5023
- });
5143
+ setTimeout(function(){ // wait 5 seconds before finishing file for some reason?
5144
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 3, 0, 'MeshCentralMCREC', function (logfile, ws) {
5145
+ obj.fs.close(logfile.fd);
5146
+ parent.debug('relay', 'Relay: Finished recording to file: ' + ws.logfile.filename);
5147
+ // Compute session length
5148
+ var sessionLength = null;
5149
+ if (ws.logfile.startTime != null) { sessionLength = Math.round((Date.now() - ws.logfile.startTime) / 1000); }
5150
+ // Add a event entry about this recording
5151
+ var basefile = parent.path.basename(ws.logfile.filename);
5152
+ var event = { etype: 'relay', action: 'recording', domain: domain.id, nodeid: ws.logfile.nodeid, msg: "Finished recording session" + (sessionLength ? (', ' + sessionLength + ' second(s)') : ''), filename: basefile, size: ws.logfile.size };
5153
+ if (user) { event.userids = [user._id]; } else if (peer.user) { event.userids = [peer.user._id]; }
5154
+ var xprotocol = (((ws.logfile.req == null) || (ws.logfile.req.query == null)) ? null : (ws.logfile.req.query.p == 2) ? 101 : 100);
5155
+ if (xprotocol != null) { event.protocol = parseInt(xprotocol); }
5156
+ var mesh = obj.meshes[ws.logfile.meshid];
5157
+ if (mesh != null) { event.meshname = mesh.name; event.meshid = mesh._id; }
5158
+ if (ws.logfile.startTime) { event.startTime = ws.logfile.startTime; event.lengthTime = sessionLength; }
5159
+ if (ws.logfile.name) { event.name = ws.logfile.name; }
5160
+ if (ws.logfile.icon) { event.icon = ws.logfile.icon; }
5161
+ obj.parent.DispatchEvent(['*', 'recording', ws.logfile.nodeid, ws.logfile.meshid], obj, event);
5162
+ delete ws.logfile;
5163
+ }, ws);
5164
+ }, 5000);
5165
}
5166
});
5167
@@ -5064,7 +5205,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5205
try { ws.send(data); } catch (e) { }
5206
} else {
5207
// Log to recording file
5067
- recordingEntry(ws.logfile.fd, 2, 0, data, function () { try { ws.send(data); } catch (e) { } });
5208
+ obj.meshRelayHandler.recordingEntry(ws.logfile, 2, 0, data, function () { try { ws.send(data); } catch (e) { } });
5209
}
5210
});
5211
@@ -5092,9 +5233,30 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
5233
ws._socket.resume();
5234
});
5235
}
5095
- return;
5236
}
5237
5238
+ // Log the connection
5239
+ if (user != null) {
5240
+ var msg = 'Started relay session', msgid = 13, ip = ((ciraconn != null) ? ciraconn.remoteAddr : (((conn & 4) != 0) ? node.host : req.clientIp));
5241
+ var event = { etype: 'relay', action: 'relaylog', domain: domain.id, userid: user._id, username: user.name, msgid: msgid, msgArgs: [ws.id, req.clientIp, ip], msg: msg + ' \"' + obj.id + '\" from ' + req.clientIp + ' to ' + ip, protocol: ((req.query.p == 2) ? 101 : 100), nodeid: node._id };
5242
+ obj.parent.DispatchEvent(['*', user._id], obj, event);
5243
+
5244
+ // Update user last access time
5245
+ if ((user != null)) {
5246
+ const timeNow = Math.floor(Date.now() / 1000);
5247
+ if (user.access < (timeNow - 300)) { // Only update user access time if longer than 5 minutes
5248
+ user.access = timeNow;
5249
+ obj.parent.db.SetUser(user);
5250
+
5251
+ // Event the change
5252
+ var message = { etype: 'user', userid: user._id, username: user.name, account: obj.CloneSafeUser(user), action: 'accountchange', domain: domain.id, nolog: 1 };
5253
+ if (parent.db.changeStream) { message.noact = 1; } // If DB change stream is active, don't use this event to change the user. Another event will come.
5254
+ var targets = ['*', 'server-users', user._id];
5255
+ if (user.groups) { for (var i in user.groups) { targets.push('server-users:' + i); } }
5256
+ obj.parent.DispatchEvent(targets, obj, message);
5257
+ }
5258
+ }
5259
+ }
5260
});
5261
}
5262
@@ -9569,7 +9731,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
9731
// Generate a random Intel AMT password
9732
function checkAmtPassword(p) { return (p.length > 7) && (/\d/.test(p)) && (/[a-z]/.test(p)) && (/[A-Z]/.test(p)) && (/\W/.test(p)); }
9733
function getRandomAmtPassword() { var p; do { p = Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); } while (checkAmtPassword(p) == false); return p; }
9572
- function getRandomPassword() { return Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').split('/').join('@'); }
9734
+ function getRandomPassword() { return Buffer.from(obj.crypto.randomBytes(9), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); }
9735
function getRandomLowerCase(len) { var r = '', random = obj.crypto.randomBytes(len); for (var i = 0; i < len; i++) { r += String.fromCharCode(97 + (random[i] % 26)); } return r; }
9736
9737
// Generate a 8 digit integer with even random probability for each value.
@@ -9594,31 +9756,6 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
9756
}
9757
}
9758
9597
- // Record a new entry in a recording log
9598
- function recordingEntry(fd, type, flags, data, func, tag) {
9599
- try {
9600
- if (typeof data == 'string') {
9601
- // String write
9602
- var blockData = Buffer.from(data), header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
9603
- header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
9604
- header.writeInt16BE(flags, 2); // Flags (1 = Binary, 2 = User)
9605
- header.writeInt32BE(blockData.length, 4); // Size
9606
- header.writeIntBE(new Date(), 10, 6); // Time
9607
- var block = Buffer.concat([header, blockData]);
9608
- obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
9609
- } else {
9610
- // Binary write
9611
- var header = Buffer.alloc(16); // Header: Type (2) + Flags (2) + Size(4) + Time(8)
9612
- header.writeInt16BE(type, 0); // Type (1 = Header, 2 = Network Data)
9613
- header.writeInt16BE(flags | 1, 2); // Flags (1 = Binary, 2 = User)
9614
- header.writeInt32BE(data.length, 4); // Size
9615
- header.writeIntBE(new Date(), 10, 6); // Time
9616
- var block = Buffer.concat([header, data]);
9617
- obj.fs.write(fd, block, 0, block.length, function () { func(fd, tag); });
9618
- }
9619
- } catch (ex) { console.log(ex); func(fd, tag); }
9620
- }
9621
-
9759
// Perform a IP match against a list
9760
function isIPMatch(ip, matchList) {
9761
const ipcheck = require('ipcheck');