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