Updated all agents except macOS and FreeBSD. New agents support compression.

Ylian Saint-Hilaire committed Jul 21, 2020 at 16:20 UTC 193c85972cdae6164c6d297502745c319e3628a7
24 files changed +64 -3501
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshagent_arm
Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ
agents/meshagent_arm-linaro
Binary files a/agents/meshagent_arm-linaro and b/agents/meshagent_arm-linaro differ
agents/meshagent_arm64
Binary files a/agents/meshagent_arm64 and b/agents/meshagent_arm64 differ
agents/meshagent_armhf
Binary files a/agents/meshagent_armhf and b/agents/meshagent_armhf differ
agents/meshagent_mips
Binary files a/agents/meshagent_mips and b/agents/meshagent_mips differ
agents/meshagent_pogo
Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ
agents/meshagent_poky
Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ
agents/meshagent_poky64
Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ
agents/meshagent_x86
Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ
agents/meshagent_x86-64
Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ
agents/meshagent_x86-64_nokvm
Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ
agents/meshagent_x86_nokvm
Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ
agents/meshcore.js
+18 -16
@@ -1189,20 +1189,22 @@ function createMeshCore(agent) {
1189 var tunnel = tunnels[this.httprequest.index];
1190 if (tunnel == null) return; // Stop duplicate calls.
1191
1192 - // Sent tunnel statistics to the server
1193 - mesh.SendCommand({
1194 - action: 'tunnelCloseStats',
1195 - url: tunnel.url,
1196 - userid: tunnel.userid,
1197 - protocol: tunnel.protocol,
1198 - sessionid: tunnel.sessionid,
1199 - sent: this.bytesSent_uncompressed + '',
1200 - sentActual: this.bytesSent_actual + '',
1201 - sentRatio: this.bytesSent_ratio,
1202 - received: this.bytesReceived_uncompressed + '',
1203 - receivedActual: this.bytesReceived_actual + '',
1204 - receivedRatio: this.bytesReceived_ratio
1205 - });
1192 + // Sent tunnel statistics to the server, only send this if compression was used.
1193 + if (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString()) {
1194 + mesh.SendCommand({
1195 + action: 'tunnelCloseStats',
1196 + url: tunnel.url,
1197 + userid: tunnel.userid,
1198 + protocol: tunnel.protocol,
1199 + sessionid: tunnel.sessionid,
1200 + sent: this.bytesSent_uncompressed.toString(),
1201 + sentActual: this.bytesSent_actual.toString(),
1202 + sentRatio: this.bytesSent_ratio,
1203 + received: this.bytesReceived_uncompressed.toString(),
1204 + receivedActual: this.bytesReceived_actual.toString(),
1205 + receivedRatio: this.bytesReceived_ratio
1206 + });
1207 + }
1208
1209 //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1210 delete tunnels[this.httprequest.index];
@@ -1340,7 +1342,7 @@ function createMeshCore(agent) {
1342 var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1343 if (this.httprequest.soptions != null) {
1344 if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1343 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.username); }
1345 + if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1346 }
1347 this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1348 this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
@@ -1547,7 +1549,7 @@ function createMeshCore(agent) {
1549 var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1550 if (this.ws.httprequest.soptions != null) {
1551 if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1550 - if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.username); }
1552 + 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); }
1553 }
1554 try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1555 }
agents/meshcore.txt deleted
-3474
@@ -1,3474 +0,0 @@
1 -/*
2 -Copyright 2018-2020 Intel Corporation
3 -
4 -Licensed under the Apache License, Version 2.0 (the "License");
5 -you may not use this file except in compliance with the License.
6 -You may obtain a copy of the License at
7 -
8 - http://www.apache.org/licenses/LICENSE-2.0
9 -
10 -Unless required by applicable law or agreed to in writing, software
11 -distributed under the License is distributed on an "AS IS" BASIS,
12 -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 -See the License for the specific language governing permissions and
14 -limitations under the License.
15 -*/
16 -
17 -process.on('uncaughtException', function (ex) {
18 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
19 -});
20 -
21 -// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
22 -//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
23 -
24 -// Mesh Rights
25 -var MNG_ERROR = 65;
26 -var MESHRIGHT_EDITMESH = 1;
27 -var MESHRIGHT_MANAGEUSERS = 2;
28 -var MESHRIGHT_MANAGECOMPUTERS = 4;
29 -var MESHRIGHT_REMOTECONTROL = 8;
30 -var MESHRIGHT_AGENTCONSOLE = 16;
31 -var MESHRIGHT_SERVERFILES = 32;
32 -var MESHRIGHT_WAKEDEVICE = 64;
33 -var MESHRIGHT_SETNOTES = 128;
34 -var MESHRIGHT_REMOTEVIEW = 256;
35 -var MESHRIGHT_NOTERMINAL = 512;
36 -var MESHRIGHT_NOFILES = 1024;
37 -var MESHRIGHT_NOAMT = 2048;
38 -var MESHRIGHT_LIMITEDINPUT = 4096;
39 -var MESHRIGHT_LIMITEVENTS = 8192;
40 -var MESHRIGHT_CHATNOTIFY = 16384;
41 -var MESHRIGHT_UNINSTALL = 32768;
42 -var MESHRIGHT_NODESKTOP = 65536;
43 -
44 -function createMeshCore(agent) {
45 - var obj = {};
46 - if (process.platform == 'win32' && require('user-sessions').isRoot()) {
47 - // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
48 - try {
49 - var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
50 - try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (x) { }
51 - if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (x2) { } }
52 - } catch (ex) { }
53 -
54 - // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
55 - try {
56 - var meshCheck = false;
57 - try { meshCheck = require('service-manager').manager.getService('Mesh Agent').isMe(); } catch (mce) { }
58 - if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59 - } catch (ex) { }
60 - }
61 -
62 - if (process.platform == 'darwin' && !process.versions) {
63 - // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
64 - var child = require('child_process').execFile('/bin/sh', ['sh']);
65 - child.stdout.str = '';
66 - child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
67 - 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, \">\"); ");
68 - 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]; } }");
69 - child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
70 - child.waitExit();
71 - if (child.stdout.str.trim() == 'Crashed') {
72 - child = require('child_process').execFile('/bin/sh', ['sh']);
73 - child.stdout.str = '';
74 - child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
75 - child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
76 - child.waitExit();
77 -
78 - if (parseInt(child.stdout.str.trim()) == process.pid) {
79 - // The currently running MeshAgent is us, so we can continue with the update
80 - var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
81 - var tokens = plist.split('<key>KeepAlive</key>');
82 - if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
83 - var tmp = tokens[1].split('</dict>');
84 - tmp.shift();
85 - tokens[1] = '\n <true/>' + tmp.join('</dict>');
86 - tokens = tokens.join('<key>KeepAlive</key>');
87 -
88 - require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
89 -
90 - var fix = '';
91 - fix += ("function macosRepair()\n");
92 - fix += ("{\n");
93 - fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
94 - fix += (" child.stdout.str = '';\n");
95 - fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
96 - fix += (" child.stderr.on('data', function (chunk) { });\n");
97 - fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
98 - fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
99 - fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
100 - fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
101 - fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
102 - fix += (" child.waitExit();\n");
103 - fix += ("}\n");
104 - fix += ("macosRepair();\n");
105 - fix += ("process.exit();\n");
106 - require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
107 -
108 - var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
109 - plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
110 - plist += '<plist version="1.0">\n';
111 - plist += ' <dict>\n';
112 - plist += ' <key>Label</key>\n';
113 - plist += (' <string>meshagentRepair</string>\n');
114 - plist += ' <key>ProgramArguments</key>\n';
115 - plist += ' <array>\n';
116 - plist += (' <string>' + process.execPath + '</string>\n');
117 - plist += ' <string>macosRepair.js</string>\n';
118 - plist += ' </array>\n';
119 - plist += ' <key>WorkingDirectory</key>\n';
120 - plist += (' <string>' + process.cwd() + '</string>\n');
121 - plist += ' <key>RunAtLoad</key>\n';
122 - plist += ' <true/>\n';
123 - plist += ' </dict>\n';
124 - plist += '</plist>';
125 - require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
126 -
127 - child = require('child_process').execFile('/bin/sh', ['sh']);
128 - child.stdout.str = '';
129 - child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
130 - child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
131 - child.waitExit();
132 - }
133 - }
134 - }
135 - }
136 -
137 - // Create Secure IPC for Diagnostic Agent Communications
138 - obj.DAIPC = require('net').createServer();
139 - if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
140 - obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141 - try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
142 - obj.DAIPC.on('connection', function (c) {
143 - c._send = function (j) {
144 - var data = JSON.stringify(j);
145 - var packet = Buffer.alloc(data.length + 4);
146 - packet.writeUInt32LE(data.length + 4, 0);
147 - Buffer.from(data).copy(packet, 4);
148 - this.end(packet);
149 - };
150 - this._daipc = c;
151 - c.parent = this;
152 - c.on('end', function () { console.log("Connection Closed"); this.parent._daipc = null; });
153 - c.on('data', function (chunk) {
154 - if (chunk.length < 4) { this.unshift(chunk); return; }
155 - var len = chunk.readUInt32LE(0);
156 - if (len > 8192) { this.parent._daipc = null; this.end(); return; }
157 - if (chunk.length < len) { this.unshift(chunk); return; }
158 -
159 - var data = chunk.slice(4, len);
160 - try {
161 - data = JSON.parse(data.toString());
162 - }
163 - catch (de) {
164 - this.parent._daipc = null; this.end(); return;
165 - }
166 -
167 - if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
168 -
169 - try {
170 - switch (data.cmd) {
171 - case 'query':
172 - switch (data.value) {
173 - case 'connection':
174 - data.result = require('MeshAgent').ConnectedServer;
175 - this._send(data);
176 - break;
177 - case 'descriptors':
178 - require('ChainViewer').getSnapshot().then(function (f)
179 - {
180 - this.tag.payload.result = f;
181 - this.tag.ipc._send(this.tag.payload);
182 - }).parentPromise.tag = { ipc: this, payload: data };
183 - break;
184 - }
185 - break;
186 - default:
187 - this.parent._daipc = null;
188 - this.end();
189 - return;
190 - }
191 - }
192 - catch (xe) {
193 - this.parent._daipc = null; this.end(); return;
194 - }
195 - });
196 - });
197 - function diagnosticAgent_uninstall() {
198 - require('service-manager').manager.uninstallService('meshagentDiagnostic');
199 - require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
200 - };
201 - function diagnosticAgent_installCheck(install) {
202 - try {
203 - var diag = require('service-manager').manager.getService('meshagentDiagnostic');
204 - return (diag);
205 - }
206 - catch (e) {
207 - }
208 - if (!install) { return (null); }
209 -
210 - var svc = null;
211 - try {
212 - require('service-manager').manager.installService(
213 - {
214 - name: 'meshagentDiagnostic',
215 - displayName: "Mesh Agent Diagnostic Service",
216 - description: "Mesh Agent Diagnostic Service",
217 - servicePath: process.execPath,
218 - parameters: ['-recovery']
219 - //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
220 - });
221 - svc = require('service-manager').manager.getService('meshagentDiagnostic');
222 - }
223 - catch (e) {
224 - return (null);
225 - }
226 - var proxyConfig = require('global-tunnel').proxyConfig;
227 - var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
228 - var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
229 - ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
230 - ddb.Put('disableUpdate', '1');
231 - ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
232 - ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
233 - ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
234 - if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
235 - if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
236 - if (proxyConfig) {
237 - ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
238 - } else {
239 - ddb.Put('ignoreProxyFile', '1');
240 - }
241 -
242 - require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
243 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
244 -
245 - delete ddb;
246 -
247 - // Set a recurrent task, to run the Diagnostic Agent every 2 days
248 - 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' });
249 - //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
250 -
251 - return (svc);
252 - }
253 -
254 - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered())
255 - {
256 - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val)
257 - {
258 - _battLevelChanged.self._currentBatteryLevel = val;
259 - _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
260 - };
261 - require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
262 - require('MeshAgent')._powerChanged = function _powerChanged(val)
263 - {
264 - _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
265 - _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
266 - };
267 - require('MeshAgent')._powerChanged.self = require('MeshAgent');
268 - require('MeshAgent').on('Connected', function (status)
269 - {
270 - if (status == 0)
271 - {
272 - require('power-monitor').removeListener('acdc', this._powerChanged);
273 - require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
274 - }
275 - else
276 - {
277 - require('power-monitor').on('acdc', this._powerChanged);
278 - require('power-monitor').on('batteryLevel', this._battLevelChanged);
279 - }
280 - });
281 - }
282 -
283 -
284 - /*
285 - function borderController() {
286 - this.container = null;
287 - this.Start = function Start(user) {
288 - if (this.container == null) {
289 - if (process.platform == 'win32') {
290 - try {
291 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
292 - } catch (ex) {
293 - this.container = require('ScriptContainer').Create({ processIsolation: 1 });
294 - }
295 - } else {
296 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
297 - }
298 - this.container.parent = this;
299 - this.container.addModule('monitor-info', getJSModule('monitor-info'));
300 - this.container.addModule('monitor-border', getJSModule('monitor-border'));
301 - this.container.addModule('promise', getJSModule('promise'));
302 - this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
303 - this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
304 - }
305 - }
306 - this.Stop = function Stop() {
307 - if (this.container != null) {
308 - this._container = this.container;
309 - this._container.parent = this;
310 - this.container = null;
311 - this._container.exit();
312 - }
313 - }
314 - }
315 - obj.borderManager = new borderController();
316 - */
317 -
318 - // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
319 - var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
320 -
321 -
322 - // Get the operating system description string
323 - try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (ex) { }
324 -
325 - var meshServerConnectionState = 0;
326 - var tunnels = {};
327 - var lastMeInfo = null;
328 - var lastNetworkInfo = null;
329 - var lastPublicLocationInfo = null;
330 - var selfInfoUpdateTimer = null;
331 - var http = require('http');
332 - var net = require('net');
333 - var fs = require('fs');
334 - var rtc = require('ILibWebRTC');
335 - var amt = null;
336 - var processManager = require('process-manager');
337 - var wifiScannerLib = null;
338 - var wifiScanner = null;
339 - var networkMonitor = null;
340 - var amtscanner = null;
341 - var nextTunnelIndex = 1;
342 - var amtPolicy = null;
343 - var apftunnel = null;
344 - var tunnelUserCount = { terminal: {}, files: {} }; // List of userid->count sessions for terminal and files.
345 -
346 - // Add to the server event log
347 - function MeshServerLog(msg, state) {
348 - if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
349 - if (state) {
350 - if (state.userid) { msg.userid = state.userid; }
351 - if (state.username) { msg.username = state.username; }
352 - if (state.sessionid) { msg.sessionid = state.sessionid; }
353 - }
354 - mesh.SendCommand(msg);
355 - }
356 -
357 - // If we are running in Duktape, agent will be null
358 - if (agent == null) {
359 - // Running in native agent, Import libraries
360 - db = require('SimpleDataStore').Shared();
361 - sha = require('SHA256Stream');
362 - mesh = require('MeshAgent');
363 - childProcess = require('child_process');
364 - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
365 - // Check if this computer supports a desktop
366 - try
367 - {
368 - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
369 - {
370 - meshCoreObj.caps |= 1;
371 - }
372 - else if(process.platform == 'linux' || process.platform == 'freebsd')
373 - {
374 - require('monitor-info').on('kvmSupportDetected', function (value)
375 - {
376 - meshCoreObj.caps |= 1;
377 - mesh.SendCommand(meshCoreObj);
378 - });
379 - }
380 - } catch (ex) { }
381 - }
382 - } else {
383 - // Running in nodejs
384 - meshCoreObj.value += '-NodeJS';
385 - meshCoreObj.caps = 8;
386 - mesh = agent.getMeshApi();
387 - }
388 -
389 - mesh.DAIPC = obj.DAIPC;
390 -
391 - /*
392 - var AMTScanner = require("AMTScanner");
393 - var scan = new AMTScanner();
394 -
395 - scan.on("found", function (data) {
396 - if (typeof data === 'string') {
397 - console.log(data);
398 - } else {
399 - console.log(JSON.stringify(data, null, " "));
400 - }
401 - });
402 - scan.scan("10.2.55.140", 1000);
403 - scan.scan("10.2.55.139-10.2.55.145", 1000);
404 - scan.scan("10.2.55.128/25", 2000);
405 - */
406 -
407 - /*
408 - // Try to load up the network monitor
409 - try {
410 - networkMonitor = require('NetworkMonitor');
411 - networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
412 - networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
413 - networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
414 - } catch (e) { networkMonitor = null; }
415 - */
416 -
417 - // Try to load up the Intel AMT scanner
418 - try {
419 - var AMTScannerModule = require('amt-scanner');
420 - amtscanner = new AMTScannerModule();
421 - //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
422 - } catch (ex) { amtscanner = null; }
423 -
424 - // Fetch the SMBios Tables
425 - var SMBiosTables = null;
426 - var SMBiosTablesRaw = null;
427 - try {
428 - var SMBiosModule = null;
429 - try { SMBiosModule = require('smbios'); } catch (ex) { }
430 - if (SMBiosModule != null) {
431 - SMBiosModule.get(function (data) {
432 - if (data != null) {
433 - SMBiosTablesRaw = data;
434 - SMBiosTables = require('smbios').parse(data)
435 - if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
436 -
437 - // If SMBios tables say that Intel AMT is present, try to connect MEI
438 - if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
439 - var amtmodule = require('amt-manage');
440 - amt = new amtmodule(mesh, db, false);
441 - amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
442 - if (amtPolicy != null) { amt.setPolicy(amtPolicy); }
443 - amt.start();
444 - }
445 - }
446 - });
447 - }
448 - } catch (ex) { sendConsoleText("ex1: " + ex); }
449 -
450 - // Try to load up the WIFI scanner
451 - try {
452 - var wifiScannerLib = require('wifi-scanner');
453 - wifiScanner = new wifiScannerLib();
454 - wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
455 - } catch (ex) { wifiScannerLib = null; wifiScanner = null; }
456 -
457 - // Get our location (lat/long) using our public IP address
458 - var getIpLocationDataExInProgress = false;
459 - var getIpLocationDataExCounts = [0, 0];
460 - function getIpLocationDataEx(func) {
461 - if (getIpLocationDataExInProgress == true) { return false; }
462 - try {
463 - getIpLocationDataExInProgress = true;
464 - getIpLocationDataExCounts[0]++;
465 - var options = http.parseUri("http://ipinfo.io/json");
466 - options.method = 'GET';
467 - http.request(options, function (resp) {
468 - if (resp.statusCode == 200) {
469 - var geoData = '';
470 - resp.data = function (geoipdata) { geoData += geoipdata; };
471 - resp.end = function () {
472 - var location = null;
473 - try {
474 - if (typeof geoData == 'string') {
475 - var result = JSON.parse(geoData);
476 - if (result.ip && result.loc) { location = result; }
477 - }
478 - } catch (e) { }
479 - if (func) { getIpLocationDataExCounts[1]++; func(location); }
480 - }
481 - } else { func(null); }
482 - getIpLocationDataExInProgress = false;
483 - }).end();
484 - return true;
485 - }
486 - catch (e) { return false; }
487 - }
488 -
489 - // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
490 - function clearGatewayMac(str) {
491 - if (str == null) return null;
492 - var x = JSON.parse(str);
493 - for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
494 - return JSON.stringify(x);
495 - }
496 -
497 - function getIpLocationData(func) {
498 - // Get the location information for the cache if possible
499 - var publicLocationInfo = db.Get('publicLocationInfo');
500 - if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
501 - if (publicLocationInfo == null) {
502 - // Nothing in the cache, fetch the data
503 - getIpLocationDataEx(function (locationData) {
504 - if (locationData != null) {
505 - publicLocationInfo = {};
506 - publicLocationInfo.netInfoStr = lastNetworkInfo;
507 - publicLocationInfo.locationData = locationData;
508 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
509 - if (func) func(locationData); // Report the new location
510 - } else {
511 - if (func) func(null); // Report no location
512 - }
513 - });
514 - } else {
515 - // Check the cache
516 - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
517 - // Cache match
518 - if (func) func(publicLocationInfo.locationData);
519 - } else {
520 - // Cache mismatch
521 - getIpLocationDataEx(function (locationData) {
522 - if (locationData != null) {
523 - publicLocationInfo = {};
524 - publicLocationInfo.netInfoStr = lastNetworkInfo;
525 - publicLocationInfo.locationData = locationData;
526 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
527 - if (func) func(locationData); // Report the new location
528 - } else {
529 - if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
530 - }
531 - });
532 - }
533 - }
534 - }
535 -
536 - // Polyfill String.endsWith
537 - if (!String.prototype.endsWith) {
538 - String.prototype.endsWith = function (searchString, position) {
539 - var subjectString = this.toString();
540 - if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
541 - position -= searchString.length;
542 - var lastIndex = subjectString.lastIndexOf(searchString, position);
543 - return lastIndex !== -1 && lastIndex === position;
544 - };
545 - }
546 -
547 - // Polyfill path.join
548 - obj.path = {
549 - join: function () {
550 - var x = [];
551 - for (var i in arguments) {
552 - var w = arguments[i];
553 - if (w != null) {
554 - while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
555 - if (i != 0) {
556 - while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
557 - }
558 - x.push(w);
559 - }
560 - }
561 - if (x.length == 0) return '/';
562 - return x.join('/');
563 - }
564 - };
565 -
566 - // Replace a string with a number if the string is an exact number
567 - function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
568 -
569 - // Convert decimal to hex
570 - function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
571 -
572 - // Convert a raw string to a hex string
573 - function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
574 -
575 - // Convert a buffer into a string
576 - function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
577 -
578 - // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
579 - function hex2rstr(d) {
580 - if (typeof d != "string" || d.length == 0) return '';
581 - var r = '', m = ('' + d).match(/../g), t;
582 - while (t = m.shift()) r += String.fromCharCode('0x' + t);
583 - return r
584 - }
585 -
586 - // Convert an object to string with all functions
587 - function objToString(x, p, pad, ret) {
588 - if (ret == undefined) ret = '';
589 - if (p == undefined) p = 0;
590 - if (x == null) { return '[null]'; }
591 - if (p > 8) { return '[...]'; }
592 - if (x == undefined) { return '[undefined]'; }
593 - if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
594 - if (typeof x == 'buffer') { return '[buffer]'; }
595 - if (typeof x != 'object') { return x; }
596 - var r = '{' + (ret ? '\r\n' : ' ');
597 - for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
598 - return r + addPad(p, pad) + '}';
599 - }
600 -
601 - // Return p number of spaces
602 - function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
603 -
604 - // Split a string taking into account the quoats. Used for command line parsing
605 - function splitArgs(str) {
606 - var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
607 - do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
608 - return myArray;
609 - }
610 -
611 - // Parse arguments string array into an object
612 - function parseArgs(argv) {
613 - var results = { '_': [] }, current = null;
614 - for (var i = 1, len = argv.length; i < len; i++) {
615 - var x = argv[i];
616 - if (x.length > 2 && x[0] == '-' && x[1] == '-') {
617 - if (current != null) { results[current] = true; }
618 - current = x.substring(2);
619 - } else {
620 - if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
621 - }
622 - }
623 - if (current != null) { results[current] = true; }
624 - return results;
625 - }
626 -
627 - // Get server target url with a custom path
628 - function getServerTargetUrl(path) {
629 - var x = mesh.ServerUrl;
630 - //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
631 - if (x == null) { return null; }
632 - if (path == null) { path = ''; }
633 - x = http.parseUri(x);
634 - if (x == null) return null;
635 - return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
636 - }
637 -
638 - // Get server url. If the url starts with "*/..." change it, it not use the url as is.
639 - function getServerTargetUrlEx(url) {
640 - if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
641 - return url;
642 - }
643 -
644 - // Send a wake-on-lan packet
645 - function sendWakeOnLan(hexMac) {
646 - var count = 0;
647 - try {
648 - var interfaces = require('os').networkInterfaces();
649 - var magic = 'FFFFFFFFFFFF';
650 - for (var x = 1; x <= 16; ++x) { magic += hexMac; }
651 - var magicbin = Buffer.from(magic, 'hex');
652 -
653 - for (var adapter in interfaces)
654 - {
655 - if (interfaces.hasOwnProperty(adapter))
656 - {
657 - for (var i = 0; i < interfaces[adapter].length; ++i)
658 - {
659 - var addr = interfaces[adapter][i];
660 - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00'))
661 - {
662 - try
663 - {
664 - var socket = require('dgram').createSocket({ type: 'udp4' });
665 - socket.bind({ address: addr.address });
666 - socket.setBroadcast(true);
667 - socket.send(magicbin, 7, '255.255.255.255');
668 - socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
669 - count++;
670 - }
671 - catch(ee)
672 - {
673 - }
674 - }
675 - }
676 - }
677 - }
678 - } catch (e) { }
679 - return count;
680 - }
681 -
682 - // Handle a mesh agent command
683 - function handleServerCommand(data) {
684 - if (typeof data == 'object') {
685 - // If this is a console command, parse it and call the console handler
686 - switch (data.action) {
687 - case 'msg': {
688 - switch (data.type) {
689 - case 'console': { // Process a console command
690 - if (data.value && data.sessionid) {
691 - MeshServerLog("Processing console command: " + data.value, data);
692 - var args = splitArgs(data.value);
693 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
694 - }
695 - break;
696 - }
697 - case 'tunnel': {
698 - if (data.value != null) { // Process a new tunnel connection request
699 - // Create a new tunnel object
700 - var xurl = getServerTargetUrlEx(data.value);
701 - if (xurl != null) {
702 - xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
703 - var woptions = http.parseUri(xurl);
704 - woptions.rejectUnauthorized = 0;
705 - //sendConsoleText(JSON.stringify(woptions));
706 - //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
707 - var tunnel = http.request(woptions);
708 - tunnel.upgrade = onTunnelUpgrade;
709 - tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
710 - tunnel.sessionid = data.sessionid;
711 - tunnel.rights = data.rights;
712 - tunnel.consent = data.consent;
713 - tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
714 - tunnel.username = data.username;
715 - tunnel.userid = data.userid;
716 - tunnel.remoteaddr = data.remoteaddr;
717 - tunnel.state = 0;
718 - tunnel.url = xurl;
719 - tunnel.protocol = 0;
720 - tunnel.soptions = data.soptions;
721 - tunnel.tcpaddr = data.tcpaddr;
722 - tunnel.tcpport = data.tcpport;
723 - tunnel.udpaddr = data.udpaddr;
724 - tunnel.udpport = data.udpport;
725 - tunnel.end();
726 - // Put the tunnel in the tunnels list
727 - var index = nextTunnelIndex++;
728 - tunnel.index = index;
729 - tunnels[index] = tunnel;
730 -
731 - //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
732 - }
733 - }
734 - break;
735 - }
736 - case 'messagebox': {
737 - // Display a message box
738 - if (data.title && data.msg) {
739 - MeshServerLog("Displaying message box, title=" + data.title + ", message=" + data.msg, data);
740 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
741 - try { require('message-box').create(data.title, data.msg, 120); } catch (ex) { }
742 - }
743 - break;
744 - }
745 - case 'ps': {
746 - // Return the list of running processes
747 - if (data.sessionid) {
748 - processManager.getProcesses(function (plist) {
749 - mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
750 - });
751 - }
752 - break;
753 - }
754 - case 'pskill': {
755 - // Kill a process
756 - if (data.value) {
757 - MeshServerLog("Killing process " + data.value, data);
758 - try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
759 - }
760 - break;
761 - }
762 - case 'services': {
763 - // Return the list of installed services
764 - var services = null;
765 - try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
766 - if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
767 - break;
768 - }
769 - case 'serviceStop': {
770 - // Stop a service
771 - try {
772 - var service = require('service-manager').manager.getService(data.serviceName);
773 - if (service != null) { service.stop(); }
774 - } catch (e) { }
775 - break;
776 - }
777 - case 'serviceStart': {
778 - // Start a service
779 - try {
780 - var service = require('service-manager').manager.getService(data.serviceName);
781 - if (service != null) { service.start(); }
782 - } catch (e) { }
783 - break;
784 - }
785 - case 'serviceRestart': {
786 - // Restart a service
787 - try {
788 - var service = require('service-manager').manager.getService(data.serviceName);
789 - if (service != null) { service.restart(); }
790 - } catch (e) { }
791 - break;
792 - }
793 - case 'deskBackground':
794 - {
795 - // Toggle desktop background
796 - try {
797 - if (process.platform == 'win32') {
798 - var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
799 - var sid = undefined;
800 - if (stype == 1) {
801 - if (require('MeshAgent')._tsid != null) {
802 - stype = 5;
803 - sid = require('MeshAgent')._tsid;
804 - }
805 - }
806 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
807 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
808 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
809 - child.stderr.on('data', function () { });
810 - child.waitExit();
811 - var current = child.stdout.str.trim();
812 - if (current != '') { require('MeshAgent')._wallpaper = current; }
813 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
814 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
815 - child.stderr.on('data', function () { });
816 - child.waitExit();
817 - } else {
818 - var id = require('user-sessions').consoleUid();
819 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
820 - if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
821 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
822 - }
823 - } catch (e) {
824 - sendConsoleText(e);
825 - }
826 - break;
827 - }
828 - case 'openUrl': {
829 - // Open a local web browser and return success/fail
830 - MeshServerLog("Opening: " + data.url, data);
831 - sendConsoleText("OpenURL: " + data.url);
832 - if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
833 - break;
834 - }
835 - case 'getclip': {
836 - // Send the load clipboard back to the user
837 - //sendConsoleText('getClip: ' + JSON.stringify(data));
838 - if (require('MeshAgent').isService) {
839 - require('clipboard').dispatchRead().then(function (str) {
840 - if (str) {
841 - MeshServerLog("Getting clipboard content, " + str.length + " byte(s)", data);
842 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str });
843 - }
844 - });
845 - } else {
846 - require("clipboard").read().then(function (str) {
847 - if (str) {
848 - MeshServerLog("Getting clipboard content, " + str.length + " byte(s)", data);
849 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str });
850 - }
851 - });
852 - }
853 - break;
854 - }
855 - case 'setclip': {
856 - // Set the load clipboard to a user value
857 - //sendConsoleText('setClip: ' + JSON.stringify(data));
858 - if (typeof data.data == 'string') {
859 - MeshServerLog("Setting clipboard content, " + data.data.length + " byte(s)", data);
860 - if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
861 - mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
862 - }
863 - break;
864 - }
865 - case 'userSessions': {
866 - // Send back current user sessions list, this is Windows only.
867 - //sendConsoleText('userSessions: ' + JSON.stringify(data));
868 - if (process.platform != 'win32') break;
869 - var p = require('user-sessions').enumerateUsers();
870 - p.sessionid = data.sessionid;
871 - p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
872 - break;
873 - }
874 - default:
875 - // Unknown action, ignore it.
876 - break;
877 - }
878 - break;
879 - }
880 - case 'acmactivate': {
881 - if (amt != null) {
882 - MeshServerLog("Attempting Intel AMT ACM mode activation", data);
883 - amt.setAcmResponse(data);
884 - }
885 - break;
886 - }
887 - case 'wakeonlan': {
888 - // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
889 - sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
890 - for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
891 - break;
892 - }
893 - case 'uninstallagent':
894 - // Uninstall this agent
895 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
896 - if (require('service-manager').manager.getService(agentName).isMe()) {
897 - try { diagnosticAgent_uninstall(); } catch (x) { }
898 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
899 - 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 });
900 - }
901 - break;
902 - case 'poweraction': {
903 - // Server telling us to execute a power action
904 - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
905 - var forced = 0;
906 - if (data.forced == 1) { forced = 1; }
907 - data.actiontype = parseInt(data.actiontype);
908 - MeshServerLog("Performing power action=" + data.actiontype + ", forced=" + forced, data);
909 - sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
910 - var r = mesh.ExecPowerState(data.actiontype, forced);
911 - sendConsoleText("ExecPowerState returned code: " + r);
912 - }
913 - break;
914 - }
915 - case 'iplocation': {
916 - // 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
917 - getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
918 - break;
919 - }
920 - case 'toast': {
921 - // Display a toast message
922 - if (data.title && data.msg) {
923 - MeshServerLog("Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
924 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
925 - try { require('toaster').Toast(data.title, data.msg); } catch (ex) { }
926 - }
927 - break;
928 - }
929 - case 'openUrl': {
930 - // Open a local web browser and return success/fail
931 - //sendConsoleText('OpenURL: ' + data.url);
932 - MeshServerLog("Opening: " + data.url, data);
933 - if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
934 - break;
935 - }
936 - case 'amtPolicy': {
937 - // Store the latest Intel AMT policy
938 - amtPolicy = data.amtPolicy;
939 - if (data.amtPolicy != null) { db.Put('amtPolicy', JSON.stringify(data.amtPolicy)); } else { db.Put('amtPolicy', null); }
940 - if (amt != null) { amt.setPolicy(amtPolicy, true); }
941 - break;
942 - }
943 - case 'getScript': {
944 - // Received a configuration script from the server
945 - sendConsoleText('getScript: ' + JSON.stringify(data));
946 - break;
947 - }
948 - case 'sysinfo': {
949 - // Fetch system information
950 - getSystemInformation(function (results) {
951 - if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
952 - });
953 - break;
954 - }
955 - case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
956 - case 'pong': { break; }
957 - case 'plugin': {
958 - try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
959 - break;
960 - }
961 - case 'coredump':
962 - if (data.value === true) {
963 - // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
964 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
965 - } else if (data.value === false) {
966 - process.coreDumpLocation = null;
967 - }
968 - break;
969 - default:
970 - // Unknown action, ignore it.
971 - break;
972 - }
973 - }
974 - }
975 -
976 - // Called when a file changed in the file system
977 - /*
978 - function onFileWatcher(a, b) {
979 - console.log('onFileWatcher', a, b, this.path);
980 - var response = getDirectoryInfo(this.path);
981 - if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
982 - }
983 - */
984 -
985 - function getSystemInformation(func) {
986 - try {
987 - var results = { hardware: require('identifiers').get() }; // Hardware info
988 - if (results.hardware && results.hardware.windows) {
989 - // Remove extra entries and things that change quickly
990 - var x = results.hardware.windows.osinfo;
991 - try { delete x.FreePhysicalMemory; } catch (ex) { }
992 - try { delete x.FreeSpaceInPagingFiles; } catch (ex) { }
993 - try { delete x.FreeVirtualMemory; } catch (ex) { }
994 - try { delete x.LocalDateTime; } catch (ex) { }
995 - try { delete x.MaxProcessMemorySize; } catch (ex) { }
996 - try { delete x.TotalVirtualMemorySize; } catch (ex) { }
997 - try { delete x.TotalVisibleMemorySize; } catch (ex) { }
998 - try {
999 - if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1000 - if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1001 - if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1002 - } catch (ex) { }
1003 - }
1004 - if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1005 - /*
1006 - if (process.platform == 'win32') {
1007 - var defragResult = function (r) {
1008 - if (typeof r == 'object') { results[this.callname] = r; }
1009 - if (this.callname == 'defrag') {
1010 - var pr = require('win-info').installedApps(); // Installed apps
1011 - pr.callname = 'installedApps';
1012 - pr.sessionid = data.sessionid;
1013 - pr.then(defragResult, defragResult);
1014 - }
1015 - else {
1016 - results.winpatches = require('win-info').qfe(); // Windows patches
1017 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1018 - func(results);
1019 - }
1020 - }
1021 - var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1022 - pr.callname = 'defrag';
1023 - pr.sessionid = data.sessionid;
1024 - pr.then(defragResult, defragResult);
1025 - } else {
1026 - */
1027 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1028 - func(results);
1029 - //}
1030 - } catch (ex) { func(null, ex); }
1031 - }
1032 -
1033 - // Get a formated response for a given directory path
1034 - function getDirectoryInfo(reqpath) {
1035 - var response = { path: reqpath, dir: [] };
1036 - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1037 - // List all the drives in the root, or the root itself
1038 - var results = null;
1039 - try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1040 - if (results != null) {
1041 - for (var i = 0; i < results.length; ++i) {
1042 - var drive = { n: results[i].name, t: 1 };
1043 - if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1044 - response.dir.push(drive);
1045 - }
1046 - }
1047 - } else {
1048 - // List all the files and folders in this path
1049 - if (reqpath == '') { reqpath = '/'; }
1050 - var results = null, xpath = obj.path.join(reqpath, '*');
1051 - //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1052 - try { results = fs.readdirSync(xpath); } catch (e) { }
1053 - if (results != null) {
1054 - for (var i = 0; i < results.length; ++i) {
1055 - if ((results[i] != '.') && (results[i] != '..')) {
1056 - var stat = null, p = obj.path.join(reqpath, results[i]);
1057 - //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1058 - try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1059 - if ((stat != null) && (stat != undefined)) {
1060 - if (stat.isDirectory() == true) {
1061 - response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1062 - } else {
1063 - response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1064 - }
1065 - }
1066 - }
1067 - }
1068 - }
1069 - }
1070 - return response;
1071 - }
1072 -
1073 - // Tunnel callback operations
1074 - function onTunnelUpgrade(response, s, head) {
1075 - this.s = s;
1076 - s.httprequest = this;
1077 - s.end = onTunnelClosed;
1078 - s.tunnel = this;
1079 - s.descriptorMetadata = "MeshAgent_relayTunnel";
1080 -
1081 - if (require('MeshAgent').idleTimeout != null)
1082 - {
1083 - s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1084 - s.on('timeout', function ()
1085 - {
1086 - this.ping();
1087 - this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1088 - });
1089 - }
1090 -
1091 -
1092 - //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1093 -
1094 - if (this.tcpport != null) {
1095 - // This is a TCP relay connection, pause now and try to connect to the target.
1096 - s.pause();
1097 - s.data = onTcpRelayServerTunnelData;
1098 - var connectionOptions = { port: parseInt(this.tcpport) };
1099 - if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1100 - s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1101 - s.tcprelay.peerindex = this.index;
1102 - } if (this.udpport != null) {
1103 - // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1104 - s.data = onUdpRelayServerTunnelData;
1105 - s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1106 - s.udprelay.bind({ port: 0 });
1107 - s.udprelay.peerindex = this.index;
1108 - s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1109 - s.udprelay.udpport = this.udpport;
1110 - s.udprelay.udpaddr = this.udpaddr;
1111 - s.udprelay.first = true;
1112 - } else {
1113 - // This is a normal connect for KVM/Terminal/Files
1114 - s.data = onTunnelData;
1115 - }
1116 - }
1117 -
1118 - // Called when UDP relay data is received // TODO****
1119 - function onUdpRelayTargetTunnelConnect(data) {
1120 - var peerTunnel = tunnels[this.peerindex];
1121 - peerTunnel.s.write(data);
1122 - }
1123 -
1124 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1125 - function onUdpRelayServerTunnelData(data) {
1126 - if (this.udprelay.first === true) {
1127 - delete this.udprelay.first; // Skip the first 'c' that is received.
1128 - } else {
1129 - this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1130 - }
1131 - }
1132 -
1133 - // Called when the TCP relay target is connected
1134 - function onTcpRelayTargetTunnelConnect() {
1135 - var peerTunnel = tunnels[this.peerindex];
1136 - this.pipe(peerTunnel.s); // Pipe Target --> Server
1137 - peerTunnel.s.first = true;
1138 - peerTunnel.s.resume();
1139 - }
1140 -
1141 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1142 - function onTcpRelayServerTunnelData(data) {
1143 - if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
1144 - }
1145 -
1146 - function onTunnelClosed() {
1147 - if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
1148 - //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
1149 - delete tunnels[this.httprequest.index];
1150 -
1151 - /*
1152 - // Close the watcher if required
1153 - if (this.httprequest.watcher != undefined) {
1154 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1155 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1156 - delete this.httprequest.watcher;
1157 - }
1158 - */
1159 -
1160 - // If there is a upload or download active on this connection, close the file
1161 - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
1162 - if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
1163 -
1164 - // Clean up WebRTC
1165 - if (this.webrtc != null) {
1166 - 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; }
1167 - if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1168 - try { this.webrtc.close(); } catch (e) { }
1169 - this.webrtc.removeAllListeners('connected');
1170 - this.webrtc.removeAllListeners('disconnected');
1171 - this.webrtc.removeAllListeners('dataChannel');
1172 - delete this.webrtc;
1173 - }
1174 -
1175 - // Clean up WebSocket
1176 - this.removeAllListeners('data');
1177 - }
1178 - function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1179 - function onTunnelData(data) {
1180 - //console.log("OnTunnelData");
1181 - //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1182 -
1183 - // If this is upload data, save it to file
1184 - if (this.httprequest.uploadFile) {
1185 - if (typeof data == 'object') {
1186 - try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1187 - this.write(new Buffer(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data
1188 - }
1189 - return;
1190 - }
1191 - /*
1192 - // If this is a download, send more of the file
1193 - if (this.httprequest.downloadFile) {
1194 - var buf = new Buffer(4096);
1195 - var len = fs.readSync(this.httprequest.downloadFile, buf, 0, 4096, null);
1196 - this.httprequest.downloadFilePtr += len;
1197 - if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); }
1198 - return;
1199 - }
1200 - */
1201 -
1202 - if (this.httprequest.state == 0) {
1203 - // Check if this is a relay connection
1204 - if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1205 - }
1206 - else
1207 - {
1208 - // Handle tunnel data
1209 - 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)
1210 - // Take a look at the protocol
1211 - if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1212 - this.httprequest.protocol = parseInt(data);
1213 - if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1214 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1215 - {
1216 - //
1217 - // Remote Terminal
1218 - //
1219 -
1220 - // Check user access rights for terminal
1221 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1222 - // Disengage this tunnel, user does not have the rights to do this!!
1223 - this.httprequest.protocol = 999999;
1224 - this.httprequest.s.end();
1225 - sendConsoleText("Error: No Terminal Control Rights.");
1226 - return;
1227 - }
1228 -
1229 - this.descriptorMetadata = "Remote Terminal";
1230 -
1231 - if (process.platform == 'win32')
1232 - {
1233 - if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1234 - {
1235 - this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1236 - this.httprequest.s.end();
1237 - return;
1238 - }
1239 - }
1240 -
1241 - var prom = require('promise');
1242 - this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1243 - this.httprequest.tpromise.that = this;
1244 - this.httprequest.tpromise.httprequest = this.httprequest;
1245 -
1246 - this.end = function ()
1247 - {
1248 - if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1249 - if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1250 -
1251 - // Remove the terminal session to the count to update the server
1252 - if (this.httprequest.userid != null)
1253 - {
1254 - 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]; } }
1255 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
1256 - }
1257 -
1258 - if (process.platform == 'win32')
1259 - {
1260 - // Unpipe the web socket
1261 - this.unpipe(this.httprequest._term);
1262 - if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1263 -
1264 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1265 - if (this.rtcchannel)
1266 - {
1267 - this.rtcchannel.unpipe(this.httprequest._term);
1268 - if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1269 - }
1270 -
1271 - // Clean up
1272 - if (this.httprequest._term) { this.httprequest._term.end(); }
1273 - this.httprequest._term = null;
1274 - }
1275 - };
1276 -
1277 - // Perform User-Consent if needed.
1278 - if (this.httprequest.consent && (this.httprequest.consent & 16))
1279 - {
1280 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1281 - var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1282 - if (this.httprequest.soptions != null) {
1283 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1284 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.username); }
1285 - }
1286 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1287 - this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1288 - this.httprequest.tpromise._consent.then(
1289 - function ()
1290 - {
1291 - // Success
1292 - MeshServerLog("Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1293 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1294 - this.retPromise._consent = null;
1295 - this.retPromise._res();
1296 - },
1297 - function (e)
1298 - {
1299 - // Denied
1300 - MeshServerLog("Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1301 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1302 - this.retPromise._rej(e.toString());
1303 - });
1304 - }
1305 - else
1306 - {
1307 - // User-Consent is not required, so just resolve this promise
1308 - this.httprequest.tpromise._res();
1309 - }
1310 -
1311 -
1312 - this.httprequest.tpromise.then(
1313 - function ()
1314 - {
1315 - this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1316 - this.httprequest.connectionPromise.ws = this.that;
1317 -
1318 - // Start Terminal
1319 - if(process.platform == 'win32')
1320 - {
1321 - try
1322 - {
1323 - var cols = 80, rows = 25;
1324 - if (this.httprequest.xoptions)
1325 - {
1326 - if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1327 - if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1328 - }
1329 -
1330 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1331 - {
1332 - // Admin Terminal
1333 - if (require('win-virtual-terminal').supported)
1334 - {
1335 - // ConPTY PseudoTerminal
1336 - // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1337 -
1338 - // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1339 - 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] } });
1340 - this.httprequest._dispatcher.httprequest = this.httprequest;
1341 - this.httprequest._dispatcher.on('connection', function (c)
1342 - {
1343 - if (this.httprequest.connectionPromise.completed)
1344 - {
1345 - c.end();
1346 - }
1347 - else
1348 - {
1349 - this.httprequest.connectionPromise._res(c);
1350 - }
1351 - });
1352 - }
1353 - else
1354 - {
1355 - // Legacy Terminal
1356 - this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1357 - }
1358 - }
1359 - else
1360 - {
1361 - // Logged in user
1362 - var userPromise = require('user-sessions').enumerateUsers();
1363 - userPromise.that = this;
1364 - userPromise.then(function (u)
1365 - {
1366 - var that = this.that;
1367 - if (u.Active.length > 0)
1368 - {
1369 - var username = u.Active[0].Username;
1370 - if (require('win-virtual-terminal').supported)
1371 - {
1372 - // ConPTY PseudoTerminal
1373 - 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] } });
1374 - }
1375 - else
1376 - {
1377 - // Legacy Terminal
1378 - 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] } });
1379 - }
1380 - that.httprequest._dispatcher.ws = that;
1381 - that.httprequest._dispatcher.on('connection', function (c)
1382 - {
1383 - if (this.ws.httprequest.connectionPromise.completed)
1384 - {
1385 - c.end();
1386 - }
1387 - else
1388 - {
1389 - this.ws.httprequest.connectionPromise._res(c);
1390 - }
1391 - });
1392 - }
1393 - });
1394 - }
1395 - }
1396 - catch (e)
1397 - {
1398 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1399 - }
1400 - }
1401 - else
1402 - {
1403 - try
1404 - {
1405 - var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1406 - var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1407 - var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1408 -
1409 - var env = { HISTCONTROL: 'ignoreboth' };
1410 - if (this.httprequest.xoptions)
1411 - {
1412 - if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1413 - if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1414 - }
1415 - var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1416 - if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1417 - {
1418 - if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1419 - this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1420 - }
1421 - else if (bash)
1422 - {
1423 - var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1424 - // Spaces at the beginning of lines are needed to hide commands from the command history
1425 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1426 - this.httprequest.connectionPromise._res(p);
1427 - }
1428 - else if (sh)
1429 - {
1430 - var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1431 - // Spaces at the beginning of lines are needed to hide commands from the command history
1432 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1433 - this.httprequest.connectionPromise._res(p);
1434 - }
1435 - else
1436 - {
1437 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1438 - }
1439 - }
1440 - catch (e)
1441 - {
1442 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1443 - }
1444 - }
1445 -
1446 - this.httprequest.connectionPromise.then(
1447 - function (term)
1448 - {
1449 - // SUCCESS
1450 - var stdoutstream;
1451 - var stdinstream;
1452 - if (process.platform == 'win32')
1453 - {
1454 - this.ws.httprequest._term = term;
1455 - this.ws.httprequest._term.tunnel = this.ws;
1456 - stdoutstream = stdinstream = term;
1457 - }
1458 - else
1459 - {
1460 - term.descriptorMetadata = 'Remote Terminal';
1461 - this.ws.httprequest.process = term;
1462 - this.ws.httprequest.process.tunnel = this.ws;
1463 - term.stderr.stdout = term.stdout;
1464 - term.stderr.on('data', function (c) { this.stdout.write(c); });
1465 - stdoutstream = term.stdout;
1466 - stdinstream = term.stdin;
1467 - this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1468 - term.prependListener('exit', function () { this.tunnel.end(); });
1469 - }
1470 -
1471 - this.ws.removeAllListeners('data');
1472 - this.ws.on('data', onTunnelControlData);
1473 -
1474 - stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1475 - this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1476 -
1477 - // Add the terminal session to the count to update the server
1478 - if (this.ws.httprequest.userid != null)
1479 - {
1480 - if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1481 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
1482 - }
1483 -
1484 - // Toast Notification, if required
1485 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1486 - {
1487 - // User Notifications is required
1488 - var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1489 - if (this.ws.httprequest.soptions != null) {
1490 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1491 - if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.username); }
1492 - }
1493 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1494 - }
1495 - },
1496 - function (e)
1497 - {
1498 - // FAILED to connect terminal
1499 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1500 - this.ws.end();
1501 - });
1502 - },
1503 - function (e)
1504 - {
1505 - // DO NOT start terminal
1506 - this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1507 - this.that.end();
1508 - });
1509 - }
1510 - else if (this.httprequest.protocol == 2)
1511 - {
1512 - //
1513 - // Remote KVM
1514 - //
1515 -
1516 - // Check user access rights for desktop
1517 - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1518 - // Disengage this tunnel, user does not have the rights to do this!!
1519 - this.httprequest.protocol = 999999;
1520 - this.httprequest.s.end();
1521 - sendConsoleText("Error: No Desktop Control Rights.");
1522 - return;
1523 - }
1524 -
1525 - this.descriptorMetadata = "Remote KVM";
1526 -
1527 - // Look for a TSID
1528 - var tsid = null;
1529 - if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1530 - require('MeshAgent')._tsid = tsid;
1531 -
1532 - // Remote desktop using native pipes
1533 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1534 - this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1535 - this.desktop = this.httprequest.desktop;
1536 -
1537 - // Add ourself to the list of remote desktop sessions
1538 - if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1539 - this.httprequest.desktop.kvm.tunnels.push(this);
1540 -
1541 - // Send a metadata update to all desktop sessions
1542 - var users = {};
1543 - if (this.httprequest.desktop.kvm.tunnels != null) {
1544 - 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 (ex) { } }
1545 - 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 (ex) { } }
1546 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
1547 - }
1548 -
1549 - this.end = function () {
1550 - --this.desktop.kvm.connectionCount;
1551 -
1552 - // Remove ourself from the list of remote desktop session
1553 - var i = this.desktop.kvm.tunnels.indexOf(this);
1554 - if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1555 -
1556 - // Send a metadata update to all desktop sessions
1557 - var users = {};
1558 - if (this.httprequest.desktop.kvm.tunnels != null) {
1559 - 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 (ex) { } }
1560 - 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 (ex) { } }
1561 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
1562 - }
1563 -
1564 - // Unpipe the web socket
1565 - try
1566 - {
1567 - this.unpipe(this.httprequest.desktop.kvm);
1568 - this.httprequest.desktop.kvm.unpipe(this);
1569 - }
1570 - catch(ex) { }
1571 -
1572 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1573 - if (this.rtcchannel)
1574 - {
1575 - try
1576 - {
1577 - this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1578 - this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1579 - }
1580 - catch(ex) { }
1581 - }
1582 -
1583 - // Place wallpaper back if needed
1584 - // TODO
1585 -
1586 - if (this.desktop.kvm.connectionCount == 0) {
1587 - // Display a toast message. This may not be supported on all platforms.
1588 - // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
1589 -
1590 - this.httprequest.desktop.kvm.end();
1591 - if (this.httprequest.desktop.kvm.connectionBar) {
1592 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1593 - this.httprequest.desktop.kvm.connectionBar.close();
1594 - this.httprequest.desktop.kvm.connectionBar = null;
1595 - }
1596 - } else {
1597 - for (var i in this.httprequest.desktop.kvm.users) {
1598 - if (this.httprequest.desktop.kvm.users[i] == this.httprequest.username && this.httprequest.desktop.kvm.connectionBar) {
1599 - this.httprequest.desktop.kvm.users.splice(i, 1);
1600 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1601 - this.httprequest.desktop.kvm.connectionBar.close();
1602 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1603 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1604 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1605 - MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1606 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1607 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1608 - }
1609 - this.httprequest.desktop.kvm.end();
1610 - });
1611 - break;
1612 - }
1613 - }
1614 - }
1615 - };
1616 - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1617 - this.httprequest.desktop.kvm.connectionCount++;
1618 - this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1619 - } else {
1620 - this.httprequest.desktop.kvm.connectionCount = 1;
1621 - this.httprequest.desktop.kvm.users = [this.httprequest.username];
1622 - }
1623 -
1624 - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1625 - // If we have remote control rights, pipe the KVM input
1626 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1627 - } else {
1628 - // We need to only pipe non-mouse & non-keyboard inputs.
1629 - //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1630 - // TODO!!!
1631 - }
1632 -
1633 - // Perform notification if needed. Toast messages may not be supported on all platforms.
1634 - if (this.httprequest.consent && (this.httprequest.consent & 8))
1635 - {
1636 - // User Consent Prompt is required
1637 - // Send a console message back using the console channel, "\n" is supported.
1638 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1639 - var consentMessage = this.httprequest.username + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1640 - if (this.httprequest.soptions != null) {
1641 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1642 - if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.username); }
1643 - }
1644 - var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1645 - pr.ws = this;
1646 - this.pause();
1647 - this._consentpromise = pr;
1648 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1649 - pr.then(
1650 - function ()
1651 - {
1652 - // Success
1653 - this.ws._consentpromise = null;
1654 - MeshServerLog("Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1655 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1656 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1657 - // User Notifications is required
1658 - var notifyMessage = this.ws.httprequest.username + " started a remote desktop session.", notifyTitle = "MeshCentral";
1659 - if (this.ws.httprequest.soptions != null) {
1660 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1661 - if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.username); }
1662 - }
1663 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
1664 - }
1665 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1666 - // Connection Bar is required
1667 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1668 - this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1669 - this.ws.httprequest.desktop.kvm.connectionBar.close();
1670 - }
1671 - try {
1672 - this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1673 - MeshServerLog("Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1674 - }
1675 - catch (xx) {
1676 - if (process.platform != 'darwin') {
1677 - MeshServerLog("Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1678 - }
1679 - }
1680 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1681 - this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1682 - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1683 - MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1684 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1685 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1686 - }
1687 - this.httprequest.desktop.kvm.end();
1688 - });
1689 - }
1690 - }
1691 - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1692 - this.ws.resume();
1693 - },
1694 - function (e)
1695 - {
1696 - // User Consent Denied/Failed
1697 - this.ws._consentpromise = null;
1698 - MeshServerLog("Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1699 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1700 - });
1701 - }
1702 - else {
1703 - // User Consent Prompt is not required
1704 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1705 - // User Notifications is required
1706 - MeshServerLog("Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1707 - var notifyMessage = this.httprequest.username + " started a remote desktop session.", notifyTitle = "MeshCentral";
1708 - if (this.httprequest.soptions != null) {
1709 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1710 - if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.username); }
1711 - }
1712 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
1713 - } else {
1714 - MeshServerLog("Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1715 - }
1716 - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1717 - // Connection Bar is required
1718 - if (this.httprequest.desktop.kvm.connectionBar) {
1719 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1720 - this.httprequest.desktop.kvm.connectionBar.close();
1721 - }
1722 - try {
1723 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1724 - MeshServerLog("Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1725 - }
1726 - catch (xx) {
1727 - MeshServerLog("Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1728 - }
1729 - if (this.httprequest.desktop.kvm.connectionBar) {
1730 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1731 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1732 - MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1733 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1734 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1735 - }
1736 - this.httprequest.desktop.kvm.end();
1737 - });
1738 - }
1739 - }
1740 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1741 - }
1742 -
1743 - this.removeAllListeners('data');
1744 - this.on('data', onTunnelControlData);
1745 - //this.write('MeshCore KVM Hello!1');
1746 -
1747 - } else if (this.httprequest.protocol == 5)
1748 - {
1749 - //
1750 - // Remote Files
1751 - //
1752 -
1753 - // Check user access rights for files
1754 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
1755 - // Disengage this tunnel, user does not have the rights to do this!!
1756 - this.httprequest.protocol = 999999;
1757 - this.httprequest.s.end();
1758 - sendConsoleText("Error: No files control rights.");
1759 - return;
1760 - }
1761 -
1762 - this.descriptorMetadata = "Remote Files";
1763 -
1764 - // Add the files session to the count to update the server
1765 - if (this.httprequest.userid != null) {
1766 - if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
1767 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
1768 - }
1769 -
1770 - this.end = function () {
1771 - // Remove the files session from the count to update the server
1772 - if (this.httprequest.userid != null) {
1773 - 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]; } }
1774 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
1775 - }
1776 - };
1777 -
1778 - // Perform notification if needed. Toast messages may not be supported on all platforms.
1779 - if (this.httprequest.consent && (this.httprequest.consent & 32)) {
1780 - // User Consent Prompt is required
1781 - // Send a console message back using the console channel, "\n" is supported.
1782 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1783 - var consentMessage = this.httprequest.username + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
1784 - if (this.httprequest.soptions != null) {
1785 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1786 - if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.username); }
1787 - }
1788 - var pr = require('message-box').create(consentTitle, consentMessage, 30);
1789 - pr.ws = this;
1790 - this.pause();
1791 - this._consentpromise = pr;
1792 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
1793 - pr.then(
1794 - function ()
1795 - {
1796 - // Success
1797 - this.ws._consentpromise = null;
1798 - MeshServerLog("Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1799 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
1800 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
1801 - // User Notifications is required
1802 - var notifyMessage = this.ws.httprequest.username + " started a remote file session.", notifyTitle = "MeshCentral";
1803 - if (this.ws.httprequest.soptions != null) {
1804 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1805 - if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.username); }
1806 - }
1807 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1808 - }
1809 - this.ws.resume();
1810 - },
1811 - function (e)
1812 - {
1813 - // User Consent Denied/Failed
1814 - this.ws._consentpromise = null;
1815 - MeshServerLog("Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1816 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1817 - });
1818 - } else {
1819 - // User Consent Prompt is not required
1820 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
1821 - // User Notifications is required
1822 - MeshServerLog("Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1823 - var notifyMessage = this.httprequest.username + " started a remote file session.", notifyTitle = "MeshCentral";
1824 - if (this.httprequest.soptions != null) {
1825 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1826 - if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.username); }
1827 - }
1828 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1829 - } else {
1830 - MeshServerLog("Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1831 - }
1832 - this.resume();
1833 - }
1834 -
1835 - // Setup files
1836 - // NOP
1837 - }
1838 - } else if (this.httprequest.protocol == 1) {
1839 - // Send data into terminal stdin
1840 - //this.write(data); // Echo back the keys (Does not seem to be a good idea)
1841 - } else if (this.httprequest.protocol == 2) {
1842 - // Send data into remote desktop
1843 - if (this.httprequest.desktop.state == 0) {
1844 - this.write(new Buffer(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
1845 - this.httprequest.desktop.state = 1;
1846 - } else {
1847 - this.httprequest.desktop.write(data);
1848 - }
1849 - } else if (this.httprequest.protocol == 5) {
1850 - // Process files commands
1851 - var cmd = null;
1852 - try { cmd = JSON.parse(data); } catch (e) { };
1853 - if (cmd == null) { return; }
1854 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
1855 - if (cmd.action == undefined) { return; }
1856 - //sendConsoleText('CMD: ' + JSON.stringify(cmd));
1857 -
1858 - if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
1859 - //console.log(objToString(cmd, 0, ' '));
1860 - switch (cmd.action) {
1861 - case 'ls': {
1862 - /*
1863 - // Close the watcher if required
1864 - var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
1865 - if ((this.httprequest.watcher != undefined) && (samepath == false)) {
1866 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1867 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1868 - delete this.httprequest.watcher;
1869 - }
1870 - */
1871 -
1872 - // Send the folder content to the browser
1873 - var response = getDirectoryInfo(cmd.path);
1874 - if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
1875 - this.write(new Buffer(JSON.stringify(response)));
1876 -
1877 - /*
1878 - // Start the directory watcher
1879 - if ((cmd.path != '') && (samepath == false)) {
1880 - var watcher = fs.watch(cmd.path, onFileWatcher);
1881 - watcher.tunnel = this.httprequest;
1882 - watcher.path = cmd.path;
1883 - this.httprequest.watcher = watcher;
1884 - //console.log('Starting watcher: ' + this.httprequest.watcher.path);
1885 - }
1886 - */
1887 - break;
1888 - }
1889 - case 'mkdir': {
1890 - // Create a new empty folder
1891 - fs.mkdirSync(cmd.path);
1892 - MeshServerLog("Create folder: \"" + cmd.path + "\"", this.httprequest);
1893 - break;
1894 - }
1895 - case 'rm': {
1896 - // Delete, possibly recursive delete
1897 - for (var i in cmd.delfiles) {
1898 - var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
1899 - try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
1900 - if ((delcount == 1) && !cmd.rec) {
1901 - MeshServerLog("Delete: \"" + p + "\"", this.httprequest);
1902 - } else {
1903 - MeshServerLog((cmd.rec ? "Delete recursive: \"" : "Delete: \"") + p + "\", " + delcount + " element(s) removed", this.httprequest);
1904 - }
1905 - }
1906 - break;
1907 - }
1908 - case 'rename': {
1909 - // Rename a file or folder
1910 - var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
1911 - var newfullpath = obj.path.join(cmd.path, cmd.newname);
1912 - MeshServerLog('Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
1913 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
1914 - break;
1915 - }
1916 - case 'download': {
1917 - // Download a file
1918 - var sendNextBlock = 0;
1919 - if (cmd.sub == 'start') { // Setup the download
1920 - MeshServerLog('Download: \"' + cmd.path + '\"', this.httprequest);
1921 - if (this.filedownload != null) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
1922 - this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
1923 - 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; }
1924 - if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
1925 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
1926 - if (cmd.sub == 'startack') { sendNextBlock = 8; } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
1927 - }
1928 - // Send the next download block(s)
1929 - while (sendNextBlock > 0) {
1930 - sendNextBlock--;
1931 - var buf = new Buffer(4096);
1932 - var len = fs.readSync(this.filedownload.f, buf, 4, 4092, null);
1933 - this.filedownload.ptr += len;
1934 - if (len < 4092) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
1935 - this.write(buf.slice(0, len + 4)); // Write as binary
1936 - }
1937 - break;
1938 - }
1939 - /*
1940 - case 'download': {
1941 - // Packet download of a file, agent to browser
1942 - if (cmd.path == undefined) break;
1943 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1944 - //console.log('Download: ' + filepath);
1945 - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
1946 - this.httprequest.downloadFileId = cmd.reqid;
1947 - this.httprequest.downloadFilePtr = 0;
1948 - if (this.httprequest.downloadFile) { this.write(new Buffer(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
1949 - break;
1950 - }
1951 - case 'download2': {
1952 - // Stream download of a file, agent to browser
1953 - if (cmd.path == undefined) break;
1954 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1955 - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
1956 - this.httprequest.downloadFile.pipe(this);
1957 - this.httprequest.downloadFile.end = function () { }
1958 - break;
1959 - }
1960 - */
1961 - case 'upload': {
1962 - // Upload a file, browser to agent
1963 - if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
1964 - if (cmd.path == undefined) break;
1965 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1966 - MeshServerLog('Upload: \"' + filepath + '\"', this.httprequest);
1967 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
1968 - this.httprequest.uploadFileid = cmd.reqid;
1969 - if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
1970 - break;
1971 - }
1972 - case 'copy': {
1973 - // Copy a bunch of files from scpath to dspath
1974 - for (var i in cmd.names) {
1975 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
1976 - MeshServerLog('Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
1977 - if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
1978 - }
1979 - break;
1980 - }
1981 - case 'move': {
1982 - // Move a bunch of files from scpath to dspath
1983 - for (var i in cmd.names) {
1984 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
1985 - MeshServerLog('Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
1986 - if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
1987 - }
1988 - break;
1989 - }
1990 - default:
1991 - // Unknown action, ignore it.
1992 - break;
1993 - }
1994 - } else if (this.httprequest.protocol == 7) { // Plugin data exchange
1995 - var cmd = null;
1996 - try { cmd = JSON.parse(data); } catch (e) { };
1997 - if (cmd == null) { return; }
1998 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
1999 - if (cmd.action == undefined) return;
2000 -
2001 - switch (cmd.action) {
2002 - case 'plugin': {
2003 - try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2004 - break;
2005 - }
2006 - default: {
2007 - // probably shouldn't happen, but just in case this feature is expanded
2008 - }
2009 - }
2010 -
2011 - }
2012 - //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2013 - }
2014 - }
2015 -
2016 - // Delete a directory with a files and directories within it
2017 - function deleteFolderRecursive(path, rec) {
2018 - var count = 0;
2019 - if (fs.existsSync(path)) {
2020 - if (rec == true) {
2021 - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2022 - var curPath = obj.path.join(path, file);
2023 - if (fs.statSync(curPath).isDirectory()) { // recurse
2024 - count += deleteFolderRecursive(curPath, true);
2025 - } else { // delete file
2026 - fs.unlinkSync(curPath);
2027 - count++;
2028 - }
2029 - });
2030 - }
2031 - fs.unlinkSync(path);
2032 - count++;
2033 - }
2034 - return count;
2035 - };
2036 -
2037 - // Called when receiving control data on WebRTC
2038 - function onTunnelWebRTCControlData(data) {
2039 - if (typeof data != 'string') return;
2040 - var obj;
2041 - try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2042 - if (obj.type == 'close') {
2043 - //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2044 - try { this.close(); } catch (e) { }
2045 - try { this.xrtc.close(); } catch (e) { }
2046 - }
2047 - }
2048 -
2049 - // Called when receiving control data on websocket
2050 - function onTunnelControlData(data, ws) {
2051 - var obj;
2052 - if (ws == null) { ws = this; }
2053 - if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2054 - else if (typeof data == 'object') { obj = data; } else { return; }
2055 - //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2056 - //console.log('onTunnelControlData: ' + JSON.stringify(data));
2057 -
2058 - if (obj.action) {
2059 - switch (obj.action) {
2060 - case 'lock': {
2061 - // Lock the current user out of the desktop
2062 - try {
2063 - if (process.platform == 'win32') {
2064 - MeshServerLog("Locking remote user out of desktop", ws.httprequest);
2065 - var child = require('child_process');
2066 - child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2067 - }
2068 - } catch (e) { }
2069 - break;
2070 - }
2071 - default:
2072 - // Unknown action, ignore it.
2073 - break;
2074 - }
2075 - return;
2076 - }
2077 -
2078 - switch (obj.type) {
2079 - case 'options': {
2080 - // These are additional connection options passed in the control channel.
2081 - //sendConsoleText('options: ' + JSON.stringify(obj));
2082 - delete obj.type;
2083 - ws.httprequest.xoptions = obj;
2084 -
2085 - // Set additional user consent options if present
2086 - if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2087 -
2088 - break;
2089 - }
2090 - case 'close': {
2091 - // We received the close on the websocket
2092 - //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2093 - try { ws.close(); } catch (e) { }
2094 - break;
2095 - }
2096 - case 'termsize': {
2097 - // Indicates a change in terminal size
2098 - if (process.platform == 'win32')
2099 - {
2100 - if (ws.httprequest._dispatcher == null) return;
2101 - //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2102 - if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2103 - } else
2104 - {
2105 - if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2106 - //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2107 -
2108 - if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2109 - }
2110 - break;
2111 - }
2112 - case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2113 - if (ws.httprequest.protocol == 1) { // Terminal
2114 - // 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
2115 - if (process.platform == 'win32') {
2116 - ws.httprequest._term.unpipe(ws);
2117 - } else {
2118 - ws.httprequest.process.stdout.unpipe(ws);
2119 - ws.httprequest.process.stderr.unpipe(ws);
2120 - }
2121 - } else if (ws.httprequest.protocol == 2) { // Desktop
2122 - // 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
2123 - ws.httprequest.desktop.kvm.unpipe(ws);
2124 - } else {
2125 - // Switch things around so all WebRTC data goes to onTunnelData().
2126 - ws.rtcchannel.httprequest = ws.httprequest;
2127 - ws.rtcchannel.removeAllListeners('data');
2128 - ws.rtcchannel.on('data', onTunnelData);
2129 - }
2130 - ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2131 - break;
2132 - }
2133 - case 'webrtc1': {
2134 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2135 - // Switch the user input from websocket to webrtc at this point.
2136 - if (process.platform == 'win32') {
2137 - ws.unpipe(ws.httprequest._term);
2138 - ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2139 - } else {
2140 - ws.unpipe(ws.httprequest.process.stdin);
2141 - ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2142 - }
2143 - ws.resume(); // Resume the websocket to keep receiving control data
2144 - } else if (ws.httprequest.protocol == 2) { // Desktop
2145 - // Switch the user input from websocket to webrtc at this point.
2146 - ws.unpipe(ws.httprequest.desktop.kvm);
2147 - try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2148 - ws.resume(); // Resume the websocket to keep receiving control data
2149 - }
2150 - ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2151 - break;
2152 - }
2153 - case 'webrtc2': {
2154 - // Other side received websocket end of data marker, start sending data on WebRTC channel
2155 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2156 - if (process.platform == 'win32') {
2157 - ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2158 - } else {
2159 - ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2160 - ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2161 - }
2162 - } else if (ws.httprequest.protocol == 2) { // Desktop
2163 - ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2164 - }
2165 - break;
2166 - }
2167 - case 'offer': {
2168 - // This is a WebRTC offer.
2169 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2170 - ws.webrtc = rtc.createConnection();
2171 - ws.webrtc.websocket = ws;
2172 - ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2173 - ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2174 - ws.webrtc.on('dataChannel', function (rtcchannel) {
2175 - //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2176 - rtcchannel.maxFragmentSize = 32768;
2177 - rtcchannel.xrtc = this;
2178 - rtcchannel.websocket = this.websocket;
2179 - this.rtcchannel = rtcchannel;
2180 - this.websocket.rtcchannel = rtcchannel;
2181 - this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2182 - this.websocket.rtcchannel.on('end', function () {
2183 - // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2184 - //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2185 - if (this.websocket.desktop && this.websocket.desktop.kvm)
2186 - {
2187 - try
2188 - {
2189 - this.unpipe(this.websocket.desktop.kvm);
2190 - this.websocket.httprequest.desktop.kvm.unpipe(this);
2191 - }
2192 - catch (xx)
2193 - { }
2194 - }
2195 - });
2196 - this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2197 - });
2198 - var sdp = null;
2199 - try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
2200 - if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2201 - break;
2202 - }
2203 - case 'rtt': {
2204 - ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2205 - break;
2206 - }
2207 - }
2208 - }
2209 -
2210 - // Console state
2211 - var consoleWebSockets = {};
2212 - var consoleHttpRequest = null;
2213 -
2214 - // Console HTTP response
2215 - function consoleHttpResponse(response) {
2216 - response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2217 - response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2218 - };
2219 -
2220 - // Open a web browser to a specified URL on current user's desktop
2221 - function openUserDesktopUrl(url) {
2222 - var child = null;
2223 - try {
2224 - switch (process.platform) {
2225 - case 'win32':
2226 - var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2227 - child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2228 - child.stderr.on('data', function () { });
2229 - child.stdout.on('data', function () { });
2230 - 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');
2231 - child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2232 - child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2233 - child.stdin.write('exit\r\n');
2234 - child.waitExit();
2235 - break;
2236 - case 'linux':
2237 - child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2238 - break;
2239 - case 'darwin':
2240 - child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2241 - break;
2242 - default:
2243 - // Unknown platform, ignore this command.
2244 - break;
2245 - }
2246 - } catch (ex) { }
2247 - return child;
2248 - }
2249 -
2250 - // Process a mesh agent console command
2251 - function processConsoleCommand(cmd, args, rights, sessionid) {
2252 - try {
2253 - var response = null;
2254 - switch (cmd) {
2255 - case 'help': { // Displays available commands
2256 - var fin = '', f = '', availcommands = '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,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper';
2257 - if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2258 - if (process.platform != 'freebsd') { availcommands += ',vm';}
2259 - if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2260 - try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (xx) { }
2261 -
2262 - availcommands = availcommands.split(',').sort();
2263 - while (availcommands.length > 0) {
2264 - if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2265 - f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2266 - }
2267 - if (f != '') { fin += f; }
2268 - response = "Available commands: \r\n" + fin + ".";
2269 - break;
2270 - }
2271 - case 'coredump':
2272 - if (args['_'].length != 1) {
2273 - response = "Proper usage: coredump on|off|status"; // Display usage
2274 - } else {
2275 - switch (args['_'][0].toLowerCase())
2276 - {
2277 - case 'on':
2278 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2279 - response = 'coredump is now on';
2280 - break;
2281 - case 'off':
2282 - process.coreDumpLocation = null;
2283 - response = 'coredump is now off';
2284 - break;
2285 - case 'status':
2286 - response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2287 - break;
2288 - default:
2289 - response = "Proper usage: coredump on|off|status"; // Display usage
2290 - break;
2291 - }
2292 - }
2293 - break;
2294 - case 'service':
2295 - if (args['_'].length != 1)
2296 - {
2297 - response = "Proper usage: service status|restart"; // Display usage
2298 - }
2299 - else
2300 - {
2301 - var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2302 - switch(args['_'][0].toLowerCase())
2303 - {
2304 - case 'status':
2305 - response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2306 - break;
2307 - case 'restart':
2308 - if (s.isMe())
2309 - {
2310 - s.restart();
2311 - }
2312 - else
2313 - {
2314 - response = 'Restarting another agent instance is not allowed';
2315 - }
2316 - break;
2317 - default:
2318 - response = "Proper usage: service status|restart"; // Display usage
2319 - break;
2320 - }
2321 - if (process.platform == 'win32') { s.close(); }
2322 - }
2323 - break;
2324 - case 'zip':
2325 - if (args['_'].length == 0)
2326 - {
2327 - response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2328 - }
2329 - else
2330 - {
2331 - var p = args['_'].join(' ').split(',');
2332 - var ofile = p.shift();
2333 - sendConsoleText('Writing ' + ofile + '...');
2334 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2335 - out.fname = ofile;
2336 - out.sessionid = sessionid;
2337 - out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2338 - var zip = require('zip-writer').write({ files: p });
2339 - zip.pipe(out);
2340 - }
2341 - break;
2342 - case 'unzip':
2343 - if (args['_'].length == 0)
2344 - {
2345 - response = "Proper usage: unzip input, destination"; // Display usage
2346 - }
2347 - else
2348 - {
2349 - var p = args['_'].join(' ').split(',');
2350 - if (p.length != 2)
2351 - {
2352 - response = "Proper usage: unzip input, destination"; // Display usage
2353 - break;
2354 - }
2355 - var prom = require('zip-reader').read(p[0]);
2356 - prom._dest = p[1];
2357 - prom.self = this;
2358 - prom.sessionid = sessionid;
2359 - prom.then(function (zipped)
2360 - {
2361 - sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2362 - zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2363 - }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2364 - }
2365 - break;
2366 - case 'setbattery':
2367 - // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2368 - if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2369 - var b = { action: 'battery', state: args['_'][0] };
2370 - if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2371 - require('MeshAgent').SendCommand(b);
2372 - } else {
2373 - require('MeshAgent').SendCommand({ action: 'battery' });
2374 - }
2375 - break;
2376 - case 'fdsnapshot':
2377 - require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2378 - break;
2379 - case 'fdcount':
2380 - require('DescriptorEvents').getDescriptorCount().then(
2381 - function (c)
2382 - {
2383 - sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2384 - }, function (e)
2385 - {
2386 - sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2387 - }).parentPromise.sessionid = sessionid;
2388 - break;
2389 - case 'uac':
2390 - if (process.platform != 'win32')
2391 - {
2392 - response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2393 - break;
2394 - }
2395 - if (args['_'].length != 1)
2396 - {
2397 - response = 'Proper usage: uac [get|interactive|secure]';
2398 - }
2399 - else
2400 - {
2401 - switch(args['_'][0].toUpperCase())
2402 - {
2403 - case 'GET':
2404 - var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2405 - response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2406 - break;
2407 - case 'INTERACTIVE':
2408 - try
2409 - {
2410 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2411 - response = 'UAC mode changed to: Interactive Desktop';
2412 - }
2413 - catch (e)
2414 - {
2415 - response = "Unable to change UAC Mode";
2416 - }
2417 - break;
2418 - case 'SECURE':
2419 - try
2420 - {
2421 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2422 - response = 'UAC mode changed to: Secure Desktop';
2423 - }
2424 - catch(e)
2425 - {
2426 - response = "Unable to change UAC Mode";
2427 - }
2428 - break;
2429 - default:
2430 - response = 'Proper usage: uac [get|interactive|secure]';
2431 - break;
2432 - }
2433 - }
2434 - break;
2435 - case 'vm':
2436 - response = 'Virtual Machine = ' + require('identifiers').isVM();
2437 - break;
2438 - case 'startupoptions':
2439 - response = JSON.stringify(require('MeshAgent').getStartupOptions());
2440 - break;
2441 - case 'kvmmode':
2442 - if (require('MeshAgent').maxKvmTileSize == null)
2443 - {
2444 - response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2445 - }
2446 - else
2447 - {
2448 - if(require('MeshAgent').maxKvmTileSize == 0)
2449 - {
2450 - response = 'KVM Mode: Full JUMBO';
2451 - }
2452 - else
2453 - {
2454 - response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2455 - response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2456 - }
2457 - }
2458 - break;
2459 - case 'alert':
2460 - if (args['_'].length == 0)
2461 - {
2462 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2463 - }
2464 - else
2465 - {
2466 - var p = args['_'].join(' ').split(',');
2467 - if(p.length<2)
2468 - {
2469 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2470 - }
2471 - else
2472 - {
2473 - this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2474 - }
2475 - }
2476 - break;
2477 - case 'agentsize':
2478 - var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2479 - if (process.platform == 'win32') {
2480 - // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2481 - var writtenSize = 0;
2482 - try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (x) { response = x; }
2483 - if (writtenSize != actualSize) {
2484 - response = "Size updated from: " + writtenSize + " to: " + actualSize;
2485 - try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (x2) { response = x2; }
2486 - } else { response = "Agent Size: " + actualSize + " kb"; }
2487 - } else { response = "Agent Size: " + actualSize + " kb"; }
2488 - break;
2489 - case 'versions':
2490 - response = JSON.stringify(process.versions, null, ' ');
2491 - break;
2492 - case 'wpfhwacceleration':
2493 - if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2494 - if (args['_'].length != 1) {
2495 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2496 - }
2497 - else {
2498 - var reg = require('win-registry');
2499 - var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2500 - var key = reg.usernameToUserKey(uname);
2501 -
2502 - switch (args['_'][0].toUpperCase()) {
2503 - default:
2504 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2505 - break;
2506 - case 'ON':
2507 - try {
2508 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2509 - response = "OK";
2510 - } catch (ex) { response = "FAILED"; }
2511 - break;
2512 - case 'OFF':
2513 - try {
2514 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2515 - response = 'OK';
2516 - } catch (ex) { response = 'FAILED'; }
2517 - break;
2518 - case 'STATUS':
2519 - var s;
2520 - try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (ex) { s = 'DEFAULT'; }
2521 - response = "WPF Hardware Acceleration: " + s;
2522 - break;
2523 - case 'DEFAULT':
2524 - try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (ex) { }
2525 - response = 'OK';
2526 - break;
2527 - }
2528 - }
2529 - break;
2530 - case 'tsid':
2531 - if (process.platform == 'win32') {
2532 - if (args['_'].length != 1) {
2533 - response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2534 - } else {
2535 - var i = parseInt(args['_'][0]);
2536 - require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2537 - response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2538 - }
2539 - } else { response = "TSID command only supported on Windows"; }
2540 - break;
2541 - case 'activeusers':
2542 - if (process.platform == 'win32') {
2543 - var p = require('user-sessions').enumerateUsers();
2544 - p.sessionid = sessionid;
2545 - p.then(function (u) {
2546 - var v = [];
2547 - for (var i in u) {
2548 - if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2549 - }
2550 - sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2551 - });
2552 - } else { response = "activeusers command only supported on Windows"; }
2553 - break;
2554 - case 'wallpaper':
2555 - if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2556 - response = "wallpaper command not supported on this platform";
2557 - }
2558 - else {
2559 - if (args['_'].length != 1) {
2560 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2561 - }
2562 - else {
2563 - switch (args['_'][0].toUpperCase()) {
2564 - default:
2565 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2566 - break;
2567 - case 'GET':
2568 - case 'TOGGLE':
2569 - if (process.platform == 'win32') {
2570 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2571 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2572 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2573 - child.stderr.on('data', function () { });
2574 - child.waitExit();
2575 - var current = child.stdout.str.trim();
2576 - if (args['_'][0].toUpperCase() == 'GET') {
2577 - response = current;
2578 - break;
2579 - }
2580 - if (current != '') {
2581 - require('MeshAgent')._wallpaper = current;
2582 - response = 'Wallpaper cleared';
2583 - } else {
2584 - response = 'Wallpaper restored';
2585 - }
2586 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2587 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2588 - child.stderr.on('data', function () { });
2589 - child.waitExit();
2590 - }
2591 - else {
2592 - var id = require('user-sessions').consoleUid();
2593 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2594 - if (args['_'][0].toUpperCase() == 'GET') {
2595 - response = current;
2596 - break;
2597 - }
2598 - if (current != '/dev/null') {
2599 - require('MeshAgent')._wallpaper = current;
2600 - response = 'Wallpaper cleared';
2601 - } else {
2602 - response = 'Wallpaper restored';
2603 - }
2604 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2605 - }
2606 - break;
2607 - }
2608 - }
2609 - }
2610 - break;
2611 - case 'safemode':
2612 - if (process.platform != 'win32') {
2613 - response = 'safemode only supported on Windows Platforms'
2614 - }
2615 - else {
2616 - if (args['_'].length != 1) {
2617 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2618 - }
2619 - else {
2620 - switch (args['_'][0].toUpperCase()) {
2621 - default:
2622 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2623 - break;
2624 - case 'ON':
2625 - require('win-bcd').setKey('safeboot', 'Network');
2626 - require('win-bcd').enableSafeModeService('Mesh Agent');
2627 - break;
2628 - case 'OFF':
2629 - require('win-bcd').deleteKey('safeboot');
2630 - break;
2631 - case 'STATUS':
2632 - var nextboot = require('win-bcd').getKey('safeboot');
2633 - if (nextboot) {
2634 - switch (nextboot) {
2635 - case 'Network':
2636 - case 'network':
2637 - nextboot = 'SAFE_MODE_NETWORK';
2638 - break;
2639 - default:
2640 - nextboot = 'SAFE_MODE';
2641 - break;
2642 - }
2643 - }
2644 - response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
2645 - break;
2646 - }
2647 - }
2648 - }
2649 - break;
2650 - /*
2651 - case 'border':
2652 - {
2653 - if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
2654 - if (meshCoreObj.users.length > 0) {
2655 - obj.borderManager.Start(meshCoreObj.users[0]);
2656 - response = 'Border blinking is on.';
2657 - } else {
2658 - response = 'Cannot turn on border blinking, no logged in users.';
2659 - }
2660 - } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
2661 - obj.borderManager.Stop();
2662 - response = 'Border blinking is off.';
2663 - } else {
2664 - response = 'Proper usage: border "on|off"'; // Display correct command usage
2665 - }
2666 - }
2667 - break;
2668 - */
2669 - case 'av':
2670 - if (process.platform == 'win32') {
2671 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
2672 - response = JSON.stringify(require('win-info').av(), null, 1);
2673 - } else {
2674 - response = 'Not supported on the platform';
2675 - }
2676 - break;
2677 - case 'log':
2678 - if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
2679 - break;
2680 - case 'getclip':
2681 - if (require('MeshAgent').isService) {
2682 - require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
2683 - } else {
2684 - require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
2685 - }
2686 - break;
2687 - case 'setclip': {
2688 - if (args['_'].length != 1) {
2689 - response = 'Proper usage: setclip "sample text"';
2690 - } else {
2691 - if (require('MeshAgent').isService) {
2692 - require('clipboard').dispatchWrite(args['_'][0]);
2693 - response = 'Setting clipboard to: "' + args['_'][0] + '"';
2694 - }
2695 - else {
2696 - require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
2697 - }
2698 - }
2699 - break;
2700 - }
2701 - case 'amtreset': {
2702 - if (amt != null) { amt.reset(); response = 'Done.'; }
2703 - break;
2704 - }
2705 - case 'amtlmsreset': {
2706 - if (amt != null) { amt.lmsreset(); response = 'Done.'; }
2707 - break;
2708 - }
2709 - case 'amtccm': {
2710 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
2711 - if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
2712 - else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
2713 - }
2714 - break;
2715 - }
2716 - case 'amtacm': {
2717 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
2718 - amt.setPolicy({ type: 0 });
2719 - amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
2720 - }
2721 - break;
2722 - }
2723 - case 'amtdeactivate': {
2724 - if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
2725 - break;
2726 - }
2727 - case 'amtpolicy': {
2728 - if (amtPolicy == null) {
2729 - response = 'No Intel(R) AMT policy.';
2730 - } else {
2731 - response = JSON.stringify(amtPolicy);
2732 - }
2733 - break;
2734 - }
2735 - case 'openurl': {
2736 - if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
2737 - else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
2738 - break;
2739 - }
2740 - case 'users': {
2741 - if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
2742 - require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
2743 - break;
2744 - }
2745 - case 'toast': {
2746 - if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
2747 - if (require('MeshAgent')._tsid == null) {
2748 - require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
2749 - }
2750 - else {
2751 - require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
2752 - }
2753 - }
2754 - break;
2755 - }
2756 - case 'setdebug': {
2757 - if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
2758 - else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
2759 - break;
2760 - }
2761 - case 'ps': {
2762 - processManager.getProcesses(function (plist) {
2763 - var x = '';
2764 - for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
2765 - sendConsoleText(x, sessionid);
2766 - });
2767 - break;
2768 - }
2769 - case 'kill': {
2770 - if ((args['_'].length < 1)) {
2771 - response = 'Proper usage: kill [pid]'; // Display correct command usage
2772 - } else {
2773 - process.kill(parseInt(args['_'][0]));
2774 - response = 'Killed process ' + args['_'][0] + '.';
2775 - }
2776 - break;
2777 - }
2778 - case 'smbios': {
2779 - if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
2780 - break;
2781 - }
2782 - case 'rawsmbios': {
2783 - if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
2784 - response = '';
2785 - for (var i in SMBiosTablesRaw) {
2786 - var header = false;
2787 - for (var j in SMBiosTablesRaw[i]) {
2788 - if (SMBiosTablesRaw[i][j].length > 0) {
2789 - if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
2790 - response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
2791 - }
2792 - }
2793 - }
2794 - }
2795 - break;
2796 - }
2797 - case 'dump':
2798 - if (args['_'].length < 1) {
2799 - response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2800 - }
2801 - else {
2802 - switch (args['_'][0].toLowerCase()) {
2803 - case 'on':
2804 - process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
2805 - response = 'enabled';
2806 - break;
2807 - case 'off':
2808 - process.coreDumpLocation = null;
2809 - response = 'disabled';
2810 - break;
2811 - case 'status':
2812 - if (process.coreDumpLocation) {
2813 - response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
2814 - }
2815 - else {
2816 - response = 'Core Dump: [DISABLED]';
2817 - }
2818 - break;
2819 - default:
2820 - response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2821 - break;
2822 - }
2823 - }
2824 - break;
2825 - case 'eval': { // Eval JavaScript
2826 - if (args['_'].length < 1) {
2827 - response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
2828 - } else {
2829 - response = JSON.stringify(mesh.eval(args['_'][0]));
2830 - }
2831 - break;
2832 - }
2833 - case 'uninstallagent': // Uninstall this agent
2834 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
2835 - if (!require('service-manager').manager.getService(agentName).isMe()) {
2836 - response = 'Uininstall failed, this instance is not the service instance';
2837 - } else {
2838 - try { diagnosticAgent_uninstall(); } catch (x) { }
2839 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
2840 - 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 });
2841 - }
2842 - break;
2843 - case 'notify': { // Send a notification message to the mesh
2844 - if (args['_'].length != 1) {
2845 - response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
2846 - } else {
2847 - var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
2848 - if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
2849 - mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
2850 - response = "ok";
2851 - }
2852 - break;
2853 - }
2854 - case 'cpuinfo': { // Return system information
2855 - // CPU & memory utilization
2856 - pr = require('sysinfo').cpuUtilization();
2857 - pr.sessionid = sessionid;
2858 - pr.then(function (data) {
2859 - sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
2860 - }, function (e) {
2861 - sendConsoleText(e);
2862 - });
2863 - break;
2864 - }
2865 - case 'sysinfo': { // Return system information
2866 - getSystemInformation(function (results, err) {
2867 - if (results == null) { sendConsoleText(err, this.sessionid); } else {
2868 - sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
2869 - }
2870 - });
2871 - break;
2872 - }
2873 - case 'info': { // Return information about the agent and agent core module
2874 - 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 + '.';
2875 - if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
2876 - if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
2877 - response += '\r\nModules: ' + addedModules.join(', ') + '.';
2878 - response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
2879 - response += '\r\lastMeInfo: ' + lastMeInfo + '.';
2880 - var oldNodeId = db.Get('OldNodeId');
2881 - if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
2882 - if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
2883 - break;
2884 - }
2885 - case 'osinfo': { // Return the operating system information
2886 - var i = 1;
2887 - if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
2888 - for (var j = 0; j < i; j++) {
2889 - var pr = require('os').name();
2890 - pr.sessionid = sessionid;
2891 - pr.then(function (v) {
2892 - sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
2893 - });
2894 - }
2895 - break;
2896 - }
2897 - case 'sendcaps': { // Send capability flags to the server
2898 - if (args['_'].length == 0) {
2899 - response = 'Proper usage: sendcaps (number)'; // Display correct command usage
2900 - } else {
2901 - meshCoreObj.caps = parseInt(args['_'][0]);
2902 - mesh.SendCommand(meshCoreObj);
2903 - response = JSON.stringify(meshCoreObj);
2904 - }
2905 - break;
2906 - }
2907 - case 'sendosdesc': { // Send OS description
2908 - if (args['_'].length > 0) {
2909 - meshCoreObj.osdesc = args['_'][0];
2910 - mesh.SendCommand(meshCoreObj);
2911 - response = JSON.stringify(meshCoreObj);
2912 - } else {
2913 - response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
2914 - }
2915 - break;
2916 - }
2917 - case 'args': { // Displays parsed command arguments
2918 - response = 'args ' + objToString(args, 0, ' ', true);
2919 - break;
2920 - }
2921 - case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
2922 - var r = [];
2923 - for (var i in args['_']) { r.push(args['_'][i]); }
2924 - console.log(r.join(' '));
2925 - response = 'Message printed on agent console.';
2926 - break;
2927 - }
2928 - case 'type': { // Returns the content of a file
2929 - if (args['_'].length == 0) {
2930 - response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
2931 - } else {
2932 - var max = 4096;
2933 - if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
2934 - if (max > 4096) max = 4096;
2935 - var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
2936 - response = buf.toString();
2937 - var i = response.indexOf('\n');
2938 - if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
2939 - if (r == max) response += '...';
2940 - fs.closeSync(fd);
2941 - }
2942 - break;
2943 - }
2944 - case 'dbkeys': { // Return all data store keys
2945 - response = JSON.stringify(db.Keys);
2946 - break;
2947 - }
2948 - case 'dbget': { // Return the data store value for a given key
2949 - if (db == null) { response = 'Database not accessible.'; break; }
2950 - if (args['_'].length != 1) {
2951 - response = 'Proper usage: dbget (key)'; // Display the value for a given database key
2952 - } else {
2953 - response = db.Get(args['_'][0]);
2954 - }
2955 - break;
2956 - }
2957 - case 'dbset': { // Set a data store key and value pair
2958 - if (db == null) { response = 'Database not accessible.'; break; }
2959 - if (args['_'].length != 2) {
2960 - response = 'Proper usage: dbset (key) (value)'; // Set a database key
2961 - } else {
2962 - var r = db.Put(args['_'][0], args['_'][1]);
2963 - response = 'Key set: ' + r;
2964 - }
2965 - break;
2966 - }
2967 - case 'dbcompact': { // Compact the data store
2968 - if (db == null) { response = 'Database not accessible.'; break; }
2969 - var r = db.Compact();
2970 - response = 'Database compacted: ' + r;
2971 - break;
2972 - }
2973 - case 'httpget': {
2974 - if (consoleHttpRequest != null) {
2975 - response = 'HTTP operation already in progress.';
2976 - } else {
2977 - if (args['_'].length != 1) {
2978 - response = 'Proper usage: httpget (url)';
2979 - } else {
2980 - var options = http.parseUri(args['_'][0]);
2981 - options.method = 'GET';
2982 - if (options == null) {
2983 - response = 'Invalid url.';
2984 - } else {
2985 - try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
2986 - consoleHttpRequest.sessionid = sessionid;
2987 - if (consoleHttpRequest != null) {
2988 - consoleHttpRequest.end();
2989 - response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
2990 - }
2991 - }
2992 - }
2993 - }
2994 - break;
2995 - }
2996 - case 'wslist': { // List all web sockets
2997 - response = '';
2998 - for (var i in consoleWebSockets) {
2999 - var httprequest = consoleWebSockets[i];
3000 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3001 - }
3002 - if (response == '') { response = 'no websocket sessions.'; }
3003 - break;
3004 - }
3005 - case 'wsconnect': { // Setup a web socket
3006 - if (args['_'].length == 0) {
3007 - response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3008 - } else {
3009 - var httprequest = null;
3010 - try {
3011 - var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3012 - options.rejectUnauthorized = 0;
3013 - httprequest = http.request(options);
3014 - } catch (e) { response = 'Invalid HTTP websocket request'; }
3015 - if (httprequest != null) {
3016 - httprequest.upgrade = onWebSocketUpgrade;
3017 - httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3018 -
3019 - var index = 1;
3020 - while (consoleWebSockets[index]) { index++; }
3021 - httprequest.sessionid = sessionid;
3022 - httprequest.index = index;
3023 - httprequest.url = args['_'][0];
3024 - consoleWebSockets[index] = httprequest;
3025 - response = 'New websocket session #' + index;
3026 - }
3027 - }
3028 - break;
3029 - }
3030 - case 'wssend': { // Send data on a web socket
3031 - if (args['_'].length == 0) {
3032 - response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3033 - for (var i in consoleWebSockets) {
3034 - var httprequest = consoleWebSockets[i];
3035 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3036 - }
3037 - } else {
3038 - var i = parseInt(args['_'][0]);
3039 - var httprequest = consoleWebSockets[i];
3040 - if (httprequest != undefined) {
3041 - httprequest.s.write(args['_'][1]);
3042 - response = 'ok';
3043 - } else {
3044 - response = 'Invalid web socket number';
3045 - }
3046 - }
3047 - break;
3048 - }
3049 - case 'wsclose': { // Close a websocket
3050 - if (args['_'].length == 0) {
3051 - response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3052 - } else {
3053 - var i = parseInt(args['_'][0]);
3054 - var httprequest = consoleWebSockets[i];
3055 - if (httprequest != undefined) {
3056 - if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3057 - response = 'ok';
3058 - } else {
3059 - response = 'Invalid web socket number';
3060 - }
3061 - }
3062 - break;
3063 - }
3064 - case 'tunnels': { // Show the list of current tunnels
3065 - response = '';
3066 - for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3067 - if (response == '') { response = 'No websocket sessions.'; }
3068 - break;
3069 - }
3070 - case 'ls': { // Show list of files and folders
3071 - response = '';
3072 - var xpath = '*';
3073 - if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3074 - response = 'List of ' + xpath + '\r\n';
3075 - var results = fs.readdirSync(xpath);
3076 - for (var i = 0; i < results.length; ++i) {
3077 - var stat = null, p = obj.path.join(args['_'][0], results[i]);
3078 - try { stat = fs.statSync(p); } catch (e) { }
3079 - if ((stat == null) || (stat == undefined)) {
3080 - response += (results[i] + "\r\n");
3081 - } else {
3082 - response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3083 - }
3084 - }
3085 - break;
3086 - }
3087 - case 'lsx': { // Show list of files and folders
3088 - response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3089 - break;
3090 - }
3091 - case 'lock': { // Lock the current user out of the desktop
3092 - 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'; }
3093 - else { response = 'Not supported on the platform'; }
3094 - break;
3095 - }
3096 - case 'amt': { // Show Intel AMT status
3097 - if (amt != null) {
3098 - amt.getAmtInfo(function (state) {
3099 - var resp = 'Intel AMT not detected.';
3100 - if (state != null) { resp = objToString(state, 0, ' ', true); }
3101 - sendConsoleText(resp, sessionid);
3102 - });
3103 - } else {
3104 - response = 'Intel AMT not detected.';
3105 - }
3106 - break;
3107 - }
3108 - case 'netinfo': { // Show network interface information
3109 - var interfaces = require('os').networkInterfaces();
3110 - response = objToString(interfaces, 0, ' ', true);
3111 - break;
3112 - }
3113 - case 'wakeonlan': { // Send wake-on-lan
3114 - if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3115 - response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3116 - } else {
3117 - var count = sendWakeOnLan(args['_'][0]);
3118 - response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3119 - }
3120 - break;
3121 - }
3122 - case 'sendall': { // Send a message to all consoles on this mesh
3123 - sendConsoleText(args['_'].join(' '));
3124 - break;
3125 - }
3126 - case 'power': { // Execute a power action on this computer
3127 - if (mesh.ExecPowerState == undefined) {
3128 - response = 'Power command not supported on this agent.';
3129 - } else {
3130 - if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3131 - 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
3132 - } else {
3133 - var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3134 - response = 'Power action executed with return code: ' + r + '.';
3135 - }
3136 - }
3137 - break;
3138 - }
3139 - case 'location': {
3140 - getIpLocationData(function (location) {
3141 - sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3142 - });
3143 - break;
3144 - }
3145 - case 'parseuri': {
3146 - response = JSON.stringify(http.parseUri(args['_'][0]));
3147 - break;
3148 - }
3149 - case 'scanwifi': {
3150 - if (wifiScanner != null) {
3151 - var wifiPresent = wifiScanner.hasWireless;
3152 - if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3153 - } else { response = "Wifi module not present."; }
3154 - break;
3155 - }
3156 - case 'scanamt': {
3157 - if (amtscanner != null) {
3158 - if (args['_'].length != 1) {
3159 - response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n'; // Display correct command usage
3160 - } else {
3161 - response = 'Scanning: ' + args['_'][0] + '...';
3162 - amtscanner.scan(args['_'][0], 2000, function (data) {
3163 - if (data.length > 0) {
3164 - var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3165 - for (var i in data) {
3166 - var x = data[i];
3167 - if (r != '') { r += '\r\n'; }
3168 - r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3169 - if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3170 - if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3171 - r += '.';
3172 - }
3173 - } else {
3174 - r = 'No Intel AMT found.';
3175 - }
3176 - sendConsoleText(r);
3177 - });
3178 - }
3179 - } else { response = "Intel AMT scanner module not present."; }
3180 - break;
3181 - }
3182 - case 'modules': {
3183 - response = JSON.stringify(addedModules);
3184 - break;
3185 - }
3186 - case 'listservices': {
3187 - var services = require('service-manager').manager.enumerateService();
3188 - response = JSON.stringify(services, null, 1);
3189 - break;
3190 - }
3191 - case 'getscript': {
3192 - if (args['_'].length != 1) {
3193 - response = "Proper usage: getscript [scriptNumber].";
3194 - } else {
3195 - mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3196 - }
3197 - break;
3198 - }
3199 - case 'diagnostic':
3200 - {
3201 - if (!mesh.DAIPC.listening) {
3202 - response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3203 - break;
3204 - }
3205 - var diag = diagnosticAgent_installCheck();
3206 - if (diag) {
3207 - if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3208 - diagnosticAgent_uninstall();
3209 - response = 'Diagnostic Agent uninstalled';
3210 - }
3211 - else {
3212 - response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3213 - }
3214 - }
3215 - else {
3216 - if (args['_'].length == 1 && args['_'][0] == 'install') {
3217 - diag = diagnosticAgent_installCheck(true);
3218 - if (diag) {
3219 - response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3220 - }
3221 - else {
3222 - response = 'Diagnostic agent installation failed';
3223 - }
3224 - }
3225 - else {
3226 - response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3227 - }
3228 - }
3229 - if (diag) { diag.close(); diag = null; }
3230 - break;
3231 - }
3232 - case 'apf': {
3233 - if (meshCoreObj.intelamt !== null) {
3234 - if (args['_'].length == 1) {
3235 - if (args['_'][0] == 'on') {
3236 - response = "Starting APF tunnel";
3237 - var apfarg = {
3238 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3239 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3240 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3241 - mpskeepalive: 60000,
3242 - clientname: require('os').hostname(),
3243 - clientaddress: '127.0.0.1',
3244 - clientuuid: meshCoreObj.intelamt.uuid
3245 - };
3246 - var tobj = { debug: false }; //
3247 - apftunnel = require('apfclient')(tobj, apfarg);
3248 - try {
3249 - apftunnel.connect();
3250 - response += "..success";
3251 - } catch (e) {
3252 - response += JSON.stringify(e);
3253 - }
3254 - } else if (args['_'][0] == 'off') {
3255 - response = "Stopping APF tunnel";
3256 - try {
3257 - apftunnel.disconnect();
3258 - response += "..success";
3259 - } catch (e) {
3260 - response += JSON.stringify(e);
3261 - }
3262 - apftunnel = null;
3263 - } else {
3264 - response = "Invalid command.\r\nCmd syntax: apf on|off";
3265 - }
3266 - } else {
3267 - response = "APF tunnel is " + (apftunnel == null ? "off" : "on");
3268 - }
3269 - } else {
3270 - response = "APF tunnel requires Intel AMT";
3271 - }
3272 - break;
3273 - }
3274 - case 'plugin': {
3275 - if (typeof args['_'][0] == 'string') {
3276 - try {
3277 - // Pass off the action to the plugin
3278 - // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3279 - // to control the output / actions here.
3280 - response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3281 - } catch (e) {
3282 - response = "There was an error in the plugin (" + e + ")";
3283 - }
3284 - } else {
3285 - response = "Proper usage: plugin [pluginName] [args].";
3286 - }
3287 - break;
3288 - }
3289 - default: { // This is an unknown command, return an error message
3290 - response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3291 - break;
3292 - }
3293 - }
3294 - } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3295 - if (response != null) { sendConsoleText(response, sessionid); }
3296 - }
3297 -
3298 - // Send a mesh agent console command
3299 - function sendConsoleText(text, sessionid) {
3300 - if (typeof text == 'object') { text = JSON.stringify(text); }
3301 - mesh.SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3302 - }
3303 -
3304 - // Called before the process exits
3305 - //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3306 -
3307 - // Called when the server connection state changes
3308 - function handleServerConnection(state) {
3309 - meshServerConnectionState = state;
3310 - if (meshServerConnectionState == 0) {
3311 - // Server disconnected
3312 - if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3313 - lastSelfInfo = null;
3314 - } else {
3315 - // Server connected, send mesh core information
3316 - var oldNodeId = db.Get('OldNodeId');
3317 - if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3318 -
3319 - // Update the server with basic info, logged in users and more.
3320 - mesh.SendCommand(meshCoreObj);
3321 -
3322 - // Send SMBios tables if present
3323 - if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3324 -
3325 - // Update the server on more advanced stuff, like Intel ME and Network Settings
3326 - meInfoStr = null;
3327 - sendPeriodicServerUpdate();
3328 - if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3329 - }
3330 - }
3331 -
3332 - // Update the server with the latest network interface information
3333 - var sendNetworkUpdateNagleTimer = null;
3334 - function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3335 - function sendNetworkUpdate(force) {
3336 - sendNetworkUpdateNagleTimer = null;
3337 -
3338 - // Update the network interfaces information data
3339 - var netInfo = { netif2: require('os').networkInterfaces() };
3340 - if (netInfo.netif2) {
3341 - netInfo.action = 'netinfo';
3342 - var netInfoStr = JSON.stringify(netInfo);
3343 - if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3344 - }
3345 - }
3346 -
3347 - // Called periodically to check if we need to send updates to the server
3348 - function sendPeriodicServerUpdate(flags) {
3349 - if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3350 - if (!flags) { flags = 0xFFFFFFFF; }
3351 -
3352 - if ((flags & 1) && (amt != null)) {
3353 - // If we have a connected MEI, get Intel ME information
3354 - amt.getAmtInfo(function (meinfo) {
3355 - try {
3356 - if (meinfo == null) return;
3357 - var intelamt = {}, p = false;
3358 - if ((meinfo.Versions != null) && (meinfo.Versions.AMT != null)) { intelamt.ver = meinfo.Versions.AMT; p = true; if (meinfo.Versions.Sku != null) { intelamt.sku = parseInt(meinfo.Versions.Sku); } }
3359 - if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3360 - if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3361 - if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3362 - if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3363 - if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3364 - // Not activated and we have wired ethernet, look for the trusted DNS
3365 - var dns = meinfo.DNS;
3366 - if (dns == null) {
3367 - // Trusted DNS not set, let's look for the OS network DNS suffix
3368 - var interfaces = require('os').networkInterfaces();
3369 - for (var i in interfaces) {
3370 - for (var j in interfaces[i]) {
3371 - if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3372 - }
3373 - }
3374 - }
3375 - if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3376 - } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3377 - if (p == true) {
3378 - var meInfoStr = JSON.stringify(intelamt);
3379 - if (meInfoStr != lastMeInfo) {
3380 - meshCoreObj.intelamt = intelamt;
3381 - mesh.SendCommand(meshCoreObj);
3382 - lastMeInfo = meInfoStr;
3383 - }
3384 - }
3385 - } catch (ex) { }
3386 - });
3387 - }
3388 -
3389 - if (flags & 2) {
3390 - // Update network information
3391 - sendNetworkUpdateNagle(false);
3392 - }
3393 -
3394 - if ((flags & 4) && (process.platform == 'win32')) {
3395 - // Update anti-virus information
3396 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3397 - var av, pr;
3398 - try { av = require('win-info').av(); } catch (ex) { av = null; } // Antivirus
3399 - //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
3400 - if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3401 - }
3402 - }
3403 -
3404 - // Starting function
3405 - obj.start = function () {
3406 - // Setup the mesh agent event handlers
3407 - mesh.AddCommandHandler(handleServerCommand);
3408 - mesh.AddConnectHandler(handleServerConnection);
3409 -
3410 - // Parse input arguments
3411 - //var args = parseArgs(process.argv);
3412 - //console.log(args);
3413 -
3414 - //resetMicroLms();
3415 -
3416 - // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3417 - try {
3418 - var userSession = require('user-sessions');
3419 - userSession.on('changed', function onUserSessionChanged() {
3420 - userSession.enumerateUsers().then(function (users) {
3421 - var u = [], a = users.Active;
3422 - for (var i = 0; i < a.length; i++) {
3423 - var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3424 - if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3425 - }
3426 - meshCoreObj.users = u;
3427 - mesh.SendCommand(meshCoreObj);
3428 - });
3429 - });
3430 - userSession.emit('changed');
3431 - //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3432 - //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3433 - } catch (ex) { }
3434 - }
3435 -
3436 - obj.stop = function () {
3437 - mesh.AddCommandHandler(null);
3438 - mesh.AddConnectHandler(null);
3439 - }
3440 -
3441 - function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3442 - function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3443 - function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3444 -
3445 - function onWebSocketUpgrade(response, s, head) {
3446 - sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3447 - this.s = s;
3448 - s.httprequest = this;
3449 - s.end = onWebSocketClosed;
3450 - s.data = onWebSocketData;
3451 - }
3452 -
3453 - return obj;
3454 -}
3455 -
3456 -//
3457 -// Module startup
3458 -//
3459 -
3460 -try {
3461 - var xexports = null, mainMeshCore = null;
3462 - try { xexports = module.exports; } catch (e) { }
3463 -
3464 - if (xexports != null) {
3465 - // If we are running within NodeJS, export the core
3466 - module.exports.createMeshCore = createMeshCore;
3467 - } else {
3468 - // If we are not running in NodeJS, launch the core
3469 - mainMeshCore = createMeshCore();
3470 - mainMeshCore.start(null);
3471 - }
3472 -} catch (ex) {
3473 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3474 -}
meshagent.js
+7 -3
@@ -179,7 +179,9 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
179 var taskLimiterOptions = { hash: meshcorehash, core: parent.parent.defaultMeshCores[corename], name: corename };
180
181 // If the agent supports compression, sent the core compressed.
182 - if ((obj.agentInfo.capabilities & 0x80) && (parent.parent.defaultMeshCoresDeflate[corename])) { args.core = parent.parent.defaultMeshCoresDeflate[corename]; }
182 + if ((obj.agentInfo.capabilities & 0x80) && (parent.parent.defaultMeshCoresDeflate[corename])) {
183 + args.core = parent.parent.defaultMeshCoresDeflate[corename];
184 + }
185
186 // Update new core with task limiting so not to flood the server. This is a high priority task.
187 obj.agentCoreUpdatePending = true;
@@ -297,12 +299,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
299 // Send compressed data
300 obj.agentUpdate.agentUpdateData = obj.agentExeInfo.zdata;
301 obj.agentUpdate.agentUpdateHash = obj.agentExeInfo.zhash;
300 - //console.log('Sending compressed update agent', obj.agentExeInfo.zhashhex);
302 } else {
303 // Send uncompressed data
304 obj.agentUpdate.agentUpdateData = obj.agentExeInfo.data;
305 obj.agentUpdate.agentUpdateHash = obj.agentExeInfo.hash;
305 - //console.log('Sending uncompressed update agent', obj.agentExeInfo.hashhex);
306 }
307
308 const len = Math.min(parent.parent.agentUpdateBlockSize, obj.agentUpdate.agentUpdateData.length - obj.agentUpdate.ptr);
@@ -1415,6 +1415,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1415 case 'tunnelCloseStats': {
1416 // TODO: This this extra stats from the tunnel, you can merge this into the tunnel event in the database.
1417 //console.log(command);
1418 +
1419 + // Event the session closed compression data.
1420 + var event = { etype: 'node', action: 'sessioncompression', nodeid: obj.dbNodeKey, domain: domain.id, sent: command.sent, sentActual: command.sentActual, msg: 'Agent closed session with ' + command.sentRatio + '% agent to server compression. Sent: ' + command.sent + ', Compressed: ' + command.sentActual + '.' };
1421 + parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(obj.dbMeshKey, [obj.dbNodeKey]), obj, event);
1422 break;
1423 }
1424 case 'plugin': {
meshcentral-config-schema.json
+1
@@ -51,6 +51,7 @@
51 "selfUpdate": { "type": "boolean", "default": false, "description": "When true, this server will attempt to self-update everyday after midnight." },
52 "browserPing": { "type": "integer", "minimum": 1, "description": "When specified, sends data to the browser at x seconds interval and expects a response from the browser." },
53 "browserPong": { "type": "integer", "minimum": 1, "description": "When specified, sends data to the browser at x seconds interval." },
54 + "agentsInRam": { "type": "boolean", "default": false, "description": "Loads all agent binaries in RAM for faster agent updates." },
55 "agentPing": { "type": "integer", "minimum": 1, "description": "When specified, sends data to the agent at x seconds interval and expects a response from the agent." },
56 "agentPong": { "type": "integer", "minimum": 1, "description": "When specified, sends data to the agent at x seconds interval." },
57 "agentIdleTimeout": { "type": "integer", "minimum": 1 },
meshcentral.js
+37 -8
@@ -649,14 +649,12 @@ function CreateMeshCentralServer(config, args) {
649 if (typeof obj.args.tlsoffload == 'string') { obj.args.tlsoffload = obj.args.tlsoffload.split(' ').join('').split(','); }
650
651 // Check if WebSocket compression is supported. It's broken in NodeJS v11.11 to v12.15
652 - if ((obj.args.wscompression == true) || (obj.args.agentwscompression == true)) {
653 - const verSplit = process.version.substring(1).split('.');
654 - var ver = parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100);
655 - if ((ver >= 11.11) && (ver <= 12.15)) {
656 - obj.args.wscompression = obj.args.agentwscompression = false;
657 - obj.config.settings.wscompression = obj.config.settings.agentwscompression = false;
658 - addServerWarning('WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15.');
659 - }
652 + const verSplit = process.version.substring(1).split('.');
653 + var ver = parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100);
654 + if ((ver >= 11.11) && (ver <= 12.15)) {
655 + if ((obj.args.wscompression === true) || (obj.args.agentwscompression === true)) { addServerWarning('WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15.'); }
656 + obj.args.wscompression = obj.args.agentwscompression = false;
657 + obj.config.settings.wscompression = obj.config.settings.agentwscompression = false;
658 }
659
660 // Local console tracing
@@ -2218,6 +2216,37 @@ function CreateMeshCentralServer(config, args) {
2216 } else {
2217 // Load the agent as-is
2218 obj.meshAgentBinaries[archid].data = obj.fs.readFileSync(agentpath);
2219 +
2220 + // Compress the agent using ZIP
2221 + var archive = require('archiver')('zip', { level: 9 }); // Sets the compression method.
2222 +
2223 + const onZipData = function onZipData(buffer) { onZipData.x.zacc.push(buffer); }
2224 + const onZipEnd = function onZipEnd() {
2225 + // Concat all the buffer for create compressed zip agent
2226 + var concatData = Buffer.concat(onZipData.x.zacc);
2227 + delete onZipData.x.zacc;
2228 +
2229 + // Hash the compressed binary
2230 + var hash = obj.crypto.createHash('sha384').update(concatData);
2231 + onZipData.x.zhash = hash.digest('binary');
2232 + onZipData.x.zhashhex = Buffer.from(onZipData.x.zhash, 'binary').toString('hex');
2233 +
2234 + // Set the agent
2235 + onZipData.x.zdata = concatData;
2236 + onZipData.x.zsize = concatData.length;
2237 +
2238 + console.log('Packed', onZipData.x.size, onZipData.x.zsize);
2239 + }
2240 + const onZipError = function onZipError() { delete onZipData.x.zacc; }
2241 + obj.meshAgentBinaries[archid].zacc = [];
2242 + onZipData.x = obj.meshAgentBinaries[archid];
2243 + onZipEnd.x = obj.meshAgentBinaries[archid];
2244 + onZipError.x = obj.meshAgentBinaries[archid];
2245 + archive.on('data', onZipData);
2246 + archive.on('end', onZipEnd);
2247 + archive.on('error', onZipError);
2248 + archive.append(obj.meshAgentBinaries[archid].data, { name: 'meshagent' });
2249 + archive.finalize();
2250 }
2251 }
2252
sample-config-advanced.json
+1
@@ -40,6 +40,7 @@
40 "_compression": true,
41 "_wscompression": false,
42 "_agentwscompression": true,
43 + "_agentsInRam": false,
44 "_webRTC": false,
45 "_nice404": false,
46 "_clickOnce": false,