MeshCentral/agents/meshcore.js

4643 lines
260 KiB
JavaScript
Raw Normal View History

2017-08-28 12:27:45 -04:00
/*
2021-01-09 17:31:09 -05:00
Copyright 2018-2021 Intel Corporation
2017-08-28 12:27:45 -04:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
2018-01-16 20:30:34 -05:00
http://www.apache.org/licenses/LICENSE-2.0
2017-08-28 12:27:45 -04:00
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2018-09-20 14:45:12 -04:00
process.on('uncaughtException', function (ex) {
2020-01-07 16:04:36 -05:00
require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
2018-09-20 14:45:12 -04:00
});
// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
2018-11-26 20:12:27 -05:00
//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
2018-09-20 14:45:12 -04:00
// Mesh Rights
2019-06-14 19:33:53 -04:00
var MNG_ERROR = 65;
2018-12-29 18:24:33 -05:00
var MESHRIGHT_EDITMESH = 1;
var MESHRIGHT_MANAGEUSERS = 2;
var MESHRIGHT_MANAGECOMPUTERS = 4;
var MESHRIGHT_REMOTECONTROL = 8;
var MESHRIGHT_AGENTCONSOLE = 16;
var MESHRIGHT_SERVERFILES = 32;
var MESHRIGHT_WAKEDEVICE = 64;
var MESHRIGHT_SETNOTES = 128;
var MESHRIGHT_REMOTEVIEW = 256;
2019-03-01 14:47:58 -05:00
var MESHRIGHT_NOTERMINAL = 512;
var MESHRIGHT_NOFILES = 1024;
var MESHRIGHT_NOAMT = 2048;
var MESHRIGHT_LIMITEDINPUT = 4096;
var MESHRIGHT_LIMITEVENTS = 8192;
var MESHRIGHT_CHATNOTIFY = 16384;
var MESHRIGHT_UNINSTALL = 32768;
var MESHRIGHT_NODESKTOP = 65536;
2021-01-15 02:57:52 -05:00
if (require('MeshAgent').ARCHID == null)
{
var id = null;
switch (process.platform)
{
case 'win32':
id = require('_GenericMarshal').PointerSize == 4 ? 3 : 4;
break;
case 'freebsd':
id = require('_GenericMarshal').PointerSize == 4 ? 31 : 30;
break;
case 'darwin':
id = require('os').arch() == 'x64' ? 16 : 29;
break;
}
if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); }
}
2021-01-15 02:57:52 -05:00
function createMeshCore(agent)
{
2018-08-31 18:23:42 -04:00
var obj = {};
2020-11-08 04:13:23 -05:00
var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server.
2020-11-09 19:51:48 -05:00
var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server.
var debugConsole = (_MSH().debugConsole == 1);
2020-11-08 04:13:23 -05:00
if (process.platform == 'win32' && require('user-sessions').isRoot())
{
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2021-01-15 02:57:52 -05:00
try
{
2020-01-22 20:15:59 -05:00
var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
} catch (x) { }
// Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
try
{
svcname = require('MeshAgent').serviceName;
}
catch (x)
{
}
try
{
2020-01-22 20:15:59 -05:00
var meshCheck = false;
try { meshCheck = require('service-manager').manager.getService(svcname).isMe(); } catch (e) { }
if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService(svcname)) { require('win-bcd').enableSafeModeService(svcname); }
} catch (e) { }
}
2018-09-14 20:42:39 -04:00
2021-01-15 02:57:52 -05:00
if (process.platform == 'darwin' && !process.versions)
{
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); ");
child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
child.waitExit();
2021-01-15 02:57:52 -05:00
if (child.stdout.str.trim() == 'Crashed')
{
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
child.waitExit();
2021-01-15 02:57:52 -05:00
if (parseInt(child.stdout.str.trim()) == process.pid)
{
// The currently running MeshAgent is us, so we can continue with the update
var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
var tokens = plist.split('<key>KeepAlive</key>');
2021-01-15 02:57:52 -05:00
if (tokens[1].split('>')[0].split('<')[1] == 'dict')
{
var tmp = tokens[1].split('</dict>');
tmp.shift();
tokens[1] = '\n <true/>' + tmp.join('</dict>');
tokens = tokens.join('<key>KeepAlive</key>');
require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
var fix = '';
fix += ("function macosRepair()\n");
fix += ("{\n");
fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
fix += (" child.stdout.str = '';\n");
fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
fix += (" child.stderr.on('data', function (chunk) { });\n");
fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
2019-05-13 17:39:12 -04:00
fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
fix += (" child.waitExit();\n");
fix += ("}\n");
fix += ("macosRepair();\n");
fix += ("process.exit();\n");
require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
plist += '<plist version="1.0">\n';
plist += ' <dict>\n';
plist += ' <key>Label</key>\n';
plist += (' <string>meshagentRepair</string>\n');
plist += ' <key>ProgramArguments</key>\n';
plist += ' <array>\n';
plist += (' <string>' + process.execPath + '</string>\n');
plist += ' <string>macosRepair.js</string>\n';
plist += ' </array>\n';
plist += ' <key>WorkingDirectory</key>\n';
plist += (' <string>' + process.cwd() + '</string>\n');
plist += ' <key>RunAtLoad</key>\n';
plist += ' <true/>\n';
plist += ' </dict>\n';
plist += '</plist>';
require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
child.waitExit();
}
}
}
}
2020-10-26 15:46:30 -04:00
// Add an Intel AMT event to the log
2021-01-15 02:57:52 -05:00
function addAmtEvent(msg)
{
2020-10-26 15:46:30 -04:00
if (obj.amtevents == null) { obj.amtevents = []; }
var d = new Date();
obj.amtevents.push(zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg);
if (obj.amtevents.length > 100) { obj.amtevents.splice(0, obj.amtevents.length - 100); }
}
function zeroPad(num, size) { var s = '000000000' + num; return s.substr(s.length - size); }
2019-06-14 19:33:53 -04:00
// Create Secure IPC for Diagnostic Agent Communications
obj.DAIPC = require('net').createServer();
if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (e) { } }
2019-06-14 19:33:53 -04:00
obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
obj.DAIPC._daipc = [];
2021-01-15 02:57:52 -05:00
obj.DAIPC.on('connection', function (c)
{
c._send = function (j)
{
2019-06-14 19:33:53 -04:00
var data = JSON.stringify(j);
var packet = Buffer.alloc(data.length + 4);
packet.writeUInt32LE(data.length + 4, 0);
Buffer.from(data).copy(packet, 4);
this.write(packet);
2019-06-14 19:33:53 -04:00
};
this._daipc.push(c);
2019-06-14 19:33:53 -04:00
c.parent = this;
c.on('end', function () { removeRegisteredApp(this); });
2021-01-15 02:57:52 -05:00
c.on('data', function (chunk)
{
2019-06-14 19:33:53 -04:00
if (chunk.length < 4) { this.unshift(chunk); return; }
var len = chunk.readUInt32LE(0);
if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
2019-06-14 19:33:53 -04:00
if (chunk.length < len) { this.unshift(chunk); return; }
2019-10-10 14:13:25 -04:00
2019-06-14 19:33:53 -04:00
var data = chunk.slice(4, len);
try { data = JSON.parse(data.toString()); } catch (e) { }
if ((data == null) || (typeof data.cmd != 'string')) return;
2019-06-14 19:33:53 -04:00
2021-01-15 02:57:52 -05:00
try
{
switch (data.cmd)
{
2020-10-01 18:45:44 -04:00
case 'requesthelp':
if (this._registered == null) return;
sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
var help = {};
help[this._registered] = data.value;
try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
break;
case 'cancelhelp':
if (this._registered == null) return;
sendConsoleText('Cancel Help (' + this._registered + ')');
try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
break;
2020-10-01 13:30:26 -04:00
case 'register':
2021-01-15 02:57:52 -05:00
if (typeof data.value == 'string')
{
2020-10-01 13:30:26 -04:00
this._registered = data.value;
var apps = {};
apps[data.value] = 1;
try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
2020-10-12 17:01:24 -04:00
this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
2020-10-01 13:30:26 -04:00
}
break;
2019-06-14 19:33:53 -04:00
case 'query':
2020-12-10 02:53:23 -05:00
switch (data.value)
{
2019-06-14 19:33:53 -04:00
case 'connection':
data.result = require('MeshAgent').ConnectedServer;
this._send(data);
2020-05-29 16:07:15 -04:00
break;
case 'descriptors':
2021-01-15 02:57:52 -05:00
require('ChainViewer').getSnapshot().then(function (f)
{
this.tag.payload.result = f;
this.tag.ipc._send(this.tag.payload);
}).parentPromise.tag = { ipc: this, payload: data };
2019-06-14 19:33:53 -04:00
break;
2021-01-15 02:57:52 -05:00
case 'timerinfo':
2020-12-10 02:53:23 -05:00
data.result = require('ChainViewer').getTimerInfo();
this._send(data);
break;
2019-06-14 19:33:53 -04:00
}
break;
2020-10-12 17:01:24 -04:00
case 'amtstate':
if (amt == null) return;
var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
func.pipe = this;
amt.getMeiState(11, func);
2020-10-12 17:01:24 -04:00
break;
case 'sessions':
this._send({ cmd: 'sessions', sessions: tunnelUserCount });
break;
case 'meshToolInfo':
2021-01-15 02:57:52 -05:00
try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (e) { }
break;
case 'console':
2021-01-15 02:57:52 -05:00
if (debugConsole)
{
var args = splitArgs(data.value);
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe');
}
break;
2019-06-14 19:33:53 -04:00
}
}
catch (e) { removeRegisteredApp(this); this.end(); return; }
2019-06-14 19:33:53 -04:00
});
});
// Send current sessions to registered apps
2021-01-15 02:57:52 -05:00
function broadcastSessionsToRegisteredApps(x)
{
broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
}
// Send this object to all registered local applications
2021-01-15 02:57:52 -05:00
function broadcastToRegisteredApps(x)
{
if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
}
2020-11-28 00:40:30 -05:00
// Send this object to a specific registered local applications
2021-01-15 02:57:52 -05:00
function sendToRegisteredApp(appid, x)
{
2020-11-28 00:40:30 -05:00
if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } }
}
// Send list of registered apps to the server
2021-01-15 02:57:52 -05:00
function updateRegisteredAppsToServer()
{
if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
var apps = {};
for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
}
// Remove a registered app
2021-01-15 02:57:52 -05:00
function removeRegisteredApp(pipe)
{
for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
if (pipe._registered != null) updateRegisteredAppsToServer();
}
2021-01-15 02:57:52 -05:00
function diagnosticAgent_uninstall()
{
2019-06-14 19:33:53 -04:00
require('service-manager').manager.uninstallService('meshagentDiagnostic');
require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
};
2021-01-15 02:57:52 -05:00
function diagnosticAgent_installCheck(install)
{
try
{
2019-06-14 19:33:53 -04:00
var diag = require('service-manager').manager.getService('meshagentDiagnostic');
return (diag);
}
2021-01-15 02:57:52 -05:00
catch (e)
{
2019-06-14 19:33:53 -04:00
}
if (!install) { return (null); }
var svc = null;
2021-01-15 02:57:52 -05:00
try
{
2019-06-14 19:33:53 -04:00
require('service-manager').manager.installService(
{
name: 'meshagentDiagnostic',
2020-01-07 16:04:36 -05:00
displayName: "Mesh Agent Diagnostic Service",
description: "Mesh Agent Diagnostic Service",
2019-06-14 19:33:53 -04:00
servicePath: process.execPath,
parameters: ['-recovery']
//files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
});
svc = require('service-manager').manager.getService('meshagentDiagnostic');
}
2021-01-15 02:57:52 -05:00
catch (e)
{
2019-06-14 19:33:53 -04:00
return (null);
}
var proxyConfig = require('global-tunnel').proxyConfig;
var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
ddb.Put('disableUpdate', '1');
ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
2021-01-15 02:57:52 -05:00
if (proxyConfig)
{
2019-06-14 19:33:53 -04:00
ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
2021-01-15 02:57:52 -05:00
} else
{
2019-06-14 19:33:53 -04:00
ddb.Put('ignoreProxyFile', '1');
}
require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
2020-01-07 16:04:36 -05:00
require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
2019-06-14 19:33:53 -04:00
delete ddb;
// Set a recurrent task, to run the Diagnostic Agent every 2 days
2019-10-10 14:13:25 -04:00
require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
2019-06-14 19:33:53 -04:00
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
return (svc);
}
// Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
2021-01-15 02:57:52 -05:00
if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null))
{
// Setup manual battery monitoring
2021-01-15 02:57:52 -05:00
require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function ()
{
if (require('MeshAgent')._batteryFileTimer != null) return;
2021-01-15 02:57:52 -05:00
require('MeshAgent')._batteryFileTimer = setTimeout(function ()
{
try
{
require('MeshAgent')._batteryFileTimer = null;
var data = null;
try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
2021-01-15 02:57:52 -05:00
if ((data != null) && (data.length < 10))
{
data = data.split(',');
2021-01-15 02:57:52 -05:00
if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc')))
{
var level = parseInt(data[1]);
if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
}
}
} catch (e) { }
}, 1000);
2020-05-31 14:24:34 -04:00
});
2021-01-15 02:57:52 -05:00
} else
{
// Setup normal battery monitoring
2021-01-15 02:57:52 -05:00
if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered())
{
require('MeshAgent')._battLevelChanged = function _battLevelChanged(val)
{
_battLevelChanged.self._currentBatteryLevel = val;
_battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
};
require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
2021-01-15 02:57:52 -05:00
require('MeshAgent')._powerChanged = function _powerChanged(val)
{
_powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
_powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
};
require('MeshAgent')._powerChanged.self = require('MeshAgent');
2021-01-15 02:57:52 -05:00
require('MeshAgent').on('Connected', function (status)
{
if (status == 0)
{
require('power-monitor').removeListener('acdc', this._powerChanged);
require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
2021-01-15 02:57:52 -05:00
} else
{
require('power-monitor').on('acdc', this._powerChanged);
require('power-monitor').on('batteryLevel', this._battLevelChanged);
}
});
}
}
2020-05-31 14:24:34 -04:00
2018-09-20 14:45:12 -04:00
/*
2018-08-31 18:23:42 -04:00
function borderController() {
this.container = null;
this.Start = function Start(user) {
if (this.container == null) {
if (process.platform == 'win32') {
2018-09-21 19:34:35 -04:00
try {
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
} catch (e) {
2018-09-21 19:34:35 -04:00
this.container = require('ScriptContainer').Create({ processIsolation: 1 });
}
} else {
2018-08-31 18:23:42 -04:00
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
}
this.container.parent = this;
this.container.addModule('monitor-info', getJSModule('monitor-info'));
this.container.addModule('monitor-border', getJSModule('monitor-border'));
this.container.addModule('promise', getJSModule('promise'));
this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
}
}
2018-08-31 18:23:42 -04:00
this.Stop = function Stop() {
if (this.container != null) {
this._container = this.container;
this._container.parent = this;
this.container = null;
this._container.exit();
}
}
}
2018-09-21 19:34:35 -04:00
obj.borderManager = new borderController();
2018-09-20 14:45:12 -04:00
*/
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
2020-04-08 22:16:33 -04:00
// Get the operating system description string
2020-11-02 03:44:07 -05:00
try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (e) { }
// Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
2021-01-15 02:57:52 -05:00
try
{
2020-11-02 03:44:07 -05:00
var userSession = require('user-sessions');
2021-01-15 02:57:52 -05:00
userSession.on('changed', function onUserSessionChanged()
{
userSession.enumerateUsers().then(function (users)
{
2020-11-02 03:44:07 -05:00
var u = [], a = users.Active;
2021-01-15 02:57:52 -05:00
for (var i = 0; i < a.length; i++)
{
2020-11-02 03:44:07 -05:00
var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
}
meshCoreObj.users = u;
meshCoreObjChanged();
});
});
userSession.emit('changed');
//userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
} catch (e) { }
2017-08-28 12:27:45 -04:00
var meshServerConnectionState = 0;
var tunnels = {};
var lastNetworkInfo = null;
var lastPublicLocationInfo = null;
2017-08-28 12:27:45 -04:00
var selfInfoUpdateTimer = null;
var http = require('http');
2017-10-23 17:09:58 -04:00
var net = require('net');
2017-08-28 12:27:45 -04:00
var fs = require('fs');
var rtc = require('ILibWebRTC');
var amt = null;
2018-04-11 16:49:05 -04:00
var processManager = require('process-manager');
var wifiScannerLib = null;
var wifiScanner = null;
var networkMonitor = null;
2018-01-18 18:43:43 -05:00
var nextTunnelIndex = 1;
var apftunnel = null;
2020-07-28 21:45:17 -04:00
var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
2018-09-27 19:17:05 -04:00
2019-07-29 19:35:48 -04:00
// Add to the server event log
2021-01-15 02:57:52 -05:00
function MeshServerLog(msg, state)
{
2020-01-07 16:04:36 -05:00
if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
2021-01-15 02:57:52 -05:00
if (state)
{
2019-07-29 19:35:48 -04:00
if (state.userid) { msg.userid = state.userid; }
if (state.username) { msg.username = state.username; }
if (state.sessionid) { msg.sessionid = state.sessionid; }
if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
}
mesh.SendCommand(msg);
}
// Add to the server event log, use internationalized events
2021-01-15 02:57:52 -05:00
function MeshServerLogEx(id, args, msg, state)
{
var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
2021-01-15 02:57:52 -05:00
if (state)
{
if (state.userid) { msg.userid = state.userid; }
if (state.username) { msg.username = state.username; }
if (state.sessionid) { msg.sessionid = state.sessionid; }
if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
2019-07-29 19:35:48 -04:00
}
mesh.SendCommand(msg);
}
2021-01-09 17:31:09 -05:00
// Import libraries
db = require('SimpleDataStore').Shared();
sha = require('SHA256Stream');
mesh = require('MeshAgent');
childProcess = require('child_process');
2021-01-15 02:57:52 -05:00
if (mesh.hasKVM == 1)
{ // if the agent is compiled with KVM support
2021-01-09 17:31:09 -05:00
// Check if this computer supports a desktop
try
{
2021-01-15 02:57:52 -05:00
if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
{
2021-01-09 17:31:09 -05:00
meshCoreObj.caps |= 1; meshCoreObjChanged();
2021-01-15 02:57:52 -05:00
} else if (process.platform == 'linux' || process.platform == 'freebsd')
{
2021-01-09 17:31:09 -05:00
require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); });
}
} catch (e) { }
2018-09-27 19:17:05 -04:00
}
2019-06-14 19:33:53 -04:00
mesh.DAIPC = obj.DAIPC;
2018-01-16 20:30:34 -05:00
/*
// Try to load up the network monitor
try {
networkMonitor = require('NetworkMonitor');
networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
} catch (e) { networkMonitor = null; }
2018-01-16 20:30:34 -05:00
*/
2018-01-09 23:13:41 -05:00
2018-09-27 19:17:05 -04:00
// Fetch the SMBios Tables
var SMBiosTables = null;
var SMBiosTablesRaw = null;
2021-01-15 02:57:52 -05:00
try
{
var SMBiosModule = null;
try { SMBiosModule = require('smbios'); } catch (e) { }
2021-01-15 02:57:52 -05:00
if (SMBiosModule != null)
{
SMBiosModule.get(function (data)
{
if (data != null)
{
SMBiosTablesRaw = data;
SMBiosTables = require('smbios').parse(data)
2020-01-07 16:04:36 -05:00
if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
2019-11-28 15:27:44 -05:00
// If SMBios tables say that Intel AMT is present, try to connect MEI
if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
{
var amtmodule = require('amt-manage');
amt = new amtmodule(mesh, db, false);
2020-11-02 03:44:07 -05:00
amt.on('portBinding_LMS', function (map) { mesh.SendCommand({ action: 'lmsinfo', value: { ports: map.keys() } }); });
amt.on('stateChange_LMS', function (v) { if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; } meshCoreObj.intelamt.microlms = v; meshCoreObjChanged(); }); // 0 = Disabled, 1 = Connecting, 2 = Connected
amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } } // MEI State
amt.reset();
}
}
});
}
} catch (e) { sendConsoleText("ex1: " + e); }
2019-10-10 14:13:25 -04:00
// Try to load up the WIFI scanner
2021-01-15 02:57:52 -05:00
try
{
2018-01-30 21:23:57 -05:00
var wifiScannerLib = require('wifi-scanner');
wifiScanner = new wifiScannerLib();
wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
} catch (e) { wifiScannerLib = null; wifiScanner = null; }
2018-12-28 00:31:20 -05:00
// Get our location (lat/long) using our public IP address
2017-09-01 14:23:22 -04:00
var getIpLocationDataExInProgress = false;
2017-10-23 17:09:58 -04:00
var getIpLocationDataExCounts = [0, 0];
2021-01-15 02:57:52 -05:00
function getIpLocationDataEx(func)
{
2017-09-01 14:23:22 -04:00
if (getIpLocationDataExInProgress == true) { return false; }
2021-01-15 02:57:52 -05:00
try
{
2017-09-01 14:23:22 -04:00
getIpLocationDataExInProgress = true;
getIpLocationDataExCounts[0]++;
var options = http.parseUri("http://ipinfo.io/json");
options.method = 'GET';
2021-01-15 02:57:52 -05:00
http.request(options, function (resp)
{
if (resp.statusCode == 200)
{
2018-01-04 15:15:21 -05:00
var geoData = '';
resp.data = function (geoipdata) { geoData += geoipdata; };
2021-01-15 02:57:52 -05:00
resp.end = function ()
{
2018-01-04 15:15:21 -05:00
var location = null;
2021-01-15 02:57:52 -05:00
try
{
if (typeof geoData == 'string')
{
2018-01-04 15:15:21 -05:00
var result = JSON.parse(geoData);
if (result.ip && result.loc) { location = result; }
}
} catch (e) { }
if (func) { getIpLocationDataExCounts[1]++; func(location); }
}
2021-01-15 02:57:52 -05:00
} else
{ func(null); }
2018-01-04 15:15:21 -05:00
getIpLocationDataExInProgress = false;
}).end();
2017-09-01 14:23:22 -04:00
return true;
}
2017-09-01 14:23:22 -04:00
catch (e) { return false; }
}
2019-10-10 14:13:25 -04:00
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
2021-01-15 02:57:52 -05:00
function clearGatewayMac(str)
{
if (str == null) return null;
var x = JSON.parse(str);
for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
return JSON.stringify(x);
}
2019-10-10 14:13:25 -04:00
2021-01-15 02:57:52 -05:00
function getIpLocationData(func)
{
// Get the location information for the cache if possible
var publicLocationInfo = db.Get('publicLocationInfo');
if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
2021-01-15 02:57:52 -05:00
if (publicLocationInfo == null)
{
// Nothing in the cache, fetch the data
2021-01-15 02:57:52 -05:00
getIpLocationDataEx(function (locationData)
{
if (locationData != null)
{
2017-09-01 20:34:02 -04:00
publicLocationInfo = {};
publicLocationInfo.netInfoStr = lastNetworkInfo;
publicLocationInfo.locationData = locationData;
var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
if (func) func(locationData); // Report the new location
2021-01-15 02:57:52 -05:00
} else
{
2017-09-01 20:34:02 -04:00
if (func) func(null); // Report no location
}
});
2021-01-15 02:57:52 -05:00
} else
{
// Check the cache
2021-01-15 02:57:52 -05:00
if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo))
{
// Cache match
if (func) func(publicLocationInfo.locationData);
2021-01-15 02:57:52 -05:00
} else
{
// Cache mismatch
2021-01-15 02:57:52 -05:00
getIpLocationDataEx(function (locationData)
{
if (locationData != null)
{
2017-09-01 20:34:02 -04:00
publicLocationInfo = {};
publicLocationInfo.netInfoStr = lastNetworkInfo;
publicLocationInfo.locationData = locationData;
var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
if (func) func(locationData); // Report the new location
2021-01-15 02:57:52 -05:00
} else
{
2017-09-01 20:34:02 -04:00
if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
}
});
}
}
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Polyfill String.endsWith
2021-01-15 02:57:52 -05:00
if (!String.prototype.endsWith)
{
String.prototype.endsWith = function (searchString, position)
{
2017-08-28 12:27:45 -04:00
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Polyfill path.join
obj.path = {
2021-01-15 02:57:52 -05:00
join: function ()
{
2017-08-28 12:27:45 -04:00
var x = [];
2021-01-15 02:57:52 -05:00
for (var i in arguments)
{
2017-08-28 12:27:45 -04:00
var w = arguments[i];
2021-01-15 02:57:52 -05:00
if (w != null)
{
2017-08-28 12:27:45 -04:00
while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
2021-01-15 02:57:52 -05:00
if (i != 0)
{
2017-10-23 17:09:58 -04:00
while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
}
2017-08-28 12:27:45 -04:00
x.push(w);
}
}
if (x.length == 0) return '/';
return x.join('/');
}
};
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Replace a string with a number if the string is an exact number
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert decimal to hex
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a raw string to a hex string
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a buffer into a string
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
2021-01-15 02:57:52 -05:00
function hex2rstr(d)
{
2017-08-28 12:27:45 -04:00
if (typeof d != "string" || d.length == 0) return '';
var r = '', m = ('' + d).match(/../g), t;
while (t = m.shift()) r += String.fromCharCode('0x' + t);
return r
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Convert an object to string with all functions
2021-01-15 02:57:52 -05:00
function objToString(x, p, pad, ret)
{
2017-08-28 12:27:45 -04:00
if (ret == undefined) ret = '';
if (p == undefined) p = 0;
if (x == null) { return '[null]'; }
if (p > 8) { return '[...]'; }
if (x == undefined) { return '[undefined]'; }
if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
if (typeof x == 'buffer') { return '[buffer]'; }
if (typeof x != 'object') { return x; }
var r = '{' + (ret ? '\r\n' : ' ');
for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
return r + addPad(p, pad) + '}';
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Return p number of spaces
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Split a string taking into account the quoats. Used for command line parsing
2021-01-15 02:57:52 -05:00
function splitArgs(str)
{
2017-08-28 12:27:45 -04:00
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
return myArray;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Parse arguments string array into an object
2021-01-15 02:57:52 -05:00
function parseArgs(argv)
{
2017-08-28 12:27:45 -04:00
var results = { '_': [] }, current = null;
2021-01-15 02:57:52 -05:00
for (var i = 1, len = argv.length; i < len; i++)
{
2017-08-28 12:27:45 -04:00
var x = argv[i];
2021-01-15 02:57:52 -05:00
if (x.length > 2 && x[0] == '-' && x[1] == '-')
{
2017-08-28 12:27:45 -04:00
if (current != null) { results[current] = true; }
current = x.substring(2);
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
}
}
if (current != null) { results[current] = true; }
return results;
}
2019-10-10 14:13:25 -04:00
// Get server target url with a custom path
2021-01-15 02:57:52 -05:00
function getServerTargetUrl(path)
{
var x = mesh.ServerUrl;
//sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
if (x == null) { return null; }
if (path == null) { path = ''; }
x = http.parseUri(x);
if (x == null) return null;
return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
}
2019-10-10 14:13:25 -04:00
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
2021-01-15 02:57:52 -05:00
function getServerTargetUrlEx(url)
{
if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
return url;
}
2019-10-10 14:13:25 -04:00
// Send a wake-on-lan packet
2021-01-15 02:57:52 -05:00
function sendWakeOnLan(hexMac)
{
hexMac = hexMac.split(':').join('');
var count = 0;
2021-01-15 02:57:52 -05:00
try
{
var interfaces = require('os').networkInterfaces();
var magic = 'FFFFFFFFFFFF';
for (var x = 1; x <= 16; ++x) { magic += hexMac; }
var magicbin = Buffer.from(magic, 'hex');
2019-10-10 14:13:25 -04:00
2021-01-15 02:57:52 -05:00
for (var adapter in interfaces)
{
if (interfaces.hasOwnProperty(adapter))
{
for (var i = 0; i < interfaces[adapter].length; ++i)
{
var addr = interfaces[adapter][i];
2021-01-15 02:57:52 -05:00
if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00'))
{
try
{
2020-06-25 22:14:29 -04:00
var socket = require('dgram').createSocket({ type: 'udp4' });
socket.bind({ address: addr.address });
socket.setBroadcast(true);
socket.setMulticastInterface(addr.address);
socket.setMulticastTTL(1);
2020-06-25 22:14:29 -04:00
socket.send(magicbin, 7, '255.255.255.255');
socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
count++;
}
catch (e) { }
}
}
}
}
} catch (e) { }
return count;
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Handle a mesh agent command
2021-01-15 02:57:52 -05:00
function handleServerCommand(data)
{
if (typeof data == 'object')
{
2017-08-28 12:27:45 -04:00
// If this is a console command, parse it and call the console handler
2021-01-15 02:57:52 -05:00
switch (data.action)
{
case 'agentupdate':
agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash });
break;
case 'msg': {
2021-01-15 02:57:52 -05:00
switch (data.type)
{
2018-04-11 16:49:05 -04:00
case 'console': { // Process a console command
2021-01-15 02:57:52 -05:00
if (data.value && data.sessionid)
{
MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
2018-04-11 16:49:05 -04:00
var args = splitArgs(data.value);
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
}
break;
}
2018-04-11 16:49:05 -04:00
case 'tunnel': {
2021-01-15 02:57:52 -05:00
if (data.value != null)
{ // Process a new tunnel connection request
2018-04-11 16:49:05 -04:00
// Create a new tunnel object
var xurl = getServerTargetUrlEx(data.value);
2021-01-15 02:57:52 -05:00
if (xurl != null)
{
2020-06-17 13:28:54 -04:00
xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
2018-04-11 16:49:05 -04:00
var woptions = http.parseUri(xurl);
woptions.perMessageDeflate = false;
if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
2020-11-08 16:29:37 -05:00
// Perform manual server TLS certificate checking based on the certificate hash given by the server.
2018-04-11 16:49:05 -04:00
woptions.rejectUnauthorized = 0;
2021-01-15 02:57:52 -05:00
woptions.checkServerIdentity = function checkServerIdentity(certs)
{
2020-11-16 18:14:30 -05:00
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
// Check that the certificate is the one expected by the server, fail if not.
if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
}
2020-11-08 16:29:37 -05:00
woptions.checkServerIdentity.servertlshash = data.servertlshash;
2018-12-01 00:23:10 -05:00
//sendConsoleText(JSON.stringify(woptions));
//sendConsoleText('TUNNEL: ' + JSON.stringify(data));
2018-04-11 16:49:05 -04:00
var tunnel = http.request(woptions);
tunnel.upgrade = onTunnelUpgrade;
tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
2018-04-11 16:49:05 -04:00
tunnel.sessionid = data.sessionid;
tunnel.rights = data.rights;
2019-04-12 17:19:03 -04:00
tunnel.consent = data.consent;
2020-01-07 16:04:36 -05:00
tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
2020-08-07 18:01:28 -04:00
tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
2019-07-29 19:35:48 -04:00
tunnel.userid = data.userid;
tunnel.remoteaddr = data.remoteaddr;
2018-04-11 16:49:05 -04:00
tunnel.state = 0;
tunnel.url = xurl;
tunnel.protocol = 0;
tunnel.soptions = data.soptions;
2018-04-11 16:49:05 -04:00
tunnel.tcpaddr = data.tcpaddr;
tunnel.tcpport = data.tcpport;
2019-05-05 01:53:57 -04:00
tunnel.udpaddr = data.udpaddr;
tunnel.udpport = data.udpport;
2018-04-11 16:49:05 -04:00
tunnel.end();
// Put the tunnel in the tunnels list
var index = nextTunnelIndex++;
2018-04-11 16:49:05 -04:00
tunnel.index = index;
tunnels[index] = tunnel;
//sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
2018-04-11 16:49:05 -04:00
}
}
break;
}
case 'messagebox': {
// Display a message box
2021-01-15 02:57:52 -05:00
if (data.title && data.msg)
{
MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
}
break;
}
2018-04-11 16:49:05 -04:00
case 'ps': {
// Return the list of running processes
2021-01-15 02:57:52 -05:00
if (data.sessionid)
{
processManager.getProcesses(function (plist)
{
2020-01-07 16:04:36 -05:00
mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
});
2018-04-11 16:49:05 -04:00
}
break;
}
case 'pskill': {
// Kill a process
2021-01-15 02:57:52 -05:00
if (data.value)
{
MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
}
break;
}
case 'services': {
// Return the list of installed services
var services = null;
try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
2020-01-07 16:04:36 -05:00
if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
break;
}
case 'serviceStop': {
// Stop a service
2021-01-15 02:57:52 -05:00
try
{
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.stop(); }
} catch (e) { }
break;
}
case 'serviceStart': {
// Start a service
2021-01-15 02:57:52 -05:00
try
{
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.start(); }
} catch (e) { }
break;
}
case 'serviceRestart': {
// Restart a service
2021-01-15 02:57:52 -05:00
try
{
var service = require('service-manager').manager.getService(data.serviceName);
if (service != null) { service.restart(); }
} catch (e) { }
break;
}
case 'deskBackground':
{
2020-01-07 16:04:36 -05:00
// Toggle desktop background
2021-01-15 02:57:52 -05:00
try
{
if (process.platform == 'win32')
{
2020-01-07 16:04:36 -05:00
var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
var sid = undefined;
2021-01-15 02:57:52 -05:00
if (stype == 1)
{
if (require('MeshAgent')._tsid != null)
{
2020-01-07 16:04:36 -05:00
stype = 5;
sid = require('MeshAgent')._tsid;
}
}
2020-01-07 16:04:36 -05:00
var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
var current = child.stdout.str.trim();
if (current != '') { require('MeshAgent')._wallpaper = current; }
child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
2021-01-15 02:57:52 -05:00
} else
{
2020-01-07 16:04:36 -05:00
var id = require('user-sessions').consoleUid();
var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
}
2021-01-15 02:57:52 -05:00
} catch (e)
{
2020-01-07 16:04:36 -05:00
sendConsoleText(e);
2019-11-28 15:27:44 -05:00
}
2020-01-07 16:04:36 -05:00
break;
}
case 'openUrl': {
// Open a local web browser and return success/fail
MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
2020-01-07 16:04:36 -05:00
sendConsoleText("OpenURL: " + data.url);
if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
2018-04-11 16:49:05 -04:00
break;
}
case 'getclip': {
// Send the load clipboard back to the user
//sendConsoleText('getClip: ' + JSON.stringify(data));
2021-01-15 02:57:52 -05:00
if (require('MeshAgent').isService)
{
require('clipboard').dispatchRead().then(function (str)
{
if (str)
{
MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
2019-07-29 19:35:48 -04:00
}
});
2021-01-15 02:57:52 -05:00
} else
{
require("clipboard").read().then(function (str)
{
if (str)
{
MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
2019-07-29 19:35:48 -04:00
}
});
}
break;
}
case 'setclip': {
// Set the load clipboard to a user value
//sendConsoleText('setClip: ' + JSON.stringify(data));
2021-01-15 02:57:52 -05:00
if (typeof data.data == 'string')
{
MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
2020-01-07 16:04:36 -05:00
mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
2019-07-29 19:35:48 -04:00
}
break;
}
case 'userSessions': {
// Send back current user sessions list, this is Windows only.
//sendConsoleText('userSessions: ' + JSON.stringify(data));
if (process.platform != 'win32') break;
var p = require('user-sessions').enumerateUsers();
p.sessionid = data.sessionid;
p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
break;
}
2020-11-16 02:52:42 -05:00
case 'cpuinfo':
// CPU & memory utilization
var cpuuse = require('sysinfo').cpuUtilization();
cpuuse.sessionid = data.sessionid;
cpuuse.tag = data.tag;
cpuuse.then(function (data) { mesh.SendCommand(JSON.stringify({ action: 'msg', type: 'cpuinfo', cpu: data, memory: require('sysinfo').memUtilization(), sessionid: this.sessionid, tag: this.tag })); }, function (ex) { });
break;
2020-11-28 00:40:30 -05:00
case 'localapp':
// Send a message to a local application
sendConsoleText('localappMsg: ' + data.appid + ', ' + JSON.stringify(data.value));
if (data.appid != null) { sendToRegisteredApp(data.appid, data.value); } else { broadcastToRegisteredApps(data.value); }
break;
2019-01-04 20:59:13 -05:00
default:
// Unknown action, ignore it.
break;
2017-08-28 12:27:45 -04:00
}
2018-04-12 14:15:01 -04:00
break;
}
case 'acmactivate': {
2021-01-15 02:57:52 -05:00
if (amt != null)
{
MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
2019-07-29 19:35:48 -04:00
amt.setAcmResponse(data);
}
break;
}
case 'wakeonlan': {
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
//sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
break;
}
2020-07-11 02:12:43 -04:00
case 'runcommands': {
if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
// data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
var options = {};
2021-01-15 02:57:52 -05:00
if (data.runAsUser > 0)
{
try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
options.type = require('child_process').SpawnTypes.TERM;
}
2021-01-15 02:57:52 -05:00
if (data.runAsUser == 2)
{
if (options.uid == null) break;
if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
}
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
if (data.type == 1)
{
2020-07-11 02:12:43 -04:00
// Windows command shell
mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
2020-07-11 02:12:43 -04:00
mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
2021-01-15 02:57:52 -05:00
} else if (data.type == 2)
{
2020-07-11 02:12:43 -04:00
// Windows Powershell
mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
2020-07-11 02:12:43 -04:00
mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
}
2021-01-15 02:57:52 -05:00
} else if (data.type == 3)
{
2020-07-11 02:12:43 -04:00
// Linux shell
mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
2020-07-11 02:12:43 -04:00
mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
}
break;
}
2019-10-24 16:13:18 -04:00
case 'uninstallagent':
// Uninstall this agent
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
try
{
agentName = require('MeshAgent').serviceName;
}
catch (x)
{
}
2021-01-15 02:57:52 -05:00
if (require('service-manager').manager.getService(agentName).isMe())
{
try { diagnosticAgent_uninstall(); } catch (e) { }
2019-10-24 16:13:18 -04:00
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
}
break;
2017-09-01 14:23:22 -04:00
case 'poweraction': {
// Server telling us to execute a power action
2021-01-15 02:57:52 -05:00
if ((mesh.ExecPowerState != undefined) && (data.actiontype))
{
2017-09-01 14:23:22 -04:00
var forced = 0;
if (data.forced == 1) { forced = 1; }
data.actiontype = parseInt(data.actiontype);
MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
2020-01-07 16:04:36 -05:00
sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
2017-09-01 14:23:22 -04:00
var r = mesh.ExecPowerState(data.actiontype, forced);
2020-01-07 16:04:36 -05:00
sendConsoleText("ExecPowerState returned code: " + r);
2017-09-01 14:23:22 -04:00
}
break;
}
2017-09-05 20:19:28 -04:00
case 'iplocation': {
// Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
2020-01-07 16:04:36 -05:00
getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
break;
2017-08-28 12:27:45 -04:00
}
2018-04-19 21:19:15 -04:00
case 'toast': {
// Display a toast message
2021-01-15 02:57:52 -05:00
if (data.title && data.msg)
{
MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
}
2018-04-19 21:19:15 -04:00
break;
}
case 'openUrl': {
// Open a local web browser and return success/fail
2019-07-29 19:35:48 -04:00
//sendConsoleText('OpenURL: ' + data.url);
MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
2020-01-07 16:04:36 -05:00
if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
break;
}
case 'amtconfig': {
// Perform Intel AMT activation and/or configuration
if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break;
2021-01-15 02:57:52 -05:00
amt.getMeiState(15, function (state)
{
if ((apftunnel != null) || (amt == null)) return;
if ((state == null) || (state.ProvisioningState == null)) return;
if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
var apfarg = {
mpsurl: mesh.ServerUrl.replace('/agent.ashx', '/apf.ashx'),
mpsuser: data.user, // Agent user name
mpspass: data.pass, // Encrypted login cookie
mpskeepalive: 60000,
clientname: state.OsHostname,
clientaddress: '127.0.0.1',
clientuuid: state.UUID,
conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
meiState: state // MEI state will be passed to MPS server
};
2020-10-26 15:46:30 -04:00
addAmtEvent('LMS tunnel start.');
apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
2021-01-15 02:57:52 -05:00
apftunnel.onJsonControl = function (data)
{
2020-10-26 15:46:30 -04:00
if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log
if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
2021-01-15 02:57:52 -05:00
if (data.action == 'deactivate')
{ // Request CCM deactivation
var amtMeiModule, amtMei;
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); });
amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
}
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
}
2020-10-26 15:46:30 -04:00
apftunnel.onChannelClosed = function () { addAmtEvent('LMS tunnel closed.'); apftunnel = null; }
try { apftunnel.connect(); } catch (ex) { }
});
break;
}
case 'getScript': {
// Received a configuration script from the server
sendConsoleText('getScript: ' + JSON.stringify(data));
break;
}
case 'sysinfo': {
// Fetch system information
2021-01-15 02:57:52 -05:00
getSystemInformation(function (results)
{
2020-01-07 16:04:36 -05:00
if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
2019-09-23 19:46:26 -04:00
});
break;
}
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
case 'pong': { break; }
2019-10-10 14:13:25 -04:00
case 'plugin': {
try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
2019-10-10 14:13:25 -04:00
break;
}
case 'coredump':
// Set the current agent coredump situation.
2021-01-15 02:57:52 -05:00
if (data.value === true)
{
if (process.platform == 'win32')
{
2020-10-27 19:40:12 -04:00
// TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
process.coreDumpLocation = process.execPath.replace('.exe', '.dmp');
2021-01-15 02:57:52 -05:00
} else
{
2020-10-27 19:40:12 -04:00
process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null;
}
2021-01-15 02:57:52 -05:00
} else if (data.value === false)
{
process.coreDumpLocation = null;
}
break;
2020-07-06 20:16:54 -04:00
case 'getcoredump':
// Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
2020-07-06 20:16:54 -04:00
var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
var coreDumpPath = null;
if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
2021-01-15 02:57:52 -05:00
if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath)))
{
try
{
2020-10-27 19:40:12 -04:00
var coredate = fs.statSync(coreDumpPath).mtime;
var coretime = new Date(coredate).getTime();
var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime();
if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); }
} catch (ex) { }
}
2021-01-15 02:57:52 -05:00
if (r.exists == true)
{
2020-10-27 19:40:12 -04:00
r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent
r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file
}
2020-07-06 20:16:54 -04:00
mesh.SendCommand(JSON.stringify(r));
break;
case 'meshToolInfo':
if (data.pipe == true) { delete data.pipe; delete data.action; data.cmd = 'meshToolInfo'; broadcastToRegisteredApps(data); }
break;
2020-11-08 04:13:23 -05:00
case 'wget': // Server uses this command to tell the agent to download a file using HTTPS/GET and place it in a given path. This is used for one-to-many file uploads.
2020-11-09 19:51:48 -05:00
agentFileHttpPendingRequests.push(data);
serverFetchFile();
2020-11-08 04:13:23 -05:00
break;
2019-01-04 20:59:13 -05:00
default:
// Unknown action, ignore it.
break;
2017-08-28 12:27:45 -04:00
}
}
}
2019-10-10 14:13:25 -04:00
2020-11-09 19:51:48 -05:00
// Agent just get a file from the server and save it locally.
2021-01-15 02:57:52 -05:00
function serverFetchFile()
{
2020-11-09 19:51:48 -05:00
if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server.
var data = agentFileHttpPendingRequests.shift();
if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file.
if (data.createFolder) { try { fs.mkdirSync(data.folder); } catch (ex) { } } // If requested, create the local folder.
data.url = 'http' + getServerTargetUrlEx('*/').substring(2);
var agentFileHttpOptions = http.parseUri(data.url);
agentFileHttpOptions.path = data.urlpath;
// Perform manual server TLS certificate checking based on the certificate hash given by the server.
agentFileHttpOptions.rejectUnauthorized = 0;
2021-01-15 02:57:52 -05:00
agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs)
{
2020-11-16 18:14:30 -05:00
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
// Check that the certificate is the one expected by the server, fail if not.
if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase())) { throw new Error('BadCert') }
}
2020-11-09 19:51:48 -05:00
agentFileHttpOptions.checkServerIdentity.servertlshash = data.servertlshash;
if (agentFileHttpOptions == null) return;
var agentFileHttpRequest = http.request(agentFileHttpOptions,
2021-01-15 02:57:52 -05:00
function (response)
{
2020-11-09 19:51:48 -05:00
response.xparent = this;
2021-01-15 02:57:52 -05:00
try
{
2020-11-09 19:51:48 -05:00
response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' })
response.pipe(response.xfile);
response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); }
} catch (ex) { delete agentFileHttpRequests[this.xurlpath]; delete response.xparent; serverFetchFile(); return; }
}
);
agentFileHttpRequest.on('error', function (ex) { delete agentFileHttpRequests[this.xurlpath]; serverFetchFile(); });
agentFileHttpRequest.end();
agentFileHttpRequest.xurlpath = data.urlpath;
agentFileHttpRequest.xpath = data.path;
agentFileHttpRequests[data.urlpath] = agentFileHttpRequest;
}
2017-08-28 12:27:45 -04:00
// Called when a file changed in the file system
2017-12-14 17:57:52 -05:00
/*
2017-08-28 12:27:45 -04:00
function onFileWatcher(a, b) {
2017-10-23 17:09:58 -04:00
console.log('onFileWatcher', a, b, this.path);
2017-08-28 12:27:45 -04:00
var response = getDirectoryInfo(this.path);
if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
}
2017-12-14 17:57:52 -05:00
*/
2017-08-28 12:27:45 -04:00
2021-01-15 02:57:52 -05:00
function getSystemInformation(func)
{
try
{
2019-09-23 19:46:26 -04:00
var results = { hardware: require('identifiers').get() }; // Hardware info
2021-01-15 02:57:52 -05:00
if (results.hardware && results.hardware.windows)
{
2019-09-23 19:46:26 -04:00
// Remove extra entries and things that change quickly
var x = results.hardware.windows.osinfo;
try { delete x.FreePhysicalMemory; } catch (e) { }
try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
try { delete x.FreeVirtualMemory; } catch (e) { }
try { delete x.LocalDateTime; } catch (e) { }
try { delete x.MaxProcessMemorySize; } catch (e) { }
try { delete x.TotalVirtualMemorySize; } catch (e) { }
try { delete x.TotalVisibleMemorySize; } catch (e) { }
2021-01-15 02:57:52 -05:00
try
{
2019-09-23 19:46:26 -04:00
if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
} catch (e) { }
2019-09-23 19:46:26 -04:00
}
if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
/*
if (process.platform == 'win32') {
var defragResult = function (r) {
if (typeof r == 'object') { results[this.callname] = r; }
if (this.callname == 'defrag') {
var pr = require('win-info').installedApps(); // Installed apps
pr.callname = 'installedApps';
pr.sessionid = data.sessionid;
pr.then(defragResult, defragResult);
}
else {
results.winpatches = require('win-info').qfe(); // Windows patches
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
func(results);
}
}
var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
pr.callname = 'defrag';
pr.sessionid = data.sessionid;
pr.then(defragResult, defragResult);
} else {
*/
2019-10-10 14:13:25 -04:00
results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
func(results);
2019-09-23 19:46:26 -04:00
//}
} catch (e) { func(null, e); }
2019-09-23 19:46:26 -04:00
}
2017-08-28 12:27:45 -04:00
// Get a formated response for a given directory path
2021-01-15 02:57:52 -05:00
function getDirectoryInfo(reqpath)
{
2017-08-28 12:27:45 -04:00
var response = { path: reqpath, dir: [] };
2021-01-15 02:57:52 -05:00
if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32'))
{
2017-08-28 12:27:45 -04:00
// List all the drives in the root, or the root itself
var results = null;
try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
2021-01-15 02:57:52 -05:00
if (results != null)
{
for (var i = 0; i < results.length; ++i)
{
2017-08-28 12:27:45 -04:00
var drive = { n: results[i].name, t: 1 };
if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
response.dir.push(drive);
}
}
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
// List all the files and folders in this path
if (reqpath == '') { reqpath = '/'; }
2018-07-06 13:13:19 -04:00
var results = null, xpath = obj.path.join(reqpath, '*');
2018-07-26 19:31:43 -04:00
//if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
2017-08-28 12:27:45 -04:00
try { results = fs.readdirSync(xpath); } catch (e) { }
2021-01-15 02:57:52 -05:00
if (results != null)
{
for (var i = 0; i < results.length; ++i)
{
if ((results[i] != '.') && (results[i] != '..'))
{
2017-08-28 12:27:45 -04:00
var stat = null, p = obj.path.join(reqpath, results[i]);
2018-07-26 19:31:43 -04:00
//if (process.platform == "win32") { p = p.split('/').join('\\'); }
2017-08-28 12:27:45 -04:00
try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
2021-01-15 02:57:52 -05:00
if ((stat != null) && (stat != undefined))
{
if (stat.isDirectory() == true)
{
2017-08-28 12:27:45 -04:00
response.dir.push({ n: results[i], t: 2, d: stat.mtime });
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
}
}
}
}
}
}
return response;
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Tunnel callback operations
2021-01-15 02:57:52 -05:00
function onTunnelUpgrade(response, s, head)
{
2017-10-23 17:09:58 -04:00
this.s = s;
s.httprequest = this;
s.end = onTunnelClosed;
2018-01-18 18:43:43 -05:00
s.tunnel = this;
2020-05-21 16:22:58 -04:00
s.descriptorMetadata = "MeshAgent_relayTunnel";
2019-01-04 17:35:01 -05:00
if (require('MeshAgent').idleTimeout != null)
{
s.setTimeout(require('MeshAgent').idleTimeout * 1000);
s.on('timeout', function ()
{
this.ping();
this.setTimeout(require('MeshAgent').idleTimeout * 1000);
});
}
//sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
2019-01-04 17:35:01 -05:00
2021-01-15 02:57:52 -05:00
if (this.tcpport != null)
{
2017-10-23 17:09:58 -04:00
// This is a TCP relay connection, pause now and try to connect to the target.
s.pause();
s.data = onTcpRelayServerTunnelData;
var connectionOptions = { port: parseInt(this.tcpport) };
if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
s.tcprelay.peerindex = this.index;
2020-07-24 17:00:49 -04:00
// Add the TCP session to the count and update the server
2021-01-15 02:57:52 -05:00
if (s.httprequest.userid != null)
{
2020-07-24 17:00:49 -04:00
if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-07-24 17:00:49 -04:00
}
2021-01-15 02:57:52 -05:00
} if (this.udpport != null)
{
2019-05-05 01:53:57 -04:00
// This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
s.data = onUdpRelayServerTunnelData;
s.udprelay = require('dgram').createSocket({ type: 'udp4' });
2019-05-05 01:53:57 -04:00
s.udprelay.bind({ port: 0 });
s.udprelay.peerindex = this.index;
s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
s.udprelay.udpport = this.udpport;
s.udprelay.udpaddr = this.udpaddr;
s.udprelay.first = true;
2020-07-24 17:00:49 -04:00
// Add the UDP session to the count and update the server
2021-01-15 02:57:52 -05:00
if (s.httprequest.userid != null)
{
2020-07-24 17:00:49 -04:00
if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-07-24 17:00:49 -04:00
}
2021-01-15 02:57:52 -05:00
} else
{
2017-10-23 17:09:58 -04:00
// This is a normal connect for KVM/Terminal/Files
s.data = onTunnelData;
}
}
2019-05-05 01:53:57 -04:00
// Called when UDP relay data is received // TODO****
2021-01-15 02:57:52 -05:00
function onUdpRelayTargetTunnelConnect(data)
{
2019-05-05 01:53:57 -04:00
var peerTunnel = tunnels[this.peerindex];
peerTunnel.s.write(data);
2019-05-05 01:53:57 -04:00
}
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
2021-01-15 02:57:52 -05:00
function onUdpRelayServerTunnelData(data)
{
if (this.udprelay.first === true)
{
delete this.udprelay.first; // Skip the first 'c' that is received.
2021-01-15 02:57:52 -05:00
} else
{
this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
}
2019-05-05 01:53:57 -04:00
}
2017-10-23 17:09:58 -04:00
// Called when the TCP relay target is connected
2021-01-15 02:57:52 -05:00
function onTcpRelayTargetTunnelConnect()
{
2017-10-23 17:09:58 -04:00
var peerTunnel = tunnels[this.peerindex];
this.pipe(peerTunnel.s); // Pipe Target --> Server
peerTunnel.s.first = true;
peerTunnel.s.resume();
}
2019-10-10 14:13:25 -04:00
2017-10-23 17:09:58 -04:00
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
2021-01-15 02:57:52 -05:00
function onTcpRelayServerTunnelData(data)
{
if (this.first == true)
{
this.first = false;
this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
}
2017-10-23 17:09:58 -04:00
}
2019-05-05 01:53:57 -04:00
2021-01-15 02:57:52 -05:00
function onTunnelClosed()
{
var tunnel = tunnels[this.httprequest.index];
if (tunnel == null) return; // Stop duplicate calls.
2020-07-24 17:00:49 -04:00
// If this is a routing session, clean up and send the new session counts.
2021-01-15 02:57:52 -05:00
if (this.httprequest.userid != null)
{
if (this.httprequest.tcpport != null)
{
2020-07-24 17:00:49 -04:00
if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.udpport != null)
{
2020-07-24 17:00:49 -04:00
if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-07-24 17:00:49 -04:00
}
}
// Sent tunnel statistics to the server, only send this if compression was used.
2021-01-15 02:57:52 -05:00
if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString()))
{
mesh.SendCommand({
action: 'tunnelCloseStats',
url: tunnel.url,
userid: tunnel.userid,
protocol: tunnel.protocol,
sessionid: tunnel.sessionid,
sent: this.bytesSent_uncompressed.toString(),
sentActual: this.bytesSent_actual.toString(),
sentRatio: this.bytesSent_ratio,
received: this.bytesReceived_uncompressed.toString(),
receivedActual: this.bytesReceived_actual.toString(),
receivedRatio: this.bytesReceived_ratio
});
}
2020-07-17 02:08:59 -04:00
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
2019-10-10 14:13:25 -04:00
2017-12-14 17:57:52 -05:00
/*
2017-08-28 12:27:45 -04:00
// Close the watcher if required
if (this.httprequest.watcher != undefined) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2017-10-23 17:09:58 -04:00
delete this.httprequest.watcher;
2017-08-28 12:27:45 -04:00
}
2017-12-14 17:57:52 -05:00
*/
2017-08-28 12:27:45 -04:00
// If there is a upload or download active on this connection, close the file
2020-08-13 15:29:18 -04:00
if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
2018-01-18 18:43:43 -05:00
// Clean up WebRTC
2021-01-15 02:57:52 -05:00
if (this.webrtc != null)
{
2018-01-18 18:43:43 -05:00
if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
if (this.webrtc.websocket) { delete this.webrtc.websocket; }
try { this.webrtc.close(); } catch (e) { }
this.webrtc.removeAllListeners('connected');
this.webrtc.removeAllListeners('disconnected');
this.webrtc.removeAllListeners('dataChannel');
delete this.webrtc;
}
// Clean up WebSocket
this.removeAllListeners('data');
2017-08-28 12:27:45 -04:00
}
function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
2021-01-15 02:57:52 -05:00
function onTunnelData(data)
{
2017-12-19 11:50:19 -05:00
//console.log("OnTunnelData");
2018-07-06 13:13:19 -04:00
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// If this is upload data, save it to file
2021-01-15 02:57:52 -05:00
if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123))
{
2020-08-13 15:29:18 -04:00
// Save the data to file being uploaded.
2021-01-15 02:57:52 -05:00
if (data[0] == 0)
{
2020-08-13 15:29:18 -04:00
// If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
2021-01-15 02:57:52 -05:00
} else
{
2020-08-13 15:29:18 -04:00
// If data does not start with zero, save as-is.
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
}
2020-08-13 15:29:18 -04:00
this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
2017-08-28 12:27:45 -04:00
return;
}
2021-01-15 02:57:52 -05:00
if (this.httprequest.state == 0)
{
2017-08-28 12:27:45 -04:00
// Check if this is a relay connection
2019-08-13 14:49:05 -04:00
if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
}
else
{
2017-08-28 12:27:45 -04:00
// Handle tunnel data
2021-01-15 02:57:52 -05:00
if (this.httprequest.protocol == 0)
{ // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
// Take a look at the protocol
if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
2017-08-28 12:27:45 -04:00
this.httprequest.protocol = parseInt(data);
if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
2021-01-15 02:57:52 -05:00
if (this.httprequest.protocol == 10)
{
//
// Basic file transfer
//
var stats = null;
2020-08-28 21:29:32 -04:00
if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
2021-01-15 02:57:52 -05:00
if (this.httprequest.downloadFile)
{
//sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
this.write(JSON.stringify({ op: 'ok', size: stats.size }));
this.httprequest.downloadFile.pipe(this);
this.httprequest.downloadFile.end = function () { }
2021-01-15 02:57:52 -05:00
} else
{
//sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
this.write(JSON.stringify({ op: 'cancel' }));
}
}
else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
{
//
// Remote Terminal
//
2019-03-01 14:47:58 -05:00
// Check user access rights for terminal
2021-01-15 02:57:52 -05:00
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0)))
{
// Disengage this tunnel, user does not have the rights to do this!!
this.httprequest.protocol = 999999;
2019-03-01 14:47:58 -05:00
this.httprequest.s.end();
2020-01-07 16:04:36 -05:00
sendConsoleText("Error: No Terminal Control Rights.");
return;
}
2020-05-21 16:22:58 -04:00
this.descriptorMetadata = "Remote Terminal";
if (process.platform == 'win32')
{
if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
{
this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
this.httprequest.s.end();
return;
}
}
var prom = require('promise');
this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
this.httprequest.tpromise.that = this;
this.httprequest.tpromise.httprequest = this.httprequest;
this.end = function ()
{
if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
2020-05-12 03:01:42 -04:00
// Remove the terminal session to the count to update the server
if (this.httprequest.userid != null)
{
2020-05-12 03:01:42 -04:00
if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } }
try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-05-12 03:01:42 -04:00
}
if (process.platform == 'win32')
{
2019-06-14 19:33:53 -04:00
// Unpipe the web socket
this.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
2019-06-14 19:33:53 -04:00
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if (this.rtcchannel)
{
2019-06-14 19:33:53 -04:00
this.rtcchannel.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
2019-06-14 19:33:53 -04:00
}
2019-06-14 19:33:53 -04:00
// Clean up
if (this.httprequest._term) { this.httprequest._term.end(); }
2019-06-14 19:33:53 -04:00
this.httprequest._term = null;
}
};
2019-04-12 17:19:03 -04:00
// Perform User-Consent if needed.
if (this.httprequest.consent && (this.httprequest.consent & 16))
{
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
2021-01-15 02:57:52 -05:00
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
this.httprequest.tpromise._consent.then(
function ()
{
// Success
MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
this.retPromise._consent = null;
this.retPromise._res();
},
function (e)
{
// Denied
MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.retPromise._rej(e.toString());
});
}
else
{
// User-Consent is not required, so just resolve this promise
this.httprequest.tpromise._res();
}
this.httprequest.tpromise.then(
function ()
{
this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
this.httprequest.connectionPromise.ws = this.that;
// Start Terminal
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
try
{
var cols = 80, rows = 25;
if (this.httprequest.xoptions)
{
if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
}
if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
{
// Admin Terminal
if (require('win-virtual-terminal').supported)
{
2019-12-16 18:07:20 -05:00
// ConPTY PseudoTerminal
// this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
// The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
this.httprequest._dispatcher.httprequest = this.httprequest;
this.httprequest._dispatcher.on('connection', function (c)
{
if (this.httprequest.connectionPromise.completed)
{
c.end();
}
else
{
this.httprequest.connectionPromise._res(c);
}
});
}
else
{
2019-12-16 18:07:20 -05:00
// Legacy Terminal
this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
2019-12-16 18:07:20 -05:00
}
}
else
{
// Logged in user
var userPromise = require('user-sessions').enumerateUsers();
userPromise.that = this;
userPromise.then(function (u)
{
var that = this.that;
if (u.Active.length > 0)
{
var username = u.Active[0].Username;
if (require('win-virtual-terminal').supported)
{
// ConPTY PseudoTerminal
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
}
else
{
// Legacy Terminal
that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
}
that.httprequest._dispatcher.ws = that;
that.httprequest._dispatcher.on('connection', function (c)
{
if (this.ws.httprequest.connectionPromise.completed)
{
c.end();
}
else
{
this.ws.httprequest.connectionPromise._res(c);
}
});
}
2019-12-16 18:07:20 -05:00
});
}
}
catch (e)
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
}
}
else
{
try
{
var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
var env = { HISTCONTROL: 'ignoreboth' };
if (this.httprequest.xoptions)
{
if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
}
var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
{
if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
}
else if (bash)
{
var p = childProcess.execFile(bash, ['bash'], options); // Start bash
// Spaces at the beginning of lines are needed to hide commands from the command history
if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
this.httprequest.connectionPromise._res(p);
}
else if (sh)
{
var p = childProcess.execFile(sh, ['sh'], options); // Start sh
// Spaces at the beginning of lines are needed to hide commands from the command history
if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
this.httprequest.connectionPromise._res(p);
}
else
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
}
}
catch (e)
{
this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
}
2019-11-25 19:11:53 -05:00
}
this.httprequest.connectionPromise.then(
function (term)
{
// SUCCESS
var stdoutstream;
var stdinstream;
if (process.platform == 'win32')
{
this.ws.httprequest._term = term;
this.ws.httprequest._term.tunnel = this.ws;
stdoutstream = stdinstream = term;
}
else
{
2020-05-22 15:23:12 -04:00
term.descriptorMetadata = 'Remote Terminal';
this.ws.httprequest.process = term;
this.ws.httprequest.process.tunnel = this.ws;
term.stderr.stdout = term.stdout;
term.stderr.on('data', function (c) { this.stdout.write(c); });
stdoutstream = term.stdout;
stdinstream = term.stdin;
this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
term.prependListener('exit', function () { this.tunnel.end(); });
}
this.ws.removeAllListeners('data');
this.ws.on('data', onTunnelControlData);
2020-05-12 03:01:42 -04:00
stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2019-06-14 19:33:53 -04:00
// Add the terminal session to the count to update the server
if (this.ws.httprequest.userid != null)
{
if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
broadcastSessionsToRegisteredApps();
}
// Toast Notification, if required
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
{
// User Notifications is required
var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
}
},
function (e)
{
// FAILED to connect terminal
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.ws.end();
});
},
function (e)
{
// DO NOT start terminal
this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
this.that.end();
2021-01-15 02:57:52 -05:00
});
}
else if (this.httprequest.protocol == 2)
{
//
// Remote KVM
//
2019-06-14 19:33:53 -04:00
2019-03-01 14:47:58 -05:00
// Check user access rights for desktop
2021-01-15 02:57:52 -05:00
if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0)))
{
// Disengage this tunnel, user does not have the rights to do this!!
this.httprequest.protocol = 999999;
2019-03-01 14:47:58 -05:00
this.httprequest.s.end();
2020-01-07 16:04:36 -05:00
sendConsoleText("Error: No Desktop Control Rights.");
return;
}
2020-05-21 16:22:58 -04:00
this.descriptorMetadata = "Remote KVM";
// Look for a TSID
var tsid = null;
if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
2019-12-16 18:07:20 -05:00
require('MeshAgent')._tsid = tsid;
2018-01-16 20:30:34 -05:00
// Remote desktop using native pipes
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
2018-01-16 20:30:34 -05:00
this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
this.desktop = this.httprequest.desktop;
2018-04-19 21:19:15 -04:00
2020-05-07 02:23:27 -04:00
// Add ourself to the list of remote desktop sessions
if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
this.httprequest.desktop.kvm.tunnels.push(this);
// Send a metadata update to all desktop sessions
var users = {};
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.tunnels != null)
{
for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
tunnelUserCount.desktop = users;
try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-05-11 04:17:18 -04:00
}
2020-05-07 02:23:27 -04:00
2021-01-15 02:57:52 -05:00
this.end = function ()
{
2018-01-16 20:30:34 -05:00
--this.desktop.kvm.connectionCount;
2020-05-07 02:23:27 -04:00
// Remove ourself from the list of remote desktop session
var i = this.desktop.kvm.tunnels.indexOf(this);
if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
// Send a metadata update to all desktop sessions
var users = {};
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.tunnels != null)
{
for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
tunnelUserCount.desktop = users;
try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-05-11 04:17:18 -04:00
}
2020-05-07 02:23:27 -04:00
// Unpipe the web socket
try
{
this.unpipe(this.httprequest.desktop.kvm);
this.httprequest.desktop.kvm.unpipe(this);
}
2021-01-15 02:57:52 -05:00
catch (e) { }
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
if (this.rtcchannel)
{
try
{
this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
}
2021-01-15 02:57:52 -05:00
catch (e) { }
}
2019-11-29 01:45:21 -05:00
// Place wallpaper back if needed
// TODO
2021-01-15 02:57:52 -05:00
if (this.desktop.kvm.connectionCount == 0)
{
// Display a toast message. This may not be supported on all platforms.
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2019-10-10 14:13:25 -04:00
2018-04-19 21:19:15 -04:00
this.httprequest.desktop.kvm.end();
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.connectionBar)
{
this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.httprequest.desktop.kvm.connectionBar.close();
this.httprequest.desktop.kvm.connectionBar = null;
}
2021-01-15 02:57:52 -05:00
} else
{
for (var i in this.httprequest.desktop.kvm.users)
{
if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar)
{
for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
this.httprequest.desktop.kvm.users.splice(i, 1);
this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.httprequest.desktop.kvm.connectionBar.close();
this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2021-01-15 02:57:52 -05:00
this.httprequest.desktop.kvm.connectionBar.on('close', function ()
{
MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2021-01-15 02:57:52 -05:00
for (var i in this.httprequest.desktop.kvm._pipedStreams)
{
this.httprequest.desktop.kvm._pipedStreams[i].end();
}
this.httprequest.desktop.kvm.end();
});
break;
}
}
2018-04-19 21:19:15 -04:00
}
2018-01-16 20:30:34 -05:00
};
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount'))
{
this.httprequest.desktop.kvm.connectionCount++;
this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
this.httprequest.desktop.kvm.users.push(this.httprequest.username);
this.httprequest.desktop.kvm.rusers.sort();
this.httprequest.desktop.kvm.users.sort();
2021-01-15 02:57:52 -05:00
} else
{
this.httprequest.desktop.kvm.connectionCount = 1;
this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
this.httprequest.desktop.kvm.users = [this.httprequest.username];
}
2021-01-15 02:57:52 -05:00
if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)))
{
// If we have remote control rights, pipe the KVM input
this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
2021-01-15 02:57:52 -05:00
} else
{
// We need to only pipe non-mouse & non-keyboard inputs.
//sendConsoleText('Warning: No Remote Desktop Input Rights.');
// TODO!!!
}
2019-06-14 19:33:53 -04:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
if (this.httprequest.consent && (this.httprequest.consent & 8))
{
2019-06-14 19:33:53 -04:00
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2020-07-10 13:33:41 -04:00
var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
2021-01-15 02:57:52 -05:00
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
2019-06-14 19:33:53 -04:00
pr.ws = this;
this.pause();
this._consentpromise = pr;
2021-01-15 02:57:52 -05:00
this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2019-06-14 19:33:53 -04:00
pr.then(
function ()
{
2019-07-29 19:35:48 -04:00
// Success
this.ws._consentpromise = null;
MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
{
2019-06-14 19:33:53 -04:00
// User Notifications is required
2020-07-10 13:33:41 -04:00
var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2019-06-14 19:33:53 -04:00
}
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40))
{
// Connection Bar is required
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.desktop.kvm.connectionBar)
{
this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.ws.httprequest.desktop.kvm.connectionBar.close();
}
2021-01-15 02:57:52 -05:00
try
{
this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
}
2021-01-15 02:57:52 -05:00
catch (e)
{
if (process.platform != 'darwin')
{
MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
}
}
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.desktop.kvm.connectionBar)
{
this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
2021-01-15 02:57:52 -05:00
this.ws.httprequest.desktop.kvm.connectionBar.on('close', function ()
{
MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2021-01-15 02:57:52 -05:00
for (var i in this.httprequest.desktop.kvm._pipedStreams)
{
this.httprequest.desktop.kvm._pipedStreams[i].end();
}
this.httprequest.desktop.kvm.end();
});
}
}
2019-06-14 19:33:53 -04:00
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
this.ws.resume();
},
function (e)
{
2019-07-29 19:35:48 -04:00
// User Consent Denied/Failed
this.ws._consentpromise = null;
MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2019-06-14 19:33:53 -04:00
});
2021-01-15 02:57:52 -05:00
} else
{
2019-06-14 19:33:53 -04:00
// User Consent Prompt is not required
2021-01-15 02:57:52 -05:00
if (this.httprequest.consent && (this.httprequest.consent & 1))
{
2019-06-14 19:33:53 -04:00
// User Notifications is required
MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2020-07-10 13:33:41 -04:00
var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2021-01-15 02:57:52 -05:00
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2021-01-15 02:57:52 -05:00
} else
{
MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2019-06-14 19:33:53 -04:00
}
2021-01-15 02:57:52 -05:00
if (this.httprequest.consent && (this.httprequest.consent & 0x40))
{
// Connection Bar is required
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.connectionBar)
{
this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
this.httprequest.desktop.kvm.connectionBar.close();
}
2021-01-15 02:57:52 -05:00
try
{
this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
}
2021-01-15 02:57:52 -05:00
catch (e)
{
MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
}
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.kvm.connectionBar)
{
this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2021-01-15 02:57:52 -05:00
this.httprequest.desktop.kvm.connectionBar.on('close', function ()
{
MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2021-01-15 02:57:52 -05:00
for (var i in this.httprequest.desktop.kvm._pipedStreams)
{
this.httprequest.desktop.kvm._pipedStreams[i].end();
}
this.httprequest.desktop.kvm.end();
});
}
}
2019-06-14 19:33:53 -04:00
this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
}
2018-01-16 20:30:34 -05:00
this.removeAllListeners('data');
this.on('data', onTunnelControlData);
//this.write('MeshCore KVM Hello!1');
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.protocol == 5)
{
2020-05-21 16:22:58 -04:00
//
// Remote Files
//
2019-03-01 14:47:58 -05:00
// Check user access rights for files
2021-01-15 02:57:52 -05:00
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0)))
{
// Disengage this tunnel, user does not have the rights to do this!!
this.httprequest.protocol = 999999;
2019-03-01 14:47:58 -05:00
this.httprequest.s.end();
2020-01-07 16:04:36 -05:00
sendConsoleText("Error: No files control rights.");
return;
}
2020-05-21 16:22:58 -04:00
this.descriptorMetadata = "Remote Files";
2020-05-15 18:23:37 -04:00
// Add the files session to the count to update the server
2021-01-15 02:57:52 -05:00
if (this.httprequest.userid != null)
{
2020-05-15 18:23:37 -04:00
if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-05-15 18:23:37 -04:00
}
2021-01-15 02:57:52 -05:00
this.end = function ()
{
2020-05-15 18:23:37 -04:00
// Remove the files session from the count to update the server
2021-01-15 02:57:52 -05:00
if (this.httprequest.userid != null)
{
2020-05-15 18:23:37 -04:00
if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } }
try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
broadcastSessionsToRegisteredApps();
2020-05-15 18:23:37 -04:00
}
};
2019-06-14 19:33:53 -04:00
// Perform notification if needed. Toast messages may not be supported on all platforms.
2021-01-15 02:57:52 -05:00
if (this.httprequest.consent && (this.httprequest.consent & 32))
{
2019-06-14 19:33:53 -04:00
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2020-07-10 13:33:41 -04:00
var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2021-01-15 02:57:52 -05:00
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
var pr = require('message-box').create(consentTitle, consentMessage, 30);
2019-06-14 19:33:53 -04:00
pr.ws = this;
this.pause();
this._consentpromise = pr;
this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2019-06-14 19:33:53 -04:00
pr.then(
function ()
{
2019-07-29 19:35:48 -04:00
// Success
this.ws._consentpromise = null;
MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2019-06-14 19:33:53 -04:00
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
{
2019-06-14 19:33:53 -04:00
// User Notifications is required
2020-07-10 13:33:41 -04:00
var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2021-01-15 02:57:52 -05:00
if (this.ws.httprequest.soptions != null)
{
if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2019-06-14 19:33:53 -04:00
}
this.ws.resume();
},
function (e)
{
2019-07-29 19:35:48 -04:00
// User Consent Denied/Failed
this.ws._consentpromise = null;
MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2019-06-14 19:33:53 -04:00
});
2021-01-15 02:57:52 -05:00
} else
{
2019-06-14 19:33:53 -04:00
// User Consent Prompt is not required
2021-01-15 02:57:52 -05:00
if (this.httprequest.consent && (this.httprequest.consent & 4))
{
2019-06-14 19:33:53 -04:00
// User Notifications is required
MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2020-07-10 13:33:41 -04:00
var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2021-01-15 02:57:52 -05:00
if (this.httprequest.soptions != null)
{
if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
}
try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2021-01-15 02:57:52 -05:00
} else
{
MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2019-06-14 19:33:53 -04:00
}
this.resume();
2019-04-12 17:19:03 -04:00
}
2017-08-28 12:27:45 -04:00
// Setup files
// NOP
}
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.protocol == 1)
{
2017-08-28 12:27:45 -04:00
// Send data into terminal stdin
//this.write(data); // Echo back the keys (Does not seem to be a good idea)
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.protocol == 2)
{
2017-08-28 12:27:45 -04:00
// Send data into remote desktop
2021-01-15 02:57:52 -05:00
if (this.httprequest.desktop.state == 0)
{
2020-08-06 16:23:17 -04:00
this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2017-08-28 12:27:45 -04:00
this.httprequest.desktop.state = 1;
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
this.httprequest.desktop.write(data);
}
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.protocol == 5)
{
2017-08-28 12:27:45 -04:00
// Process files commands
var cmd = null;
try { cmd = JSON.parse(data); } catch (e) { };
if (cmd == null) { return; }
if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
if (cmd.action == undefined) { return; }
//sendConsoleText('CMD: ' + JSON.stringify(cmd));
2017-10-16 23:11:03 -04:00
if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
//console.log(objToString(cmd, 0, ' '));
2021-01-15 02:57:52 -05:00
switch (cmd.action)
{
2017-08-28 12:27:45 -04:00
case 'ls': {
2017-12-14 17:57:52 -05:00
/*
2017-08-28 12:27:45 -04:00
// Close the watcher if required
var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
if ((this.httprequest.watcher != undefined) && (samepath == false)) {
//console.log('Closing watcher: ' + this.httprequest.watcher.path);
//this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
delete this.httprequest.watcher;
}
2017-12-14 17:57:52 -05:00
*/
2017-08-28 12:27:45 -04:00
// Send the folder content to the browser
var response = getDirectoryInfo(cmd.path);
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2020-08-06 16:23:17 -04:00
this.write(Buffer.from(JSON.stringify(response)));
2019-10-10 14:13:25 -04:00
2017-12-14 17:57:52 -05:00
/*
2017-08-28 12:27:45 -04:00
// Start the directory watcher
if ((cmd.path != '') && (samepath == false)) {
var watcher = fs.watch(cmd.path, onFileWatcher);
watcher.tunnel = this.httprequest;
watcher.path = cmd.path;
this.httprequest.watcher = watcher;
//console.log('Starting watcher: ' + this.httprequest.watcher.path);
}
2017-12-14 17:57:52 -05:00
*/
2017-08-28 12:27:45 -04:00
break;
}
case 'mkdir': {
// Create a new empty folder
fs.mkdirSync(cmd.path);
MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2017-08-28 12:27:45 -04:00
break;
}
case 'rm': {
2018-09-25 14:51:40 -04:00
// Delete, possibly recursive delete
2021-01-15 02:57:52 -05:00
for (var i in cmd.delfiles)
{
2019-07-29 19:35:48 -04:00
var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2021-01-15 02:57:52 -05:00
if ((delcount == 1) && !cmd.rec)
{
MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2021-01-15 02:57:52 -05:00
} else
{
if (cmd.rec)
{
MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2021-01-15 02:57:52 -05:00
} else
{
MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
}
}
2017-08-28 12:27:45 -04:00
}
break;
}
case 'markcoredump': {
// If we are asking for the coredump file, set the right path.
var coreDumpPath = null;
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2021-01-15 02:57:52 -05:00
} else
{
if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
}
if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
break;
}
2017-08-28 12:27:45 -04:00
case 'rename': {
// Rename a file or folder
var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
var newfullpath = obj.path.join(cmd.path, cmd.newname);
MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2017-08-28 12:27:45 -04:00
try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
break;
}
2020-11-12 01:56:47 -05:00
case 'findfile': {
// Search for files
var r = require('file-search').find('"' + cmd.path + '"', cmd.filter);
2020-11-12 20:49:22 -05:00
if (!r.cancel) { r.cancel = function cancel() { this.child.kill(); }; }
this._search = r;
2020-11-12 01:56:47 -05:00
r.socket = this;
r.socket.reqid = cmd.reqid; // Search request id. This is used to send responses and cancel the request.
r.socket.path = cmd.path; // Search path
r.on('result', function (str) { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: str.substring(this.socket.path.length), reqid: this.socket.reqid }))); } catch (ex) { } });
r.then(function () { try { this.socket.write(Buffer.from(JSON.stringify({ action: 'findfile', r: null, reqid: this.socket.reqid }))); } catch (ex) { } });
break;
}
case 'cancelfindfile': {
if (this._search) { this._search.cancel(); this._search = null; }
2020-11-12 01:56:47 -05:00
}
case 'download': {
// Download a file
var sendNextBlock = 0;
2021-01-15 02:57:52 -05:00
if (cmd.sub == 'start')
{ // Setup the download
if ((cmd.path == null) && (cmd.ask == 'coredump'))
{ // If we are asking for the coredump file, set the right path.
if (process.platform == 'win32')
{
if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2021-01-15 02:57:52 -05:00
} else
{
if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
}
}
2021-01-15 02:57:52 -05:00
MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2021-01-15 02:57:52 -05:00
} else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
{ // Download commands
if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
}
// Send the next download block(s)
2021-01-15 02:57:52 -05:00
while (sendNextBlock > 0)
{
sendNextBlock--;
2020-08-06 16:23:17 -04:00
var buf = Buffer.alloc(16384);
var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
this.filedownload.ptr += len;
if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2018-02-13 15:28:11 -05:00
this.write(buf.slice(0, len + 4)); // Write as binary
}
break;
}
2021-01-15 02:57:52 -05:00
/*
case 'download': {
// Packet download of a file, agent to browser
if (cmd.path == undefined) break;
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
//console.log('Download: ' + filepath);
try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
this.httprequest.downloadFileId = cmd.reqid;
this.httprequest.downloadFilePtr = 0;
if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
break;
}
case 'download2': {
// Stream download of a file, agent to browser
if (cmd.path == undefined) break;
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
this.httprequest.downloadFile.pipe(this);
this.httprequest.downloadFile.end = function () { }
break;
}
*/
2017-08-28 12:27:45 -04:00
case 'upload': {
// Upload a file, browser to agent
if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2017-08-28 12:27:45 -04:00
if (cmd.path == undefined) break;
var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2020-08-13 15:29:18 -04:00
this.httprequest.uploadFilePath = filepath;
MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2020-08-06 16:23:17 -04:00
try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2017-08-28 12:27:45 -04:00
this.httprequest.uploadFileid = cmd.reqid;
2020-08-06 16:23:17 -04:00
if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2017-08-28 12:27:45 -04:00
break;
}
2020-08-13 15:29:18 -04:00
case 'uploaddone': {
// Indicates that an upload is done
2021-01-15 02:57:52 -05:00
if (this.httprequest.uploadFile)
{
2020-08-13 15:29:18 -04:00
fs.closeSync(this.httprequest.uploadFile);
this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
delete this.httprequest.uploadFile;
delete this.httprequest.uploadFileid;
delete this.httprequest.uploadFilePath;
}
break;
}
case 'uploadcancel': {
// Indicates that an upload is canceled
2021-01-15 02:57:52 -05:00
if (this.httprequest.uploadFile)
{
2020-08-13 15:29:18 -04:00
fs.closeSync(this.httprequest.uploadFile);
fs.unlinkSync(this.httprequest.uploadFilePath);
this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
delete this.httprequest.uploadFile;
delete this.httprequest.uploadFileid;
delete this.httprequest.uploadFilePath;
}
break;
}
case 'copy': {
// Copy a bunch of files from scpath to dspath
2021-01-15 02:57:52 -05:00
for (var i in cmd.names)
{
var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
}
break;
}
case 'move': {
// Move a bunch of files from scpath to dspath
2021-01-15 02:57:52 -05:00
for (var i in cmd.names)
{
var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
}
break;
}
2020-08-13 16:13:21 -04:00
case 'zip':
// Zip a bunch of files
if (this.zip != null) return; // Zip operating is currently running, exit now.
// Check that the specified files exist & build full paths
var fp, stat, p = [];
for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (e) { } if (stat != null) { p.push(fp); } }
if (p.length == 0) return; // No files, quit now.
// Setup file compression
var ofile = cmd.path + '/' + cmd.output;
this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2020-08-28 21:29:32 -04:00
this.zipfile = ofile;
delete this.zipcancel;
2020-08-13 16:13:21 -04:00
var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
out.xws = this;
2021-01-15 02:57:52 -05:00
out.on('close', function ()
{
2020-08-28 21:29:32 -04:00
this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
delete this.xws.zipcancel;
delete this.xws.zipfile;
delete this.xws.zip;
});
this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
this.zip.xws = this;
this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
2020-08-18 14:10:21 -04:00
this.zip.pipe(out);
2020-08-13 16:13:21 -04:00
break;
case 'cancel':
// Cancel zip operation if present
try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
this.zip = null;
break;
2019-01-04 20:59:13 -05:00
default:
// Unknown action, ignore it.
break;
2017-08-28 12:27:45 -04:00
}
2021-01-15 02:57:52 -05:00
} else if (this.httprequest.protocol == 7)
{ // Plugin data exchange
2019-10-10 14:13:25 -04:00
var cmd = null;
try { cmd = JSON.parse(data); } catch (e) { };
if (cmd == null) { return; }
if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
if (cmd.action == undefined) return;
2021-01-15 02:57:52 -05:00
switch (cmd.action)
{
2019-10-10 14:13:25 -04:00
case 'plugin': {
try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
break;
}
default: {
// probably shouldn't happen, but just in case this feature is expanded
}
}
2017-08-28 12:27:45 -04:00
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
}
2018-01-16 20:30:34 -05:00
2019-03-14 15:28:58 -04:00
// Delete a directory with a files and directories within it
2021-01-15 02:57:52 -05:00
function deleteFolderRecursive(path, rec)
{
2019-07-29 19:35:48 -04:00
var count = 0;
2021-01-15 02:57:52 -05:00
if (fs.existsSync(path))
{
if (rec == true)
{
fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index)
{
2019-03-14 15:28:58 -04:00
var curPath = obj.path.join(path, file);
2021-01-15 02:57:52 -05:00
if (fs.statSync(curPath).isDirectory())
{ // recurse
2019-07-29 19:35:48 -04:00
count += deleteFolderRecursive(curPath, true);
2021-01-15 02:57:52 -05:00
} else
{ // delete file
2019-03-14 15:28:58 -04:00
fs.unlinkSync(curPath);
2019-07-29 19:35:48 -04:00
count++;
2019-03-14 15:28:58 -04:00
}
});
}
fs.unlinkSync(path);
2019-07-29 19:35:48 -04:00
count++;
2019-03-14 15:28:58 -04:00
}
2019-07-29 19:35:48 -04:00
return count;
2019-03-14 15:28:58 -04:00
};
2018-01-18 18:43:43 -05:00
// Called when receiving control data on WebRTC
2021-01-15 02:57:52 -05:00
function onTunnelWebRTCControlData(data)
{
2018-01-18 18:43:43 -05:00
if (typeof data != 'string') return;
var obj;
try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2021-01-15 02:57:52 -05:00
if (obj.type == 'close')
{
//sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2018-01-18 18:43:43 -05:00
try { this.close(); } catch (e) { }
try { this.xrtc.close(); } catch (e) { }
}
}
// Called when receiving control data on websocket
2021-01-15 02:57:52 -05:00
function onTunnelControlData(data, ws)
{
2018-01-18 18:43:43 -05:00
var obj;
if (ws == null) { ws = this; }
if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
else if (typeof data == 'object') { obj = data; } else { return; }
//sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
//console.log('onTunnelControlData: ' + JSON.stringify(data));
2018-01-18 18:43:43 -05:00
2021-01-15 02:57:52 -05:00
if (obj.action)
{
switch (obj.action)
{
case 'lock': {
// Lock the current user out of the desktop
2021-01-15 02:57:52 -05:00
try
{
if (process.platform == 'win32')
{
MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
var child = require('child_process');
child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
}
} catch (e) { }
break;
}
2019-01-04 20:59:13 -05:00
default:
// Unknown action, ignore it.
break;
}
return;
}
2021-01-15 02:57:52 -05:00
switch (obj.type)
{
2020-01-24 15:49:41 -05:00
case 'options': {
// These are additional connection options passed in the control channel.
//sendConsoleText('options: ' + JSON.stringify(obj));
delete obj.type;
ws.httprequest.xoptions = obj;
// Set additional user consent options if present
2020-04-15 15:36:22 -04:00
if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2020-01-24 15:49:41 -05:00
break;
}
2020-01-24 15:49:41 -05:00
case 'close': {
// We received the close on the websocket
//sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
try { ws.close(); } catch (e) { }
break;
}
case 'termsize': {
// Indicates a change in terminal size
if (process.platform == 'win32')
{
if (ws.httprequest._dispatcher == null) return;
//sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
} else
{
if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
//sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
break;
2018-01-18 18:43:43 -05:00
}
2020-01-24 15:49:41 -05:00
case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2021-01-15 02:57:52 -05:00
if (ws.httprequest.protocol == 1)
{ // Terminal
2020-01-24 15:49:41 -05:00
// This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
2020-01-24 15:49:41 -05:00
ws.httprequest._term.unpipe(ws);
2021-01-15 02:57:52 -05:00
} else
{
2020-01-24 15:49:41 -05:00
ws.httprequest.process.stdout.unpipe(ws);
ws.httprequest.process.stderr.unpipe(ws);
}
2021-01-15 02:57:52 -05:00
} else if (ws.httprequest.protocol == 2)
{ // Desktop
2020-01-24 15:49:41 -05:00
// This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
ws.httprequest.desktop.kvm.unpipe(ws);
2021-01-15 02:57:52 -05:00
} else
{
2020-01-24 15:49:41 -05:00
// Switch things around so all WebRTC data goes to onTunnelData().
ws.rtcchannel.httprequest = ws.httprequest;
ws.rtcchannel.removeAllListeners('data');
ws.rtcchannel.on('data', onTunnelData);
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
break;
}
case 'webrtc1': {
2021-01-15 02:57:52 -05:00
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6))
{ // Terminal
2020-01-24 15:49:41 -05:00
// Switch the user input from websocket to webrtc at this point.
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
2020-01-24 15:49:41 -05:00
ws.unpipe(ws.httprequest._term);
ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2021-01-15 02:57:52 -05:00
} else
{
2020-01-24 15:49:41 -05:00
ws.unpipe(ws.httprequest.process.stdin);
ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
ws.resume(); // Resume the websocket to keep receiving control data
2021-01-15 02:57:52 -05:00
} else if (ws.httprequest.protocol == 2)
{ // Desktop
2020-01-24 15:49:41 -05:00
// Switch the user input from websocket to webrtc at this point.
ws.unpipe(ws.httprequest.desktop.kvm);
try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
ws.resume(); // Resume the websocket to keep receiving control data
2018-12-12 18:34:42 -05:00
}
2020-01-24 15:49:41 -05:00
ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
break;
2018-01-18 18:43:43 -05:00
}
2020-01-24 15:49:41 -05:00
case 'webrtc2': {
// Other side received websocket end of data marker, start sending data on WebRTC channel
2021-01-15 02:57:52 -05:00
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6))
{ // Terminal
if (process.platform == 'win32')
{
2020-01-24 15:49:41 -05:00
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2021-01-15 02:57:52 -05:00
} else
{
2020-01-24 15:49:41 -05:00
ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
}
2021-01-15 02:57:52 -05:00
} else if (ws.httprequest.protocol == 2)
{ // Desktop
2020-01-24 15:49:41 -05:00
ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
break;
}
case 'offer': {
// This is a WebRTC offer.
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
ws.webrtc = rtc.createConnection();
ws.webrtc.websocket = ws;
ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2021-01-15 02:57:52 -05:00
ws.webrtc.on('dataChannel', function (rtcchannel)
{
2020-01-24 15:49:41 -05:00
//sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
rtcchannel.maxFragmentSize = 32768;
2020-01-24 15:49:41 -05:00
rtcchannel.xrtc = this;
rtcchannel.websocket = this.websocket;
this.rtcchannel = rtcchannel;
this.websocket.rtcchannel = rtcchannel;
this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2021-01-15 02:57:52 -05:00
this.websocket.rtcchannel.on('end', function ()
{
2020-01-24 15:49:41 -05:00
// The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
//sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
if (this.websocket.desktop && this.websocket.desktop.kvm)
{
try
{
this.unpipe(this.websocket.desktop.kvm);
this.websocket.httprequest.desktop.kvm.unpipe(this);
}
catch (e) { }
2020-01-24 15:49:41 -05:00
}
});
this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
});
2020-01-24 15:49:41 -05:00
var sdp = null;
try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (e) { }
2020-01-24 15:49:41 -05:00
if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
break;
}
case 'ping': {
ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
break;
}
case 'pong': { // NOP
break;
}
2020-04-14 06:08:53 -04:00
case 'rtt': {
ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
break;
}
2018-01-16 20:30:34 -05:00
}
}
2017-08-28 12:27:45 -04:00
// Console state
var consoleWebSockets = {};
var consoleHttpRequest = null;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Console HTTP response
2021-01-15 02:57:52 -05:00
function consoleHttpResponse(response)
{
2017-08-28 12:27:45 -04:00
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
};
// Open a web browser to a specified URL on current user's desktop
2021-01-15 02:57:52 -05:00
function openUserDesktopUrl(url)
{
if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
var child = null;
2021-01-15 02:57:52 -05:00
try
{
switch (process.platform)
{
case 'win32':
var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
child.stderr.on('data', function () { });
child.stdout.on('data', function () { });
child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process.env['windir'] + '\\system32\\cmd.exe /C START ' + url + '"\r\n');
child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
child.stdin.write('exit\r\n');
child.waitExit();
break;
case 'linux':
child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
break;
case 'darwin':
child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
break;
2019-01-04 20:59:13 -05:00
default:
// Unknown platform, ignore this command.
break;
}
} catch (e) { }
return child;
}
2017-08-28 12:27:45 -04:00
// Process a mesh agent console command
2021-01-15 02:57:52 -05:00
function processConsoleCommand(cmd, args, rights, sessionid)
{
try
{
2017-08-28 12:27:45 -04:00
var response = null;
2021-01-15 02:57:52 -05:00
switch (cmd)
{
2017-08-28 12:27:45 -04:00
case 'help': { // Displays available commands
2021-01-15 02:57:52 -05:00
var fin = '', f = '', availcommands = 'agentupdate,msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg';
2020-05-17 03:22:26 -04:00
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
if (amt != null) { availcommands += ',amt,amtconfig,amtevents'; }
2021-01-15 02:57:52 -05:00
if (process.platform != 'freebsd') { availcommands += ',vm'; }
if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
availcommands = availcommands.split(',').sort();
2021-01-15 02:57:52 -05:00
while (availcommands.length > 0)
{
if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
f += (((f != '') ? ', ' : ' ') + availcommands.shift());
}
if (f != '') { fin += f; }
2020-01-07 16:04:36 -05:00
response = "Available commands: \r\n" + fin + ".";
2018-04-19 21:19:15 -04:00
break;
}
case 'agentupdate':
2021-01-15 02:57:52 -05:00
require('MeshAgent').SendCommand({ action: 'agentupdate' });
break;
case 'agentupdateex':
// Perform an direct agent update without requesting any information from the server, this should not typically be used.
agentUpdate_Start(null, { session: sessionid });
break;
case 'msh':
response = JSON.stringify(_MSH(), null, 2);
break;
2020-12-10 02:53:23 -05:00
case 'timerinfo':
response = require('ChainViewer').getTimerInfo();
break;
2020-11-09 17:17:11 -05:00
case 'find':
if (args['_'].length <= 1)
{
response = "Proper usage:\r\n find root criteria [criteria2] [criteria n...]";
}
else
{
var root = args['_'][0];
var p = args['_'].slice(1);
var r = require('file-search').find(root, p);
r.sid = sessionid;
r.on('result', function (str) { sendConsoleText(str, this.sid); });
r.then(function () { sendConsoleText('*** End Results ***', this.sid); });
response = "Find: [" + root + "] " + JSON.stringify(p);
}
break;
2020-10-30 20:08:07 -04:00
case 'coreinfo': {
response = JSON.stringify(meshCoreObj, null, 2);
break;
}
2020-11-02 03:44:07 -05:00
case 'coreinfoupdate': {
sendPeriodicServerUpdate();
break;
}
2020-07-31 21:05:00 -04:00
case 'agentmsg': {
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2020-07-31 21:05:00 -04:00
response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2021-01-15 02:57:52 -05:00
} else
{
if ((args['_'][0] == 'add') && (args['_'].length > 1))
{
2020-07-31 21:05:00 -04:00
var msgIndex = 1, iconIndex = 0;
while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
2020-07-31 21:05:00 -04:00
if (typeof iconIndex != 'number') { iconIndex = 0; }
tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
response = 'Agent message ' + msgIndex + ' added.';
2021-01-15 02:57:52 -05:00
} else if ((args['_'][0] == 'remove') && (args['_'].length > 1))
{
2020-07-31 21:05:00 -04:00
var msgIndex = 0;
try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
2020-07-31 21:05:00 -04:00
if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2021-01-15 02:57:52 -05:00
} else if (args['_'][0] == 'list')
{
2020-07-31 21:05:00 -04:00
response = JSON.stringify(tunnelUserCount.msg, null, 2);
}
try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
broadcastSessionsToRegisteredApps();
2020-07-28 21:45:17 -04:00
}
break;
}
case 'clearagentmsg': {
tunnelUserCount.msg = {};
try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
broadcastSessionsToRegisteredApps();
2020-07-28 21:45:17 -04:00
break;
}
2020-07-06 17:19:16 -04:00
case 'coredump':
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
response = "Proper usage: coredump on|off|status|clear"; // Display usage
2021-01-15 02:57:52 -05:00
} else
{
switch (args['_'][0].toLowerCase())
{
2020-07-06 17:19:16 -04:00
case 'on':
process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2020-07-06 17:19:16 -04:00
response = 'coredump is now on';
break;
case 'off':
process.coreDumpLocation = null;
response = 'coredump is now off';
break;
case 'status':
response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2021-01-15 02:57:52 -05:00
if (process.coreDumpLocation != null)
{
if (process.platform == 'win32')
{
if (fs.existsSync(process.coreDumpLocation))
{
2020-10-27 19:40:12 -04:00
response += '\r\n CoreDump present at: ' + process.coreDumpLocation;
response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.coreDumpLocation).mtime).getTime();
response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
}
2021-01-15 02:57:52 -05:00
} else
{
if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core'))
{
2020-10-27 19:40:12 -04:00
response += '\r\n CoreDump present at: ' + process.cwd() + 'core';
response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.cwd() + 'core').mtime).getTime();
response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
}
2020-07-06 20:16:54 -04:00
}
}
2020-07-06 17:19:16 -04:00
break;
case 'clear':
db.Put('CoreDumpTime', null);
response = 'coredump db cleared';
break;
2020-07-06 17:19:16 -04:00
default:
response = "Proper usage: coredump on|off|status"; // Display usage
2020-07-06 17:19:16 -04:00
break;
}
}
break;
2020-07-01 16:15:07 -04:00
case 'service':
if (args['_'].length != 1)
{
response = "Proper usage: service status|restart"; // Display usage
2020-07-01 16:15:07 -04:00
}
else
{
var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
try
{
svcname = require('MeshAgent').serviceName;
}
2021-01-15 02:57:52 -05:00
catch (x)
{
}
var s = require('service-manager').manager.getService(svcname);
2021-01-15 02:57:52 -05:00
switch (args['_'][0].toLowerCase())
{
2020-07-01 16:15:07 -04:00
case 'status':
response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
break;
case 'restart':
if (s.isMe())
{
2020-07-01 16:15:07 -04:00
s.restart();
}
else
{
2020-07-01 16:15:07 -04:00
response = 'Restarting another agent instance is not allowed';
}
break;
default:
response = "Proper usage: service status|restart"; // Display usage
2020-07-01 16:15:07 -04:00
break;
}
if (process.platform == 'win32') { s.close(); }
}
break;
case 'zip':
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2021-01-15 02:57:52 -05:00
} else
{
var p = args['_'].join(' ').split(',');
var ofile = p.shift();
sendConsoleText('Writing ' + ofile + '...');
var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
out.fname = ofile;
out.sessionid = sessionid;
out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
var zip = require('zip-writer').write({ files: p });
zip.pipe(out);
}
break;
case 'unzip':
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
response = "Proper usage: unzip input, destination"; // Display usage
2021-01-15 02:57:52 -05:00
} else
{
var p = args['_'].join(' ').split(',');
2020-08-13 16:13:21 -04:00
if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
var prom = require('zip-reader').read(p[0]);
prom._dest = p[1];
prom.self = this;
prom.sessionid = sessionid;
2021-01-15 02:57:52 -05:00
prom.then(function (zipped)
{
sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
}, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
}
break;
2020-05-30 20:21:45 -04:00
case 'setbattery':
// require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2021-01-15 02:57:52 -05:00
if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc')))
{
2020-05-30 20:21:45 -04:00
var b = { action: 'battery', state: args['_'][0] };
if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
require('MeshAgent').SendCommand(b);
2021-01-15 02:57:52 -05:00
} else
{
2020-05-30 20:21:45 -04:00
require('MeshAgent').SendCommand({ action: 'battery' });
}
break;
case 'fdsnapshot':
require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
break;
case 'fdcount':
require('DescriptorEvents').getDescriptorCount().then(
function (c)
{
sendConsoleText('Descriptor Count: ' + c, this.sessionid);
}, function (e)
{
sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
}).parentPromise.sessionid = sessionid;
break;
2020-05-17 03:22:26 -04:00
case 'uac':
if (process.platform != 'win32')
{
2020-05-17 03:22:26 -04:00
response = 'Unknown command "uac", type "help" for list of avaialble commands.';
break;
}
if (args['_'].length != 1)
{
2020-05-17 03:22:26 -04:00
response = 'Proper usage: uac [get|interactive|secure]';
}
else
{
2021-01-15 02:57:52 -05:00
switch (args['_'][0].toUpperCase())
{
2020-05-17 03:22:26 -04:00
case 'GET':
var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
break;
case 'INTERACTIVE':
try
{
2020-05-17 03:22:26 -04:00
require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
response = 'UAC mode changed to: Interactive Desktop';
}
catch (e)
{
2020-05-17 03:22:26 -04:00
response = "Unable to change UAC Mode";
}
break;
case 'SECURE':
try
{
2020-05-17 03:22:26 -04:00
require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
response = 'UAC mode changed to: Secure Desktop';
}
2021-01-15 02:57:52 -05:00
catch (e)
{
2020-05-17 03:22:26 -04:00
response = "Unable to change UAC Mode";
}
break;
default:
response = 'Proper usage: uac [get|interactive|secure]';
break;
}
}
break;
2021-01-15 02:57:52 -05:00
case 'vm':
response = 'Virtual Machine = ' + require('identifiers').isVM();
break;
2020-04-08 20:09:11 -04:00
case 'startupoptions':
response = JSON.stringify(require('MeshAgent').getStartupOptions());
break;
case 'kvmmode':
if (require('MeshAgent').maxKvmTileSize == null)
{
response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
}
else
{
2021-01-15 02:57:52 -05:00
if (require('MeshAgent').maxKvmTileSize == 0)
{
response = 'KVM Mode: Full JUMBO';
}
2021-01-15 02:57:52 -05:00
else
{
response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2021-01-15 02:57:52 -05:00
response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize / 1024) + ' Kbytes')));
}
}
break;
2020-03-12 00:25:03 -04:00
case 'alert':
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2020-03-12 00:25:03 -04:00
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
}
else
{
2020-03-12 00:25:03 -04:00
var p = args['_'].join(' ').split(',');
2021-01-15 02:57:52 -05:00
if (p.length < 2)
{
2020-03-12 00:25:03 -04:00
response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
}
else
{
2021-01-15 02:57:52 -05:00
this._alert = require('message-box').create(p[0], p[1], p.length == 3 ? parseInt(p[2]) : 9999, 1);
2020-03-12 00:25:03 -04:00
}
}
break;
2020-01-07 12:51:15 -05:00
case 'agentsize':
var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
2020-01-07 12:51:15 -05:00
// Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2020-01-07 16:04:36 -05:00
var writtenSize = 0;
try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
2021-01-15 02:57:52 -05:00
if (writtenSize != actualSize)
{
2020-01-07 16:04:36 -05:00
response = "Size updated from: " + writtenSize + " to: " + actualSize;
try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
2021-01-15 02:57:52 -05:00
} else
{ response = "Agent Size: " + actualSize + " kb"; }
} else
{ response = "Agent Size: " + actualSize + " kb"; }
2020-01-07 12:51:15 -05:00
break;
case 'versions':
response = JSON.stringify(process.versions, null, ' ');
break;
case 'wpfhwacceleration':
2020-01-07 16:04:36 -05:00
if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
2020-01-07 16:04:36 -05:00
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
}
2021-01-15 02:57:52 -05:00
else
{
var reg = require('win-registry');
var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
var key = reg.usernameToUserKey(uname);
2021-01-15 02:57:52 -05:00
switch (args['_'][0].toUpperCase())
{
default:
2020-01-07 16:04:36 -05:00
response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
break;
case 'ON':
2021-01-15 02:57:52 -05:00
try
{
2019-12-10 17:23:35 -05:00
reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2020-01-07 16:04:36 -05:00
response = "OK";
} catch (e) { response = "FAILED"; }
break;
case 'OFF':
2021-01-15 02:57:52 -05:00
try
{
2019-12-10 17:23:35 -05:00
reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
response = 'OK';
} catch (e) { response = 'FAILED'; }
break;
case 'STATUS':
var s;
try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (e) { s = 'DEFAULT'; }
2020-01-07 16:04:36 -05:00
response = "WPF Hardware Acceleration: " + s;
break;
case 'DEFAULT':
try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (e) { }
response = 'OK';
break;
}
}
break;
case 'tsid':
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
if (args['_'].length != 1)
{
2020-01-07 16:04:36 -05:00
response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2021-01-15 02:57:52 -05:00
} else
{
var i = parseInt(args['_'][0]);
require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2020-01-07 16:04:36 -05:00
response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
}
2021-01-15 02:57:52 -05:00
} else
{ response = "TSID command only supported on Windows"; }
break;
case 'activeusers':
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
var p = require('user-sessions').enumerateUsers();
p.sessionid = sessionid;
2021-01-15 02:57:52 -05:00
p.then(function (u)
{
var v = [];
2021-01-15 02:57:52 -05:00
for (var i in u)
{
2020-01-07 16:04:36 -05:00
if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
}
sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
});
2021-01-15 02:57:52 -05:00
} else
{ response = "activeusers command only supported on Windows"; }
break;
2019-11-13 17:45:16 -05:00
case 'wallpaper':
2021-01-15 02:57:52 -05:00
if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available))
{
2020-01-07 16:04:36 -05:00
response = "wallpaper command not supported on this platform";
2019-11-13 17:45:16 -05:00
}
2021-01-15 02:57:52 -05:00
else
{
if (args['_'].length != 1)
{
2019-11-13 17:45:16 -05:00
response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
}
2021-01-15 02:57:52 -05:00
else
{
switch (args['_'][0].toUpperCase())
{
2019-11-13 17:45:16 -05:00
default:
response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
break;
case 'GET':
case 'TOGGLE':
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
var current = child.stdout.str.trim();
2021-01-15 02:57:52 -05:00
if (args['_'][0].toUpperCase() == 'GET')
{
response = current;
break;
}
2021-01-15 02:57:52 -05:00
if (current != '')
{
require('MeshAgent')._wallpaper = current;
response = 'Wallpaper cleared';
2021-01-15 02:57:52 -05:00
} else
{
response = 'Wallpaper restored';
}
child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
child.stderr.on('data', function () { });
child.waitExit();
2019-11-13 17:45:16 -05:00
}
2021-01-15 02:57:52 -05:00
else
{
var id = require('user-sessions').consoleUid();
var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2021-01-15 02:57:52 -05:00
if (args['_'][0].toUpperCase() == 'GET')
{
response = current;
break;
}
2021-01-15 02:57:52 -05:00
if (current != '/dev/null')
{
require('MeshAgent')._wallpaper = current;
response = 'Wallpaper cleared';
2021-01-15 02:57:52 -05:00
} else
{
response = 'Wallpaper restored';
}
require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2019-11-13 17:45:16 -05:00
}
break;
}
}
}
break;
case 'safemode':
if (process.platform != 'win32')
{
response = 'safemode only supported on Windows Platforms'
}
else
{
if (args['_'].length != 1)
{
response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2020-01-07 16:04:36 -05:00
}
else
{
var svcname = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
try
{
svcname = require('MeshAgent').serviceName;
}
catch (x)
{
}
switch (args['_'][0].toUpperCase())
{
default:
response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
break;
case 'ON':
require('win-bcd').setKey('safeboot', 'Network');
require('win-bcd').enableSafeModeService(svcname);
break;
case 'OFF':
require('win-bcd').deleteKey('safeboot');
break;
case 'STATUS':
var nextboot = require('win-bcd').getKey('safeboot');
if (nextboot)
{
switch (nextboot)
{
2019-10-29 14:05:33 -04:00
case 'Network':
case 'network':
nextboot = 'SAFE_MODE_NETWORK';
break;
default:
nextboot = 'SAFE_MODE';
break;
}
}
2019-11-28 15:27:44 -05:00
response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
break;
}
}
}
break;
2021-01-15 02:57:52 -05:00
/*
case 'border':
{
if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
if (meshCoreObj.users.length > 0) {
obj.borderManager.Start(meshCoreObj.users[0]);
response = 'Border blinking is on.';
} else {
response = 'Cannot turn on border blinking, no logged in users.';
}
} else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
obj.borderManager.Stop();
response = 'Border blinking is off.';
} else {
2021-01-15 02:57:52 -05:00
response = 'Proper usage: border "on|off"'; // Display correct command usage
}
}
2021-01-15 02:57:52 -05:00
break;
*/
2019-08-02 14:35:59 -04:00
case 'av':
2021-01-15 02:57:52 -05:00
if (process.platform == 'win32')
{
2019-10-11 16:45:23 -04:00
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
response = JSON.stringify(require('win-info').av(), null, 1);
2021-01-15 02:57:52 -05:00
} else
{
response = 'Not supported on the platform';
}
2019-08-02 14:35:59 -04:00
break;
2019-07-29 19:35:48 -04:00
case 'log':
if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
break;
2019-03-17 22:34:52 -04:00
case 'getclip':
2021-01-15 02:57:52 -05:00
if (require('MeshAgent').isService)
{
2019-03-17 22:34:52 -04:00
require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
2021-01-15 02:57:52 -05:00
} else
{
2019-03-17 22:34:52 -04:00
require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
}
break;
case 'setclip': {
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
response = 'Proper usage: setclip "sample text"';
2021-01-15 02:57:52 -05:00
} else
{
if (require('MeshAgent').isService)
{
2019-03-17 22:34:52 -04:00
require('clipboard').dispatchWrite(args['_'][0]);
response = 'Setting clipboard to: "' + args['_'][0] + '"';
2019-03-17 22:34:52 -04:00
}
2021-01-15 02:57:52 -05:00
else
{
require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
2019-03-17 22:34:52 -04:00
}
}
break;
}
case 'openurl': {
if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
break;
}
case 'users': {
if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
2018-12-20 15:12:24 -05:00
require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
break;
}
2018-04-19 21:19:15 -04:00
case 'toast': {
2020-01-07 16:04:36 -05:00
if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
2021-01-15 02:57:52 -05:00
if (require('MeshAgent')._tsid == null)
{
require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
}
2021-01-15 02:57:52 -05:00
else
{
require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
}
2018-04-20 19:05:06 -04:00
}
2018-03-08 20:58:22 -05:00
break;
}
case 'setdebug': {
if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
break;
}
2018-04-11 16:49:05 -04:00
case 'ps': {
2021-01-15 02:57:52 -05:00
processManager.getProcesses(function (plist)
{
2018-04-11 16:49:05 -04:00
var x = '';
for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
2018-04-11 16:49:05 -04:00
sendConsoleText(x, sessionid);
});
break;
}
case 'kill': {
2021-01-15 02:57:52 -05:00
if ((args['_'].length < 1))
{
2018-04-11 16:49:05 -04:00
response = 'Proper usage: kill [pid]'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2018-04-11 16:49:05 -04:00
process.kill(parseInt(args['_'][0]));
response = 'Killed process ' + args['_'][0] + '.';
}
break;
}
case 'smbios': {
2018-09-27 19:17:05 -04:00
if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
break;
}
case 'rawsmbios': {
2018-09-27 19:17:05 -04:00
if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
response = '';
2021-01-15 02:57:52 -05:00
for (var i in SMBiosTablesRaw)
{
2018-09-27 19:17:05 -04:00
var header = false;
2021-01-15 02:57:52 -05:00
for (var j in SMBiosTablesRaw[i])
{
if (SMBiosTablesRaw[i][j].length > 0)
{
2018-09-27 19:17:05 -04:00
if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
}
}
2018-09-27 19:17:05 -04:00
}
}
break;
}
case 'eval': { // Eval JavaScript
2021-01-15 02:57:52 -05:00
if (args['_'].length < 1)
{
response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2020-09-22 17:25:22 -04:00
response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
}
2017-08-28 12:27:45 -04:00
break;
}
2019-10-24 16:13:18 -04:00
case 'uninstallagent': // Uninstall this agent
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
try
{
agentName = require('MeshAgent').serviceName;
}
catch (x)
{
}
2021-01-15 02:57:52 -05:00
if (!require('service-manager').manager.getService(agentName).isMe())
{
response = 'Uininstall failed, this instance is not the service instance';
2021-01-15 02:57:52 -05:00
} else
{
try { diagnosticAgent_uninstall(); } catch (e) { }
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
}
break;
2017-08-28 12:27:45 -04:00
case 'notify': { // Send a notification message to the mesh
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2020-01-07 16:04:36 -05:00
var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
2017-08-28 12:27:45 -04:00
if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
2020-01-07 16:04:36 -05:00
response = "ok";
2017-08-28 12:27:45 -04:00
}
break;
}
case 'cpuinfo': { // Return system information
// CPU & memory utilization
pr = require('sysinfo').cpuUtilization();
pr.sessionid = sessionid;
2021-01-15 02:57:52 -05:00
pr.then(function (data)
{
sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
2021-01-15 02:57:52 -05:00
}, function (e)
{
sendConsoleText(e);
});
break;
}
case 'sysinfo': { // Return system information
2021-01-15 02:57:52 -05:00
getSystemInformation(function (results, err)
{
if (results == null) { sendConsoleText(err, this.sessionid); } else {
sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
}
2019-09-23 19:46:26 -04:00
});
break;
}
2017-08-28 12:27:45 -04:00
case 'info': { // Return information about the agent and agent core module
2021-01-15 02:57:52 -05:00
response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
response += '\r\nModules: ' + addedModules.join(', ') + '.';
2018-09-20 14:45:12 -04:00
response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
var oldNodeId = db.Get('OldNodeId');
if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
//response += '\r\Debug Console: ' + debugConsole + '.';
2018-09-20 14:45:12 -04:00
break;
}
case 'osinfo': { // Return the operating system information
var i = 1;
2018-09-21 19:34:35 -04:00
if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
2021-01-15 02:57:52 -05:00
for (var j = 0; j < i; j++)
{
2018-09-20 14:45:12 -04:00
var pr = require('os').name();
pr.sessionid = sessionid;
2021-01-15 02:57:52 -05:00
pr.then(function (v)
{
2020-01-07 16:04:36 -05:00
sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
});
}
2017-08-28 12:27:45 -04:00
break;
}
case 'args': { // Displays parsed command arguments
response = 'args ' + objToString(args, 0, ' ', true);
2017-08-28 12:27:45 -04:00
break;
}
case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
var r = [];
for (var i in args['_']) { r.push(args['_'][i]); }
console.log(r.join(' '));
response = 'Message printed on agent console.';
break;
}
case 'type': { // Returns the content of a file
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
var max = 4096;
if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
if (max > 4096) max = 4096;
var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
2017-08-28 12:27:45 -04:00
response = buf.toString();
var i = response.indexOf('\n');
if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
if (r == max) response += '...';
fs.closeSync(fd);
}
break;
}
2017-09-25 14:00:57 -04:00
case 'dbkeys': { // Return all data store keys
response = JSON.stringify(db.Keys);
break;
}
2017-08-28 12:27:45 -04:00
case 'dbget': { // Return the data store value for a given key
if (db == null) { response = 'Database not accessible.'; break; }
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: dbget (key)'; // Display the value for a given database key
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
response = db.Get(args['_'][0]);
}
break;
}
case 'dbset': { // Set a data store key and value pair
if (db == null) { response = 'Database not accessible.'; break; }
2021-01-15 02:57:52 -05:00
if (args['_'].length != 2)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: dbset (key) (value)'; // Set a database key
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
var r = db.Put(args['_'][0], args['_'][1]);
response = 'Key set: ' + r;
}
break;
}
case 'dbcompact': { // Compact the data store
if (db == null) { response = 'Database not accessible.'; break; }
var r = db.Compact();
response = 'Database compacted: ' + r;
break;
}
case 'httpget': {
2021-01-15 02:57:52 -05:00
if (consoleHttpRequest != null)
{
2017-08-28 12:27:45 -04:00
response = 'HTTP operation already in progress.';
2021-01-15 02:57:52 -05:00
} else
{
if (args['_'].length != 1)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: httpget (url)';
2021-01-15 02:57:52 -05:00
} else
{
var options = http.parseUri(args['_'][0]);
2017-08-28 12:27:45 -04:00
options.method = 'GET';
2021-01-15 02:57:52 -05:00
if (options == null)
{
2017-08-28 12:27:45 -04:00
response = 'Invalid url.';
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
consoleHttpRequest.sessionid = sessionid;
2021-01-15 02:57:52 -05:00
if (consoleHttpRequest != null)
{
2017-08-28 12:27:45 -04:00
consoleHttpRequest.end();
response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
2017-08-28 12:27:45 -04:00
}
}
}
}
break;
}
case 'wslist': { // List all web sockets
response = '';
2021-01-15 02:57:52 -05:00
for (var i in consoleWebSockets)
{
2017-08-28 12:27:45 -04:00
var httprequest = consoleWebSockets[i];
response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
}
if (response == '') { response = 'no websocket sessions.'; }
break;
}
case 'wsconnect': { // Setup a web socket
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
var httprequest = null;
2021-01-15 02:57:52 -05:00
try
{
2020-06-17 13:28:54 -04:00
var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
2018-01-04 15:15:21 -05:00
options.rejectUnauthorized = 0;
httprequest = http.request(options);
2017-09-01 20:34:02 -04:00
} catch (e) { response = 'Invalid HTTP websocket request'; }
2021-01-15 02:57:52 -05:00
if (httprequest != null)
{
2017-08-28 12:27:45 -04:00
httprequest.upgrade = onWebSocketUpgrade;
httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
var index = 1;
while (consoleWebSockets[index]) { index++; }
httprequest.sessionid = sessionid;
httprequest.index = index;
httprequest.url = args['_'][0];
consoleWebSockets[index] = httprequest;
response = 'New websocket session #' + index;
}
}
break;
}
case 'wssend': { // Send data on a web socket
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
2021-01-15 02:57:52 -05:00
for (var i in consoleWebSockets)
{
2017-08-28 12:27:45 -04:00
var httprequest = consoleWebSockets[i];
response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
}
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
var i = parseInt(args['_'][0]);
var httprequest = consoleWebSockets[i];
2021-01-15 02:57:52 -05:00
if (httprequest != undefined)
{
2017-08-28 12:27:45 -04:00
httprequest.s.write(args['_'][1]);
response = 'ok';
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
response = 'Invalid web socket number';
}
}
break;
}
case 'wsclose': { // Close a websocket
2021-01-15 02:57:52 -05:00
if (args['_'].length == 0)
{
2017-08-28 12:27:45 -04:00
response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
var i = parseInt(args['_'][0]);
var httprequest = consoleWebSockets[i];
2021-01-15 02:57:52 -05:00
if (httprequest != undefined)
{
2017-09-01 20:34:02 -04:00
if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
2017-08-28 12:27:45 -04:00
response = 'ok';
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
response = 'Invalid web socket number';
}
}
break;
}
case 'tunnels': { // Show the list of current tunnels
response = '';
for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
if (response == '') { response = 'No websocket sessions.'; }
break;
}
case 'ls': { // Show list of files and folders
response = '';
var xpath = '*';
if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
response = 'List of ' + xpath + '\r\n';
var results = fs.readdirSync(xpath);
2021-01-15 02:57:52 -05:00
for (var i = 0; i < results.length; ++i)
{
2017-08-28 12:27:45 -04:00
var stat = null, p = obj.path.join(args['_'][0], results[i]);
try { stat = fs.statSync(p); } catch (e) { }
2021-01-15 02:57:52 -05:00
if ((stat == null) || (stat == undefined))
{
2017-08-28 12:27:45 -04:00
response += (results[i] + "\r\n");
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
2017-10-23 17:09:58 -04:00
}
2017-08-28 12:27:45 -04:00
}
break;
}
case 'lsx': { // Show list of files and folders
response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
break;
}
case 'lock': { // Lock the current user out of the desktop
if (process.platform == 'win32') { var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); response = 'Ok'; }
else { response = 'Not supported on the platform'; }
break;
}
2017-08-28 12:27:45 -04:00
case 'amt': { // Show Intel AMT status
2021-01-15 02:57:52 -05:00
if (amt != null)
{
amt.getMeiState(9, function (state)
{
var resp = "Intel AMT not detected.";
if (state != null) { resp = objToString(state, 0, ' ', true); }
sendConsoleText(resp, sessionid);
});
2021-01-15 02:57:52 -05:00
} else
{
response = "Intel AMT not detected.";
}
2017-08-28 12:27:45 -04:00
break;
}
case 'netinfo': { // Show network interface information
var interfaces = require('os').networkInterfaces();
response = objToString(interfaces, 0, ' ', true);
break;
}
case 'wakeonlan': { // Send wake-on-lan
2021-01-15 02:57:52 -05:00
if ((args['_'].length != 1) || (args['_'][0].length != 12))
{
response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
2021-01-15 02:57:52 -05:00
} else
{
var count = sendWakeOnLan(args['_'][0]);
response = 'Sent wake-on-lan on ' + count + ' interface(s).';
}
2017-08-28 12:27:45 -04:00
break;
}
case 'sendall': { // Send a message to all consoles on this mesh
sendConsoleText(args['_'].join(' '));
break;
}
2017-09-01 14:23:22 -04:00
case 'power': { // Execute a power action on this computer
2021-01-15 02:57:52 -05:00
if (mesh.ExecPowerState == undefined)
{
2017-09-01 14:23:22 -04:00
response = 'Power command not supported on this agent.';
2021-01-15 02:57:52 -05:00
} else
{
if ((args['_'].length == 0) || isNaN(Number(args['_'][0])))
{
2017-09-01 14:23:22 -04:00
response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
2021-01-15 02:57:52 -05:00
} else
{
2020-02-04 19:22:00 -05:00
var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
2017-09-01 14:23:22 -04:00
response = 'Power action executed with return code: ' + r + '.';
}
}
break;
}
case 'location': {
2021-01-15 02:57:52 -05:00
getIpLocationData(function (location)
{
2020-01-07 16:04:36 -05:00
sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
});
break;
}
case 'parseuri': {
response = JSON.stringify(http.parseUri(args['_'][0]));
break;
}
case 'scanwifi': {
2021-01-15 02:57:52 -05:00
if (wifiScanner != null)
{
var wifiPresent = wifiScanner.hasWireless;
if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
2021-01-15 02:57:52 -05:00
} else
{ response = "Wifi module not present."; }
break;
}
2018-01-09 23:13:41 -05:00
case 'modules': {
response = JSON.stringify(addedModules);
break;
}
case 'listservices': {
var services = require('service-manager').manager.enumerateService();
response = JSON.stringify(services, null, 1);
break;
}
case 'getscript': {
2021-01-15 02:57:52 -05:00
if (args['_'].length != 1)
{
2020-01-07 16:04:36 -05:00
response = "Proper usage: getscript [scriptNumber].";
2021-01-15 02:57:52 -05:00
} else
{
2020-01-07 16:04:36 -05:00
mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
}
break;
2018-01-09 23:13:41 -05:00
}
2019-06-14 19:33:53 -04:00
case 'diagnostic':
{
2021-01-15 02:57:52 -05:00
if (!mesh.DAIPC.listening)
{
2019-06-14 19:33:53 -04:00
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
break;
}
var diag = diagnosticAgent_installCheck();
2021-01-15 02:57:52 -05:00
if (diag)
{
if (args['_'].length == 1 && args['_'][0] == 'uninstall')
{
2019-06-14 19:33:53 -04:00
diagnosticAgent_uninstall();
response = 'Diagnostic Agent uninstalled';
}
2021-01-15 02:57:52 -05:00
else
{
2019-06-14 19:33:53 -04:00
response = 'Diagnostic Agent installed at: ' + diag.appLocation();
}
}
2021-01-15 02:57:52 -05:00
else
{
if (args['_'].length == 1 && args['_'][0] == 'install')
{
2019-06-14 19:33:53 -04:00
diag = diagnosticAgent_installCheck(true);
2021-01-15 02:57:52 -05:00
if (diag)
{
2019-06-14 19:33:53 -04:00
response = 'Diagnostic agent was installed at: ' + diag.appLocation();
}
2021-01-15 02:57:52 -05:00
else
{
2019-06-14 19:33:53 -04:00
response = 'Diagnostic agent installation failed';
}
}
2021-01-15 02:57:52 -05:00
else
{
2019-06-14 19:33:53 -04:00
response = 'Diagnostic Agent Not installed. To install: diagnostic install';
}
}
if (diag) { diag.close(); diag = null; }
break;
}
2020-10-26 15:46:30 -04:00
case 'amtevents': {
if (obj.amtevents == null) { response = 'No events.'; } else { response = obj.amtevents.join('\r\n'); }
break;
}
case 'amtconfig': {
if (amt == null) { response = "Intel AMT not detected."; break; }
if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
2021-01-15 02:57:52 -05:00
amt.getMeiState(15, function (state)
{
var rx = '';
2020-10-20 21:14:00 -04:00
if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
var apfarg = {
mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpskeepalive: 60000,
clientname: state.OsHostname,
clientaddress: '127.0.0.1',
clientuuid: state.UUID,
conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
meiState: state // MEI state will be passed to MPS server
};
2021-01-15 02:57:52 -05:00
if ((state.UUID == null) || (state.UUID.length != 36))
{
2020-10-20 21:14:00 -04:00
rx = "Unable to get Intel AMT UUID";
2021-01-15 02:57:52 -05:00
} else
{
2020-10-26 15:46:30 -04:00
addAmtEvent('User LMS tunnel start.');
apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
2021-01-15 02:57:52 -05:00
apftunnel.onJsonControl = function (data)
{
2020-10-26 15:46:30 -04:00
if (data.action == 'console') { addAmtEvent(data.msg); require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
2021-01-15 02:57:52 -05:00
if (data.action == 'deactivate')
{ // Request CCM deactivation
var amtMeiModule, amtMei;
try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
}
2020-10-20 21:14:00 -04:00
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
}
2020-10-26 15:46:30 -04:00
apftunnel.onChannelClosed = function () { addAmtEvent('User LMS tunnel closed.'); apftunnel = null; }
2021-01-15 02:57:52 -05:00
try
{
2020-10-20 21:14:00 -04:00
apftunnel.connect();
rx = "Started Intel AMT configuration";
2021-01-15 02:57:52 -05:00
} catch (ex)
{
2020-10-20 21:14:00 -04:00
rx = JSON.stringify(ex);
}
}
}
if (rx != '') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: rx }); }
});
break;
}
case 'apf': {
2021-01-15 02:57:52 -05:00
if (meshCoreObj.intelamt !== null)
{
if (args['_'].length == 1)
{
2020-10-12 12:29:34 -04:00
var connType = -1, connTypeStr = args['_'][0].toLowerCase();
if (connTypeStr == 'lms') { connType = 2; }
if (connTypeStr == 'relay') { connType = 1; }
if (connTypeStr == 'cira') { connType = 0; }
if (connTypeStr == 'off') { connType = -2; }
2021-01-15 02:57:52 -05:00
if (connType >= 0)
{ // Connect
var apfarg = {
2019-10-10 14:13:25 -04:00
mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpskeepalive: 60000,
clientname: require('os').hostname(),
clientaddress: '127.0.0.1',
2020-10-09 18:44:09 -04:00
clientuuid: meshCoreObj.intelamt.uuid,
2020-10-12 12:29:34 -04:00
conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
};
2021-01-15 02:57:52 -05:00
if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36))
{
2020-10-09 18:44:09 -04:00
response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
2021-01-15 02:57:52 -05:00
} else
{
apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
2021-01-15 02:57:52 -05:00
apftunnel.onJsonControl = function (data)
{
if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
}
apftunnel.onChannelClosed = function () { apftunnel = null; }
2021-01-15 02:57:52 -05:00
try
{
2020-10-09 18:44:09 -04:00
apftunnel.connect();
2020-10-12 12:29:34 -04:00
response = "Started APF tunnel";
2021-01-15 02:57:52 -05:00
} catch (e)
{
2020-10-12 12:29:34 -04:00
response = JSON.stringify(e);
2020-10-09 18:44:09 -04:00
}
2019-10-10 14:13:25 -04:00
}
2021-01-15 02:57:52 -05:00
} else if (connType == -2)
{ // Disconnect
try
{
apftunnel.disconnect();
2020-10-12 12:29:34 -04:00
response = "Stopped APF tunnel";
2021-01-15 02:57:52 -05:00
} catch (e)
{
2020-10-12 12:29:34 -04:00
response = JSON.stringify(e);
}
2019-10-10 14:13:25 -04:00
apftunnel = null;
2021-01-15 02:57:52 -05:00
} else
{
2020-10-12 12:29:34 -04:00
response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
}
2021-01-15 02:57:52 -05:00
} else
{
2020-10-12 12:29:34 -04:00
response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
}
2021-01-15 02:57:52 -05:00
} else
{
2020-01-07 16:04:36 -05:00
response = "APF tunnel requires Intel AMT";
}
break;
}
2019-10-10 14:13:25 -04:00
case 'plugin': {
2021-01-15 02:57:52 -05:00
if (typeof args['_'][0] == 'string')
{
try
{
2019-10-10 14:13:25 -04:00
// Pass off the action to the plugin
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
// to control the output / actions here.
response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
2021-01-15 02:57:52 -05:00
} catch (e)
{
2020-01-07 16:04:36 -05:00
response = "There was an error in the plugin (" + e + ")";
2019-10-10 14:13:25 -04:00
}
2021-01-15 02:57:52 -05:00
} else
{
2020-01-07 16:04:36 -05:00
response = "Proper usage: plugin [pluginName] [args].";
2019-10-10 14:13:25 -04:00
}
break;
}
2017-08-28 12:27:45 -04:00
default: { // This is an unknown command, return an error message
2020-01-07 16:04:36 -05:00
response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
2017-08-28 12:27:45 -04:00
break;
}
}
2020-01-07 16:04:36 -05:00
} catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
2017-08-28 12:27:45 -04:00
if (response != null) { sendConsoleText(response, sessionid); }
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Send a mesh agent console command
2021-01-15 02:57:52 -05:00
function sendConsoleText(text, sessionid)
{
if (typeof text == 'object') { text = JSON.stringify(text); }
if (debugConsole && ((sessionid == null) || (sessionid == 'pipe'))) { broadcastToRegisteredApps({ cmd: 'console', value: text }); }
if (sessionid != 'pipe') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid }); }
}
2019-06-20 19:56:19 -04:00
2021-01-15 02:57:52 -05:00
// Send a mesh agent message to server, placing a bubble/badge on the agent device
function sendAgentMessage(msg, icon)
{
if (sendAgentMessage.messages == null)
{
sendAgentMessage.messages = {};
sendAgentMessage.nextid = 1;
}
sendAgentMessage.messages[sendAgentMessage.nextid++] = { msg: msg, icon: icon };
require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages });
}
function windows_execve(name, agentfilename, sessionid)
{
var libc;
try
{
libc = require('_GenericMarshal').CreateNativeProxy('msvcrt.dll');
libc.CreateMethod('_wexecve');
}
catch (xx)
{
sendConsoleText('Self Update failed because msvcrt.dll is missing', sessionid);
sendAgentMessage('Self Update failed because msvcrt.dll is missing', 3);
return;
}
var name = 'Mesh Agent';
var agentfilename = 'MeshAgent.exe';
var cmd = require('_GenericMarshal').CreateVariable(process.env['windir'] + '\\system32\\cmd.exe', { wide: true });
var args = require('_GenericMarshal').CreateVariable(3 * require('_GenericMarshal').PointerSize);
var arg1 = require('_GenericMarshal').CreateVariable('cmd.exe', { wide: true });
var arg2 = require('_GenericMarshal').CreateVariable('/C wmic service "' + name + '" call stopservice & copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" & wmic service "' + name + '" call startservice & erase "' + process.cwd() + agentfilename + '.update"', { wide: true });
arg1.pointerBuffer().copy(args.toBuffer());
arg2.pointerBuffer().copy(args.toBuffer(), require('_GenericMarshal').PointerSize);
libc._wexecve(cmd, args, 0);
}
2021-01-15 02:57:52 -05:00
// Start a JavaScript based Agent Self-Update
function agentUpdate_Start(updateurl, updateoptions)
{
// If this value is null
var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
if (this._selfupdate != null)
{
// We were already called, so we will ignore this duplicate request
if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
}
else
{
if (require('MeshAgent').ARCHID == null && updateurl == null)
{
// This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
}
else
{
var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
var name = require('MeshAgent').serviceName;
if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default
try
{
var s = require('service-manager').manager.getService(name);
if (!s.isMe())
{
if (process.platform == 'win32') { s.close(); }
sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
return;
}
if (process.platform == 'win32') { s.close(); }
}
catch (zz)
{
sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
return;
}
sendConsoleText('Downloading update...', sessionid);
var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
options.protocol = 'https:';
if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
options.rejectUnauthorized = false;
options.checkServerIdentity = function checkServerIdentity(certs)
{
// If the tunnel certificate matches the control channel certificate, accept the connection
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
// Check that the certificate is the one expected by the server, fail if not.
if (checkServerIdentity.servertlshash == null)
{
sendConsoleText('Self Update failed, because the url cannot be verified', sessionid);
sendAgentMessage('Self Update failed, because the url cannot be verified', 3);
throw new Error('BadCert');
}
if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase()))
{
sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
throw new Error('BadCert')
}
}
options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
this._selfupdate = require('https').get(options);
this._selfupdate.on('error', function (e)
{
sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid);
sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
});
this._selfupdate.on('response', function (img)
{
this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' });
this._filehash = require('SHA384Stream').create();
this._filehash.on('hash', function (h)
{
if (updateoptions != null && updateoptions.hash != null)
{
if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
{
sendConsoleText('Download complete. HASH verified.', sessionid);
}
else
{
sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid);
sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3);
return;
}
}
else
{
sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
}
sendConsoleText('Updating and restarting agent...', sessionid);
if (process.platform == 'win32')
{
// Use _wexecve() equivalent to perform the update
windows_execve(name, agentfilename, sessionid);
2021-01-15 02:57:52 -05:00
}
else
{
// remove binary
require('fs').unlinkSync(process.execPath);
// copy update
require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath);
// erase update
require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
// add execute permissions
var m = require('fs').statSync(process.execPath).mode;
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
require('fs').chmodSync(process.execPath, m);
sendConsoleText('Restarting service...', sessionid);
try
{
// restart service
var s = require('service-manager').manager.getService(name);
s.restart();
}
catch (zz)
{
sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
sendAgentMessage('Self Update encountered an error trying to restart service', 3);
}
}
});
img.pipe(this._file);
img.pipe(this._filehash);
});
}
}
}
2017-08-28 12:27:45 -04:00
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Called when the server connection state changes
2021-01-15 02:57:52 -05:00
function handleServerConnection(state)
{
2017-08-28 12:27:45 -04:00
meshServerConnectionState = state;
2021-01-15 02:57:52 -05:00
if (meshServerConnectionState == 0)
{
2017-08-28 12:27:45 -04:00
// Server disconnected
if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
lastSelfInfo = null;
2021-01-15 02:57:52 -05:00
} else
{
2017-08-28 12:27:45 -04:00
// Server connected, send mesh core information
var oldNodeId = db.Get('OldNodeId');
if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
2018-09-21 19:34:35 -04:00
2018-09-27 19:17:05 -04:00
// Send SMBios tables if present
2020-01-07 16:04:36 -05:00
if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
2018-09-27 19:17:05 -04:00
2020-11-02 03:44:07 -05:00
// Update the server on with basic info, logged in users and more advanced stuff, like Intel ME and Network Settings
2018-09-21 19:34:35 -04:00
meInfoStr = null;
2020-11-03 05:58:29 -05:00
sendPeriodicServerUpdate(null, true);
2020-12-10 13:00:15 -05:00
if (selfInfoUpdateTimer == null)
{
selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); // 20 minutes
selfInfoUpdateTimer.metadata = 'meshcore (InfoUpdate Timer)';
2021-01-15 02:57:52 -05:00
}
// Send any state messages
2021-01-15 02:57:52 -05:00
if (Object.keys(tunnelUserCount.msg).length > 0)
{
try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
broadcastSessionsToRegisteredApps();
}
2020-10-01 13:30:26 -04:00
// Send update of registered applications to the server
updateRegisteredAppsToServer();
2020-10-01 13:30:26 -04:00
}
// Send server state update to registered applications
broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null;
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
2021-01-15 02:57:52 -05:00
function sendNetworkUpdate(force)
{
sendNetworkUpdateNagleTimer = null;
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
// Update the network interfaces information data
var netInfo = { netif2: require('os').networkInterfaces() };
2021-01-15 02:57:52 -05:00
if (netInfo.netif2)
{
netInfo.action = 'netinfo';
var netInfoStr = JSON.stringify(netInfo);
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
}
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
// Called periodically to check if we need to send updates to the server
2021-01-15 02:57:52 -05:00
function sendPeriodicServerUpdate(flags, force)
{
2018-09-21 19:34:35 -04:00
if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
if (!flags) { flags = 0xFFFFFFFF; }
2020-11-02 03:44:07 -05:00
// If we have a connected MEI, get Intel ME information
2021-01-15 02:57:52 -05:00
if ((flags & 1) && (amt != null) && (amt.state == 2))
{
2020-11-02 03:44:07 -05:00
delete meshCoreObj.intelamt;
2021-01-15 02:57:52 -05:00
amt.getMeiState(9, function (meinfo)
{
2020-11-02 03:44:07 -05:00
meshCoreObj.intelamt = meinfo;
meshCoreObj.intelamt.microlms = amt.lmsstate;
meshCoreObjChanged();
});
}
2018-09-21 19:34:35 -04:00
2020-11-02 03:44:07 -05:00
// Update network information
if (flags & 2) { sendNetworkUpdateNagle(false); }
2019-08-02 14:35:59 -04:00
2020-11-02 03:44:07 -05:00
// Update anti-virus information
2021-01-15 02:57:52 -05:00
if ((flags & 4) && (process.platform == 'win32'))
{
2019-10-11 16:45:23 -04:00
// Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
2020-11-02 03:44:07 -05:00
try { meshCoreObj.av = require('win-info').av(); meshCoreObjChanged(); } catch (e) { av = null; } // Antivirus
//if (process.platform == 'win32') { try { meshCoreObj.pr = require('win-info').pendingReboot(); meshCoreObjChanged(); } catch (e) { meshCoreObj.pr = null; } } // Pending reboot
}
// Send available data right now
2021-01-15 02:57:52 -05:00
if (force)
{
2020-11-02 03:44:07 -05:00
meshCoreObj = sortObjRec(meshCoreObj);
var x = JSON.stringify(meshCoreObj);
2021-01-15 02:57:52 -05:00
if (x != LastPeriodicServerUpdate)
{
2020-11-03 05:58:29 -05:00
LastPeriodicServerUpdate = x;
mesh.SendCommand(meshCoreObj);
}
2019-08-02 14:35:59 -04:00
}
}
2019-10-10 14:13:25 -04:00
2020-11-02 03:44:07 -05:00
// Once we are done collecting all the data, send to server if needed
var LastPeriodicServerUpdate = null;
var PeriodicServerUpdateNagleTimer = null;
function meshCoreObjChanged() { if (PeriodicServerUpdateNagleTimer == null) { PeriodicServerUpdateNagleTimer = setTimeout(meshCoreObjChangedEx, 500); } }
2021-01-15 02:57:52 -05:00
function meshCoreObjChangedEx()
{
2020-11-02 03:44:07 -05:00
PeriodicServerUpdateNagleTimer = null;
meshCoreObj = sortObjRec(meshCoreObj);
var x = JSON.stringify(meshCoreObj);
2021-01-15 02:57:52 -05:00
if (x != LastPeriodicServerUpdate)
{
2020-11-03 05:58:29 -05:00
try { LastPeriodicServerUpdate = x; mesh.SendCommand(meshCoreObj); } catch (ex) { }
}
2020-11-02 03:44:07 -05:00
}
2020-11-02 12:47:02 -05:00
function sortObjRec(o) { if ((typeof o != 'object') || (Array.isArray(o))) return o; for (var i in o) { if (typeof o[i] == 'object') { o[i] = sortObjRec(o[i]); } } return sortObj(o); }
2020-11-02 03:44:07 -05:00
function sortObj(o) { return Object.keys(o).sort().reduce(function (result, key) { result[key] = o[key]; return result; }, {}); }
2018-12-28 00:31:20 -05:00
// Starting function
2021-01-15 02:57:52 -05:00
obj.start = function ()
{
2018-12-28 00:31:20 -05:00
// Setup the mesh agent event handlers
mesh.AddCommandHandler(handleServerCommand);
mesh.AddConnectHandler(handleServerConnection);
2017-08-28 12:27:45 -04:00
}
2019-10-10 14:13:25 -04:00
2021-01-15 02:57:52 -05:00
obj.stop = function ()
{
2017-08-28 12:27:45 -04:00
mesh.AddCommandHandler(null);
mesh.AddConnectHandler(null);
}
2019-10-10 14:13:25 -04:00
2017-08-28 12:27:45 -04:00
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
2019-10-10 14:13:25 -04:00
2021-01-15 02:57:52 -05:00
function onWebSocketUpgrade(response, s, head)
{
2017-08-28 12:27:45 -04:00
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
this.s = s;
s.httprequest = this;
s.end = onWebSocketClosed;
s.data = onWebSocketData;
}
2017-08-28 12:27:45 -04:00
return obj;
}
//
2021-01-09 17:31:09 -05:00
// Startup for Duktape only. This file is not intended to run in NodeJS.
//
2021-01-15 02:57:52 -05:00
try
{
2021-01-09 17:31:09 -05:00
mainMeshCore = createMeshCore();
mainMeshCore.start(null);
2021-01-15 02:57:52 -05:00
} catch (e)
{
2020-01-07 16:04:36 -05:00
require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
}